Lambdabot is an excellent tool to learn to use point-free style (among other things) in Haskell and similar programming langauges. This tool when asked to point-free the given expression often makes heavy use of sections of (.). I personally have hard time reading expressions using those thingies. I thought it’s only a lambdabot thing, which perhaps nobody uses in their own code. Soon I was proven wrong. I saw them being used in several places. Since I wasn’t able to develop an intuition for them and they were apparently used by those who were clearly not bots, I tried to get help from brilliant folks on #haskell IRC channel.

Here is the whole conversation: (Note: It’s from October 3rd.)

[18:35] <rahul_> Hi
[18:37] <rahul_> I have noticed this general pattern while composing functions. `f x e = a (b x e)` can be written as `f = (a .) . b`, and `f x e = a x (b e)` can be written as `f = (. b) . a`
[18:37] < dankna > yes, indeed
[18:38] <rahul_> I however fail to see how to intuitively think of these compositions. I have almost had them memorized now, but I didn't came up with these on my own. They got etched on my memory after repeated experiments with lambdabot
[18:38] <rahul_> Can someone please tell me how should I "read" these point free versions?
[18:38] <rahul_> Sorry if my question is vague. Dunno how else to put it. :-(
[18:39] < dankna > I don't know that they necessarily have an intuitive translation into English
[18:39] < dankna > which I think is what you're asking for
[18:39] <rahul_> dankna: Yes.
[18:39] < dainanaki > Basically you read them by putting together the type signatures in your head.
[18:39] < RaptorRarr > rahul_: You mean (a .) and (. a)?
[18:39] < dankna > I guess I'd say f = (a .) . b
[18:39] < dankna > as
[18:40] <rahul_> RaptorRarr: Yes.
[18:40] < Botje > rahul_: if you expand them once, you get f x = a . b x back
[18:40] <rahul_> dainanaki: Tried that. It blows my head. :(
[18:40] < dankna > "A slice-compose, composed with B"
[18:40] < dainanaki > It takes time.
[18:40] < dankna > and f = (. a) . b as
[18:40] < dankna > "Slice-compose A, composed with B"
[18:40] < dankna > but that convention is not very useful
[18:40] < dainanaki > rahul_: my advice is trying them out on paper until you get used to it.
[18:40] < dankna > in particular it doesn't generalize to things with more levels of parentheses
[18:40] < RaptorRarr > rahul_: "Apply b, then (a .). (a .) takes the first argument as if it's a function."
[18:40] < dankna > you really want to get to a point where "math is your first language"
[18:40] < dankna > that is
[18:41] < moriramar > @pl f x = g x (d c x)
[18:41] < lambdabot > f = ap g (d c)
[18:41] < RaptorRarr > @unpl (a .) . b
[18:41] < lambdabot > (\ f i -> a (b f i))
[18:41] < int-e > Heh. I just don't use sections of (.).
[18:41] < dankna > it's a standard test of natural-language proficiency that you can do arithmetic, speaking the intermediate steps out loud, as quickly in the target language as you can in your first language
[18:41] < dankna > but for me and probably many people here, I can't even do that in English!
[18:41] < dankna > because it's faster to treat the math as "its own mental object"
[18:41] < dankna > and not put it in words at all
[18:42] <rahul_> I understood arrows after I read Wikibook's arrows chapter that provides a metaphor of conveyor belt and robots. Something like that would help!
[18:42] < Botje > rahul_: there is no metaphor. only zool^w the source
[18:42] < dankna > sp. zuul
[18:42] <rahul_> dankna, Botje : Sounds good. Maybe I should be patient, and wait for my moment of mathematical enlightenment. :-)
[18:42] < dankna > rahul_: yeah :)
[18:43] < benmachine > I sort of imagine squashing arguments on the end with sufficient force that a dot pops out, then clamping it there with some parentheses
[18:43] < dankna > benmachine: hahhahahaa!    wow
[18:43] < benmachine > and then sorting out the sections
[18:43] < dankna > that's quite a metaphor
[18:43] <rahul_> benmachine: LOL
[18:43] < benmachine > :P
[18:44] < RaptorRarr > @unpl (a .)
[18:44] < lambdabot > (\ b e -> a (b e))
[18:44] < RaptorRarr > @unpl (. a)
[18:44] < lambdabot > (\ b e -> b (a e))

I wasn’t satisfied with the answers, and haven’t been able to develop a math-y mind as they suggested. Yesterday I stumbled upon it again, and so tweeted about it. Thankfully @runarorama saw that tweet and provided the following excellent answer:

(f .) is a function that modifies the return type of another function by f. (. f) is a function that modifies the argument type of another function by f.

That’s a pretty good way to think of and understand expressions that make use of sections of (.). It seems pretty obvious and simple when you’re told it, but it’s really not that obvious, trust me. :-) (You are free to treat that last sentence as me trying to justify my inability to come up with that explanation on my own. :-P)

To be honest, even after receiving that explanation, I don’t feel very comfortable with sections of (.). I tried to come up with simpler combinators before (in October, again), ones that capture common (.) patterns in more meaningful ways, but couldn’t quite manage it. For now, I have just decided not to use sections of (.) in my own code; and to expand them mentally when I encounter them in someone else’s code.

Incidentally I came across Generic.Pointless.Combinators Haskell library this morning. It looks quite interesting, and I look forward to playing with it this weekend.

I will close this short post with a hat tip to concatenative languages which make such composition a no-brainer. For example, both \x e -> a (b x e) and \x e -> a x (b e) become simply b a in Factor.