I am always interested in learning new languages, a fact that keeps me on my toes and makes me (I believe) a better programmer. My attempts at conquering Haskell come and go - t
(f . g) x = f (g x)
This is true. You concluded from that that
(f . g) x y = f (g x y)
must also be true, but that is not the case. In fact, the following is true:
(f . g) x y = f (g x) y
which is not the same.
Why is this true? Well (f . g) x y
is the same as ((f . g) x) y
and since we know that (f . g) x = f (g x)
we can reduce that to (f (g x)) y
, which is again the same as f (g x) y
.
So (concatMap . ins) 1 [[2,3]]
is equivalent to concatMap (ins 1) [[2,3]]
. There is no magic going on here.
Another way to approach this is via the types:
.
has the type (b -> c) -> (a -> b) -> a -> c
, concatMap
has the type (x -> [y]) -> [x] -> [y]
, ins
has the type t -> [t] -> [[t]]
. So if we use concatMap
as the b -> c
argument and ins
as the a -> b
argument, then a
becomes t
, b
becomes [t] -> [[t]]
and c
becomes [[t]] -> [[t]]
(with x
= [t]
and y
= [t]
).
So the type of concatMap . ins
is t -> [[t]] -> [[t]]
, which means a function taking a whatever and a list of lists (of whatevers) and returning a list of lists (of the same type).
I'd like to add my two cents. The question and answer make it sound like .
is some magical operator that does strange things with re-arranging function calls. That's not the case. .
is just function composition. Here's an implementation in Python:
def dot(f, g):
def result(arg):
return f(g(arg))
return result
It just creates a new function which applies g
to an argument, applies f
to the result, and returns the result of applying f
.
So (concatMap . ins) 1 [[2, 3]]
is saying: create a function, concatMap . ins
, and apply it to the arguments 1
and [[2, 3]]
. When you do concatMap (ins 1 [[2,3]])
you're instead saying, apply the function concatMap
to the result of applying ins
to 1
and [[2, 3]]
- completely different, as you figured out by Haskell's horrendous error message.
UPDATE: To stress this even further. You said that (f . g) x
was another syntax for f (g x)
. This is wrong! .
is just a function, as functions can have non-alpha-numeric names (>><
, ..
, etc., could also be function names).
You're overthinking this problem. You can work it all out using simple equational reasoning. Let's try it from scratch:
permute = foldr (concatMap . ins) [[]]
This can be converted trivially to:
permute lst = foldr (concatMap . ins) [[]] lst
concatMap
can be defined as:
concatMap f lst = concat (map f lst)
The way foldr
works on a list is that (for instance):
-- let lst = [x, y, z]
foldr f init lst
= foldr f init [x, y, z]
= foldr f init (x : y : z : [])
= f x (f y (f z init))
So something like
permute [1, 2, 3]
becomes:
foldr (concatMap . ins) [[]] [1, 2, 3]
= (concatMap . ins) 1
((concatMap . ins) 2
((concatMap . ins) 3 [[]]))
Let's work through the first expression:
(concatMap . ins) 3 [[]]
= (\x -> concatMap (ins x)) 3 [[]] -- definition of (.)
= (concatMap (ins 3)) [[]]
= concatMap (ins 3) [[]] -- parens are unnecessary
= concat (map (ins 3) [[]]) -- definition of concatMap
Now ins 3 [] == [3]
, so
map (ins 3) [[]] == (ins 3 []) : [] -- definition of map
= [3] : []
= [[3]]
So our original expression becomes:
foldr (concatMap . ins) [[]] [1, 2, 3]
= (concatMap . ins) 1
((concatMap . ins) 2
((concatMap . ins) 3 [[]]))
= (concatMap . ins) 1
((concatMap . ins) 2 [[3]]
Let's work through
(concatMap . ins) 2 [[3]]
= (\x -> concatMap (ins x)) 2 [[3]]
= (concatMap (ins 2)) [[3]]
= concatMap (ins 2) [[3]] -- parens are unnecessary
= concat (map (ins 2) [[3]]) -- definition of concatMap
= concat (ins 2 [3] : [])
= concat ([[2, 3], [3, 2]] : [])
= concat [[[2, 3], [3, 2]]]
= [[2, 3], [3, 2]]
So our original expression becomes:
foldr (concatMap . ins) [[]] [1, 2, 3]
= (concatMap . ins) 1 [[2, 3], [3, 2]]
= (\x -> concatMap (ins x)) 1 [[2, 3], [3, 2]]
= concatMap (ins 1) [[2, 3], [3, 2]]
= concat (map (ins 1) [[2, 3], [3, 2]])
= concat [ins 1 [2, 3], ins 1 [3, 2]] -- definition of map
= concat [[[1, 2, 3], [2, 1, 3], [2, 3, 1]],
[[1, 3, 2], [3, 1, 2], [3, 2, 1]]] -- defn of ins
= [[1, 2, 3], [2, 1, 3], [2, 3, 1],
[1, 3, 2], [3, 1, 2], [3, 2, 1]]
Nothing magical here. I think you may have been confused because it's easy to assume that concatMap = concat . map
, but this is not the case. Similarly, it may seem like concatMap f = concat . (map f)
, but this isn't true either. Equational reasoning will show you why.