16

Say in a Haskell do-notation block, I wish to have a variable is_root indicating if I am root:

import System.Posix.User
main = do
    uid <- getRealUserID
    is_root <- return $ uid == 0

That annoying uid variable is only used in that one place. I wish instead I could write this:

main = do
    is_root <- getRealUserID == 0

But of course that won't compile.

How can I get rid of superfluous variables like uid? Here's the best I've come up with:

import System.Posix.User
main = do
    is_root <- getRealUserID >>= return . ((==) 0)

Blech! Is there a nicer way?

ridiculous_fish
  • 17,273
  • 1
  • 54
  • 61
  • 5
    This doesn't get rid of the extra variable, but you should also be using `let is_root = uid == 0` instead of `is_root <- return $ uid == 0`. It's not a monadic computation, so there's no need to wrap it in `return` and use bind. – bheklilr Aug 14 '14 at 18:43
  • I don't see anything ugly in your attempt, just in the right operand of bind it may be shorten to `return . (== 0)`. – David Unric Aug 16 '14 at 00:43

2 Answers2

23

One way is

fmap (== 0) getRealUserID
Sibi
  • 47,472
  • 16
  • 95
  • 163
pdw
  • 8,359
  • 2
  • 29
  • 41
  • 8
    Note that `m >>= return . f` (as in the code in the question) is required to be equal to `fmap f m` by the `Monad` laws: http://hackage.haskell.org/package/base/docs/Control-Monad.html#t:Monad – David Young Aug 14 '14 at 18:23
  • 1
    You can also use the Applicative instance, something like: `is_root <- (==0) <$> getRealUserID`, which I think reads a little better. – Peter Hall Aug 14 '14 at 21:19
  • 2
    @PeterHall `(<$>)` is still a synonym for `fmap` from the `Functor` instance, it's just frequently used together with applicative notation (and re-exported from `Control.Applicative` for that purpose). – Ørjan Johansen Aug 14 '14 at 22:11
  • 3
    (<$>) is also in Data.Functor – nomen Aug 15 '14 at 00:35
  • As the computation runs in `IO` context for which (with proper imports) `Functor`, `Applicative` and of course `Monad` instances do exist, you may apply whatever mapping function as you please. – David Unric Aug 16 '14 at 00:50
6

(I assume your goal is to limit the scope of uid, not to just be pointfree for its own sake)

In a simple case like this, @pdw's answer is probably the way to go. The operators <$> and <*> from Control.Applicative are particularly helpful here.

foo = do
  are_same <- (==) <$> doThis <*> doThat

In slightly more complicated situations, you could use a nested-do:

complicatedEq :: This -> That -> IO Bool

main = do
  are_same <- do
    this <- doThis
    that <- doThatBasedOn this
    complicatedEq this that
  ... rest ...

Anything significantly long probably deserves to be its own function.

Community
  • 1
  • 1
Lambdageek
  • 12,465
  • 1
  • 25
  • 33