I was doing a Haskell types exercise and this one has stumped me. The provided expression is:
f2 f g h = h.g.f
And the type for f2 is apparently:
f2 :: (a -> b1) -> (b1 -> b) -> (b -> c) -> a -> c
This seems overly complicated for such a short expression. Can someone explain why this makes sense as the type?
I find these “determine the type of such and such expression” generally a bit backwards. Types should always come first: you want to program a solution to some task, you formulate the problem description as a type signature. Then you go ahead and actually write an implementation.
In this case, you'd start with the problem: I have three functions f
, g
and h
, and a value of type that I can pass to f
. Furthermore, the functions have pairwise matching result/argument type. Hence the signature
f2 :: (α -> β) -> (β -> γ) -> (γ -> δ) -> α -> δ
You could now go on and implement this in explicit-pointed form, ie
f2 f g h x = h (g (f x))
which is still quite brief. After all, it's quite a simple task!
But in Haskell you can make it even shorter, by using the standard composition operator .
. The fact that the final implementation is so extremely short is basically just down to the fact that f2
does essentially the same thing as .
, just twice. So this isn't more surprising than if you have a very complex task with a complicated type signature, but discover some library that contains a function which does almost that exact task. Obviously, invoking that ready-build function will give you a much shorter implementation than the task complexity would suggest, but the complexity is merely deferred to the library function.
Why do you think it's complicated? It's just a function that takes three functions (of matching types) and returns a new one.
I've renamed b1
to b
and updated other names, for consistency. So here is a slightly edited version:
f2 :: (a -> b) -> (b -> c) -> (c -> d) -> a -> d
f2
takes f
which has type a -> b
. Meaning, it's a function, with some input of some type a
( a
can be just anything, no restrictions here) and some return value of type b
. f2
takes g
which input's type must match f
's output, so it's b -> c
. It cannot be, like, e -> c
(where e
is something different from b
), or the code won't make sense as it won't be possible to compose f . g
f . g
. h
, with type c -> d
. f2
returns) is a new function that takes in whatever f
does and returns whatever h
returns, so it's a -> d
. With this we've covered the whole type definition. Basically, it's a relatively long definition, but I think it's of a very simple nature.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.