2
compose :: [(u -> t)] -> ((u -> t) -> (u->y)) -> [(u->y)] 
compose [] _ = []
compose l f = map f l

Im trying to create a function that receive a list of functions and maps another function over the elements of this lists.

Example: compose [(+2),(+3)] (+1) = [(+3),(+4)]

thats the message the console shows me when i try to run this code

*Main> (compose [(+2)] (+1))

:77:2: No instance for (Show (t0 -> t0)) arising from a use of `print' In a stmt of an interactive GHCi command: print it *Main>

5
  • Functions are not an instance of Show, so they can't be printed for visualization. Commented Apr 13, 2015 at 16:24
  • 2
    I just wanted to add that: you've got the right intuition that compose is just a domain-restriction of flip map. (As others have said, the problem is that if you write (+3) into the console you will generate the same error because GHC does not know a canonical way to print the function \x -> x + 3.) Commented Apr 13, 2015 at 18:44
  • 1
    The type of your example and the declared type of compose do not match. In particular: in your example the second argument to compose is (+1), while the second argument of compose has the declared type ((u -> t) -> (u -> y)). And (+1) definitely does not have this latter type. You should probably try to first get the type of compose to be what you want it to be. Commented Apr 13, 2015 at 21:59
  • The way the example is currently written, the primary problem is not that functions can't be printed. It is somewhat more basic. Namely, that there is no type which is both a Num and a function from Num to Num . As currently written, the type of compose [(+2),(+3)] (+1) is (Num (y -> y), Num y) => [y -> y] !! Commented Apr 13, 2015 at 22:07
  • This is a hint that merely mapping a function over a list of functions is probably not what you want to do. Commented Apr 13, 2015 at 22:13

1 Answer 1

6

There isn't a way to intelligently convert a function to a string, which is what the error message you see here is saying. There isn't a Show instance for functions, and you need a Show instance to see output in GHCi. When you create a function in Haskell the compiler turns it into low level commands, you don't have the original function definition preserved in metadata or anything. You wouldn't be able to see (+1) . (+2) become (+3), it just isn't how Haskell functions work.

Instead you could assign it to a name:

> let fs = compose [(+2)] (+1)

Then apply values to it

> map ($ 10) fs
[13]

If you want to be able to turn something like (+2) and (+1) into (+3), you'll need to create your own data type. This means that the functions you can represent are severely limited in what they can do unless you define very generic behavior. For simple functions on Ints, you could do

data ArithFunc
    = Add Int
    | Subtract Int
    | Multiply Int
    | ModBy Int
    | Abs
    | Negate
    | Compose ArithFunc ArithFunc
    deriving (Eq, Show)

Then you could write a custom compose operator:

toFunction :: ArithFunc -> (Int -> Int)
toFunction (Add x) = (+x)
toFunction (Subtract x) = subtract x
toFunction (Multiply x) = (*x)
toFunction (ModBy x) = (`mod` x)
toFunction Abs = abs
toFunction Negate = negate
toFunction (Compose f1 f2) = toFunction f1 . toFunction f2

infixr 9 #
(#) :: ArithFunc -> ArithFunc -> ArithFunc
f1 # f2 = simplify $ Compose f1 f2

infixr 0 $$
($$) :: ArithFunc -> Int -> Int
f $$ x = toFunction f x

simplify (Compose (Add x) (Add y)) = Add (x + y)
simplify (Compose (Add x) (Subtract y)) = Add (x - y)
-- Continue adding simplification rules as desired

compose :: [ArithFunc] -> (ArithFunc -> ArithFunc) -> [ArithFunc]
compose l f = map (simplify . f) l

Then you could write

> compose [Add 2] (Add 1)
[Add 3]
> map ($$ 10) $ compose [Add 2] (Add 1)
[13]

And this solution is of no where near complete, you would really need to define simplify in such a way that it continues to simplify nested Compose constructs until no change was made, there needs to be more rules for simplification, there are other operations that could be represented, resulting in a larger number of simplification rules needed, and more. All of this work above is just to do this sort of thing with a limited set of numeric computations on Ints only, imagine expanding this to work for all types in general. This is why Haskell chooses a simpler route of storing the function definition less literally, and why you therefore can't show a function.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

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