7

Just a quick conceptual question, I am currently trying to learn and understand Haskell better.

I know the Show function is used to convert values to strings, but why can't function types be used with show?

Prelude> (\x -> x*3)

<interactive>:7:1:
    No instance for (Show (a0 -> a0))
      arising from a use of `print'
    Possible fix: add an instance declaration for (Show (a0 -> a0))
    In a stmt of an interactive GHCi command: print it
Prelude>
hammar
  • 138,522
  • 17
  • 304
  • 385
Justin Tyler
  • 225
  • 1
  • 5
  • 7

3 Answers3

10

It's not that they can't, but that there's not usually a good reason to.

But if you'd like, you definitely can:

Prelude> :{
Prelude| instance Show (a -> b) where
Prelude|    show _ = "A function."
Prelude| :}
Prelude> print (\x -> x + 7)
A function.
Prelude> print (\a b c -> a + b + c)
A function.

If you'd like to show the textual representation of the function, well - you can't do that. Unlike metaprogramming languages like Ruby, JS, etc, Haskell code very little knowledge of its own internals.

amindfv
  • 8,438
  • 5
  • 36
  • 58
  • 3
    There's actually a `Show` instance built in. This is something of an FAQ - [here](http://stackoverflow.com/questions/15015698/derive-eq-and-show-for-type-alias-in-haskell/15015731#15015731) and [here](http://stackoverflow.com/questions/10551210/instance-show-for-function/10551513#10551513) for example. – Thomas M. DuBuisson Apr 05 '13 at 03:22
8

There is a partial solution that goes beyond just a fixed string for all functions using Data.Typeable.

{-# LANGUAGE ScopedTypeVariables #-}

import Data.Typeable

instance (Typeable a, Typeable b) => Show (a->b) where
  show _ = show $ typeOf (undefined :: a -> b)

in ghci

> let test :: Int->Int; test x = x + x
> test
Int -> Int

Unfortunately without a type signature the type will go to it default.

> let test x = x + x
> test
Integer -> Integer

This solution works on multiple function arities because a -> b -> c is the same as a -> (b -> c) which you might as well write as a -> d where d = b -> c.

> let m10 a b c d e f g h i j = a * b * c * d * e * f * g * h* i * j
> m10
Integer -> Integer -> Integer -> Integer -> Integer -> Integer -> Integer
        -> Integer -> Integer -> Integer -> Integer

This method does not work however when it is unknown if parameters of the function have the typeable class however so while map (+1) will work map will not.

> map (+1)
[Integer] -> [Integer]
> map

<interactive>:233:1:
...

After glancing at the internals of Data.Data and an experiment or two it seems like it could be refactored to be a little more generalized cover more functions.

Davorak
  • 7,362
  • 1
  • 38
  • 48
3

show is the function that is defined on functions that are members of the Show typeclass (if you don't know what a typeclass is, it's kinda like an OOP interface).

By default, functions are not members of the typeclass, so we can't print them.

We could make it a member of the typeclass with

instance Show (a -> b) where
   show f = "Unicorns!!"

but here we realize why it isn't implemented by default. There isn't a simple, obvious representation of functions and haskell doesn't want to guess, and thus no instance.

The only "permissible" instance would be one that actually prints out the function, but this would require actual language change, ie it would be hardwired into the compiler, which just isn't worth it for the few cases in which it could be useful.

Further more it's a nontrivial compiler change, Haskell is compiled which means the differences between something like f = g and

f =             g 

are entirely lost on it. But you'd definitely want that in your function representation. Because of this, you'd have to lug around this string through out the program. This is definitely not what you want in a binary.

If you really want it to print unicorns!! though, feel free.

daniel gratzer
  • 52,833
  • 11
  • 94
  • 134
  • Jynx! I think it's worth noting why (big) changes to the compiler would be required: compiling Haskell strips away many details of the code. – amindfv Apr 05 '13 at 00:04
  • Well in fairness, with ghc, the intermediate languages do retain a fair amount of information, however the actual function text, ie stuff like whitespace, vanish after parsing and are gone forever. Ps "Unicorn!!" is far more informative than "A function." :P – daniel gratzer Apr 05 '13 at 00:06
  • 4
    An argument could also be made that semantically, `\x -> 3*x` and `\x -> x*3` are the same function (at least on `Int`), so you'd either have to arbitrarily pick one string to represent them both or work in the `IO` monad to distinguish between them. – hammar Apr 05 '13 at 00:16