Reputation: 831
I'm going through the new book Haskell Programming from First Principles. It seems decent, but I feel that there are some confusing holes in the explanations. I apologize if I'm missing something basic.
The last problem in chapter 5 is to fill in the ??? below so that things make sense:
munge :: (x -> y) -> (y -> (w, z)) -> x -> w
munge = ???
The solution which was explained to me (after much head-scratching) goes:
g :: y -> (w, z)
g = undefined
f :: x -> y
f = undefined
munge :: (x -> y) -> (y -> (w, z)) -> x -> w
munge g f v = fst (g (f v))
I'm getting hung up on this example in two ways.
First, it seems like the munge
function ought to take a function as input which takes x -> y
. But the way munge
is defined, it seems like we supply an additional argument v
to the function f
first. But if f :: x -> y
, then won't the expression f v
be of type just y
instead of x -> y
?
Second, I'm struggling to understand why the x
appears in the second-to-last position in the type declaration. At that point I feel like the logical next piece after the (y -> (w,x))
step should just be w
, since at that stage the function g
is being applied to fst
and w
ought to be the type of what fst
returns. I can feel that I'm close, but can't quite close the gap.
Clearly I'm not understanding the notation correctly. Can anyone set me straight?
EDIT: Ok, here is a clarifying question to the second part. Is it possible to revise the munge
function so that it has the following type (i.e. original type with second-to-last x
application omitted)? If so what would it look like?
munge :: (x -> y) -> (y -> (w, z)) -> w
Upvotes: 0
Views: 87
Reputation: 531245
The solution, confusingly, is using the same variables f
and g
for two different things: as global names for two functions, and as parameter names in defining munge
. Making a change of variable should make it clearer:
g :: y -> (w, z)
g = undefined
f :: x -> y
f = undefined
munge :: (x -> y) -> (y -> (w, z)) -> x -> w
munge f1 f2 v = fst (f2 (f1 v)) -- fst . f2 . f1 $ v
Then you would call munge
on f
and g
will thing like
munge f g someArgumentForF
Inside munge
, f
(called f1
) is first applied to someArgumentForF
(called v
) to get a value that can be passed to g
(called f2
). This produces a tuple, and applying fst
to the tuple returns the value of type w
needed as the final result.
Upvotes: 2
Reputation: 16645
The answer is incorrect and ill-typed. f
and g
should be swapped:
munge :: (x -> y) -> (y -> (w, z)) -> x -> w
munge g f v = fst (f (g v))
I'm not sure if that clears up your confusion.
EDIT In case it's interesting, here are more equivalent ways of writing this function and its type:
-- notice parens in type signature; `->` associates right
munge :: (x -> y) -> ((y -> (w, z)) -> (x -> w))
munge g f v = -- omitted
-- type signature omitted
munge boop _plort zOWY = fst (_plort (boop zOWY))
munge g f = fst . f . g
munge g = \f v -> fst . f . g $ v
-- don't do this please
munge = ((fst .) .) . (.)
EDIT2 It might be helpful to play around with this in GHCi, asking the inferred type of different expressions:
Prelude> let munge g f v = fst (f (g v))
Prelude> :t munge
munge :: (t1 -> t) -> (t -> (a, b)) -> t1 -> a
Prelude> :t munge head
munge head :: (t -> (a, b)) -> [t] -> a
Prelude> :t munge head (\x-> (x, not x))
munge head (\x-> (x, not x)) :: [Bool] -> Bool
Prelude> :t munge ((+1) . fst . snd . head)
munge ((+1) . fst . snd . head)
:: Num t => (t -> (a, b)) -> [(a1, (t, b1))] -> a
Upvotes: 4