1

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.

ljedrz
22.6k5 gold badges77 silver badges110 bronze badges
asked Dec 5, 2012 at 3:58
6
  • 3
    Can you explain why you gave the function that type signature? Commented Dec 5, 2012 at 4:08
  • Oh sorry, didn't realize I put that type signature, but it's defined in my program, I'll just change my example type case. Commented Dec 5, 2012 at 4:18
  • 2
    I don't understand what you just said, but your function works perfectly fine once you remove the type signature (or replace it with a correct one). Commented Dec 5, 2012 at 4:29
  • It's more like the other way around, sorry I'm new to Haskell still trying to get the terminology down, I got that type signature from a website that said that was they way to do composition. So I toke out the type signature and now I'm getting a parse error on the = Commented Dec 5, 2012 at 4:51
  • All I can tell you is that I don't get a parse error when I remove the type signature. Maybe you have a mistake earlier in the file? Commented Dec 5, 2012 at 5:16

3 Answers 3

6

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!"
answered Dec 5, 2012 at 8:25
Sign up to request clarification or add additional context in comments.

1 Comment

Or you could go even more further and define chainN as fold over a thrist of functions, which gives you back full generality. (And because I think thrists are underrepresented...)
1

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) = ...
answered Dec 5, 2012 at 4:56

Comments

0

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.

answered Sep 9, 2017 at 6:49

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.