Reputation: 61
I have a function that takes 3 functions and combines them to alter a list argument.
For example a test case call would be: chain init tail reverse "Haskell!" the output should be lleksa
I've tried to do this problem a few different ways including using the map
function but I kept getting association problems. so i did
chain :: Ord a => [a] -> a
chain f g h x = f.g.h$x
and the error was Couldn't match expected type [t0 -> t1 -> t2 -> a0]
When I type the problem directly in GHCi like by writing, for instance, init.tail.reverse$"Haskell!"
it works properly
Is there even a way to include three function arguments? I've only seen two in examples.
Upvotes: 1
Views: 711
Reputation: 51393
When prelude analyses your function:
chain f g h x = f.g.h$x
it assumes that you are receiving the function f
, g
, and h
. Why it is assuming that? Because the purpose of the .
operator is to chain functions. So if you are using it is because you are chaining functions.
You defined a type signature for your function ([a] -> a)
that is different from what your function should receive and return. One solution is to not specify the type signature and leave it to prelude, another is to correct the type signature.
But if you are expecting your function to receive a list of a
and return an a
you should modify your function to something like this:
chain :: (Ord a) => [a] -> a
chain (x:xs) = ...
Upvotes: 1
Reputation:
Is there even a way to include three function arguments? I've only seen two in examples.
You should probably review whatever introductory material you've read, or pick up some introductory material if you can. You seem to be quite confused.
When I type the problem directly in GHCi like by writing, for instance,
init.tail.reverse$"Haskell!"
it works properly
You can use ghci
to find the type of your expression.
λ:> chain f g h x = f.g.h$x
λ:> :t chain
chain :: (b1 -> c) -> (b2 -> b1) -> (a -> b2) -> a -> c
This is just a specialization of
chain :: ([a] -> [a]) -> ([a] -> [a]) -> ([a] -> [a]) -> [a] -> [a]
which is I'm guessing is what you wanted to write.
For what it's worth, you would probably want something like the following if you're just working on lists.
chain :: [[a] -> [a]] -> [a] -> [a]
chain = foldr (.) id
Spin up GHCi:
λ:> chain = foldr (.) id
λ:> chain [init, tail, reverse] "Haskell!"
"lleksa"
And you get the result I'm guessing you want. If you don't understand how this works yet, don't worry, and stick with the other solution and come back when you feel ready.
Upvotes: 0
Reputation: 63349
The most general type signature for a higher-order function that composes 3 functions would be:
chain :: (b -> c) -> (b1 -> b) -> (a -> b1) -> a -> c
chain f g h x = f.g.h$x
(you can also write the definition without x
, just
chain f g h = f.g.h
). Notice that the return types of intermediate functions b
and b1
are arbitrary, the only requirement is that they must match the type of the argument of the next function. Now, if you call chain init tail reverse "Haskell!"
you'll get "lleksa"
.
If you know how to write a function, but you don't know its proper type, you can let GHCi infer the type for you. Just load the function there and type for example :t chain
(:t
is a shorthand for :type
, see GHCi commands).
You could go even further and make a composition of any number of functions. (But in this case the type system forces you to a bit less general type signatures.)
chainN :: [a -> a] -> (a -> a)
chainN fs = foldr (.) id fs
This function takes a list of functions from a
to a
and composes them together. If the list is empty, it returns just the identity function. With chainN
you can write things like
chainN [init, tail, reverse] "Haskell!"
Upvotes: 6