3

So I'm trying to understand exactly how the Haskell do notation works. I understand that it is used together with monads and that it basically expands (since it's actually syntactic sugar) into anonymous functions connected with either bind (>>=) or then (>>) as shown here https://en.wikibooks.org/wiki/Haskell/Syntactic_sugar#Do_notation.

However my question is why the following command

Prelude> do [1, 2, 3]; "hello"

returns

"hellohellohello"

I know that arrays are actually monads (and that strings are arrays of chars) but I fail to see how this results in the behavior above.

jberryman
  • 16,334
  • 5
  • 42
  • 83
Andreas
  • 662
  • 1
  • 6
  • 13

2 Answers2

18
do [1, 2, 3]; "hello"

desugars to

[1, 2, 3] >> "hello"

which is the same as

[1, 2, 3] >>= (\_ -> "hello")

which is the same as

concatMap (\_ -> "hello") [1, 2, 3]

which is the same as

concat (map (\_ -> "hello") [1, 2, 3])

which is the same as

concat [(\_ -> "hello") 1, (\_ -> "hello") 2, (\_ -> "hello") 3])

which is the same as

concat ["hello","hello","hello"]

which is the same as

"hellohellohello"
Joachim Breitner
  • 25,395
  • 6
  • 78
  • 139
  • Wow, Thanks! Now that I knew what I was looking for it seems this is (more or less) how the bind `>>=` operator is defined for arrays (see https://en.wikipedia.org/wiki/Monad_%28functional_programming%29#Syntactic_sugar:_do-notation ) The important part being `m >>= f = concat (map f m)` – Andreas Jan 07 '16 at 17:20
  • 2
    I think the key point here is that `list >>= f` is defined as `concatMap f list`. The intuition is that a list represents a nondeterministic computation, so `[1,2,3]` is a computation with three possible outputs. When you bind that with a function, the result is that function applied to all possible outputs. – Jeff Burka Jan 07 '16 at 17:22
3

To complement Joachim Breitner's answer, here's a look at this from another angle:

do [1, 2, 3]
   "hello"

is

do a <- [1, 2, 3]
   b <- "hello"
   return b

is

do a <- [1, 2, 3]
   do b <- "hello"
      do return b

is

[b | a <- [1,2,3], b <- "hello"]

is the same as the pseudocode

for a in (a list of Nums) [1, 2, 3]:
   -- here we have `a` (a Num)
   for b in (a list of Chars) "hello":
      -- here we have that same `a` (a Num), 
      -- and `b` (which is a Char)
      emit b       -- whatever "emit" means

Of course, list comprehensions for lists (not "arrays") of stuff (whatever that stuff is, say Nums, Chars, etc.) desugar into the same concatMap&dash;using code; but it is sometimes easier to handle them mentally, either as is, or as a specification for some nested loops.

In fact it seems that do-notation could as easily have been a for-notation in the first place.

Will Ness
  • 70,110
  • 9
  • 98
  • 181