17

When playing around with Pointfree I was presented with a piece of code that I can't seem to understand.

:pl map (\x -> x * x) [1..10]
-- map (join (*)) [1..10]

My main problem is that I don't get how join works here. I understand that it 'removes' one layer of a monadic wrapping (m (m a) to m a). I figure it boils down to something like [1..10] >>= (\x -> [x * x]), but I don't really get how the "extra layer" gets introduced. I get that join x = x >>= id, but then I'm still stuck on how that "duplicates" each value so that (*) gets two arguments. This has been bugging me for about half an hour now and I'm mostly annoyed at myself, because I feel like I have all the puzzle pieces but can't seem to fit them together...

P.S. Don't worry, I would't really use this pointfree version, this is pure curiosity and an attempt to understand Haskell better.

Michael Kohl
  • 66,324
  • 14
  • 138
  • 158
  • `join :: m (m a) -> m a` doesn't exactly "remove" a layer of monadic wrapping, it's much more accurate to say it squishes two layers into one. Hence the name, indeed... – Ben Millwood Oct 26 '13 at 12:34

1 Answers1

21

join is using the instance of Monad for (->) a, as defined in Control.Monad.Instances. The instance is similar to Reader, but without an explicit wrapper. It is defined like this:

instance Monad ((->) a) where
  -- return :: b -> (a -> b)
  return = const
  -- (>>=) :: (a -> b) -> (b -> a -> c) -> (a -> c)
  f >>= g = \x -> g (f x) x

If you now reduce join using this instance:

join
(>>= id)
flip (\f g x -> g (f x) x) (\a -> a)
(\f x -> (\a -> a) (f x) x)
(\f x -> f x x)

As you can see, the instance for (->) a makes join to a function that applies an argument twice. Because of this, join (*) is simply \x -> x * x.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
fuz
  • 88,405
  • 25
  • 200
  • 352
  • Sh*t. Thank you. I am apparently too tired. – fuz Jul 23 '11 at 00:04
  • 5
    You can also work this out from parametricity. In the monad `(->) a`, `join` has the type `((->) a) ((->) a b) -> ((->) a b)`, which simplifies to `(a -> a -> b) -> a -> b`. It's clear that the only thing `join` can do to get a `b` is feed the `a` to the function twice. (Apart from using bottom values, of course). – hammar Jul 23 '11 at 00:07
  • 2
    @hammar Wasn't there a program that could figure out a function's only sensible definition by looking at the type signature? (If possible) – fuz Jul 23 '11 at 00:11
  • 1
    @FUZxxl: Yes, [Djinn](http://hackage.haskell.org/package/djinn). The package seems to be slightly out of date, though. – hammar Jul 23 '11 at 00:17
  • @FUZxxl: It's called Djinn; while I found [the announcement on Lambda the Ultimate](http://lambda-the-ultimate.org/node/1178), the darcs repository seems to be down. – Antal Spector-Zabusky Jul 23 '11 at 00:21
  • Djinn is also built into lambdabot on the #haskell IRC channel... though lambdabot seems to be mysteriously MIA right at this moment. – C. A. McCann Jul 23 '11 at 00:31
  • 8
    I've uploaded a new version of Djinn. – augustss Jul 23 '11 at 08:00
  • 1
    Thanks, exactly what I was missing! :-) I actually played around with the monad instance of `((->) a)` a bit but didn't get anywhere, probably because it was approaching 2am here and I was pretty tired. Thanks again! – Michael Kohl Jul 23 '11 at 08:46
  • 1
    There you go, I had only accepted so far, not upvoted (by mistake). – Michael Kohl Jul 23 '11 at 13:52