Reputation: 23
I can't seem to get my head around why the below code will not compile:
decode :: [Bit] -> String
decode xs = map (chr . bin2int) . (chop8 xs)
But the below will:
decode :: [Bit] -> String
decode = map (chr . bin2int) . chop8
When we give the 2nd function a list, it applies chop8 to it, then maps (chr . bin2int) to the elements in the list produced.
So why doesn't the 1st example do the same? Are we not effectively doing the same thing, but just giving chop8 the argument in the function definition?
Thanks
Upvotes: 2
Views: 188
Reputation: 1310
The definition of (.)
is key here.
To give a simpler example:
decode1, decode2 :: Int -> Char
decode1 x = chr . (abs x)
decode2 = chr . abs
Now, the definition of (.)
is f . g = \a -> f (g a)
, so we can use that in both of our definitions:
decode1 x = \a -> chr (abs x a)
decode2 = \x -> chr (abs x)
We can further simplify this, by moving the lambda parameters into the pattern match:
decode1 x a = chr (abs x a)
decode2 x = chr (abs x)
Obviously decode2
is the right funtion; decode1
doesn't even typecheck. abs
only takes one argument, but decode1
calls it with 2! Furthermore, decode1
has an extra parameter, a
, which we don't want.
Upvotes: 8
Reputation: 1257
The second version, using what is called point-free style, does not require the "xs" parameter because the expression map (chr . bin2int) . chop8
already produces a function of type [Bit] -> String
- so all you have is to set decode =
to the expression's value.
With the first version, you are explicitly passing the xs
argument, so the expression to the right side of =
should be of type Int
. Since you are explicitly applying chop8 to xs, the result is a concrete list, not a function.
One solution is to explicitly apply map to the result of chop8:
decode xs = map (chr . bin2int) $ (chop8 xs)
Note the replacement of .
with $
- "apply function to parameter" instead of "compose function with another function"
Upvotes: 4