gogurt
gogurt

Reputation: 831

Partial application of data constructor

I don't understand why the following exercise "works" in Haskell Programming from First Principles:

type Subject = String
type Verb = String
type Object = String

data Sentence =
    Sentence Subject Verb Object
    deriving (Eq, Show)

s1 = Sentence "dogs" "drool"
s2 = Sentence "Julie" "loves" "dogs"

Loading this into ghci shows that it typechecks just fine, but why is it that the definition of s1 even makes sense? I'm still very new to Haskell, so at first I thought this was because in s1 Haskell was implicitly letting the Object string be empty. But then...

*Main> s1

<interactive>:13:1:
    No instance for (Show (Object -> Sentence))
      arising from a use of `print'
    Possible fix:
      add an instance declaration for (Show (Object -> Sentence))
    In a stmt of an interactive GHCi command: print it

I'm still learning how to properly interpret these error messages, so please bear with me. But can someone explain what No instance for (Show (Object -> Sentence)) means? More specifically, how does leaving out the Object string in s1 result in this (Object -> Sentence) thing?

I'm sure this is stupid easy, but I don't think the book has equipped me to understand this by this point...

Upvotes: 4

Views: 956

Answers (2)

dimid
dimid

Reputation: 7631

No instance for (Show (Object -> Sentence))
  arising from a use of `print'
Possible fix:
  add an instance declaration for (Show (Object -> Sentence))
In a stmt of an interactive GHCi command: print it

To supplement @ErikR's answer: you may be wondering why GHC doesn't have a built-in support for displaying functions, i.e. unlike integers and strings, functions don't have an instance for the Show typeclass (these terms will be explained in depth later in the book, so don't worry if you don't understand what's a typeclass and an instance), unless you explicitly define it yourself. As someone who is learning Haskell and comes from an Object-Oriented background, I found it easier to get an intuition for typeclasses by thinking of them as Java-like interfaces.

So, why there's no Show insance for functions? The Haskell wiki provides two answers:

1.Practically, GHC doesn't keep track of variable names, i.e. the following are the same for the compiler:

addOne num = num + 1
f x = x + 1
f y = y + 1  

Additionally, functions can be optimized, e.g. the following may have equivalent representations

f x = x - x + x
f x = x

2.Theoretically, a function is defined by its graph, i.e. the set of (input, output) pairs. e.g. for

f x = x + x

the pairs are (1,2), (2,4), etc. Thus, functions with the same graph are identical GHC-wise, e.g

f x = x + x
g y = 2 * y

but you would expect show f and show g to be different, especially if you use significant variable names instead of x and y.

That said, you can use a pragma (an extension for the GHC compiler, that contains some functionality beyond the Haskell language standard) that would show only the function's type, as explained in this answer:

{-# LANGUAGE ScopedTypeVariables #-}

import Data.Typeable

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

This will get you

> s1
[Char] -> Sentence

since Object is an alias of String (using the type keyword, String is itself an alias for [Char]). If you want to explicitly see the distinction between subjects and objects, you can convert Object to a type with a datatype constructor MkObject:

newtype Object = MkObject String deriving (Eq, Show)

s1 = Sentence "dogs" "drool"
s2 = Sentence "Julie" "loves" (MkObject "dogs")

and voilà

> s1
Object -> Sentence

Upvotes: 0

ErikR
ErikR

Reputation: 52039

but why is it that the definition of s1 even makes sense?

As @Alec mentioned, it's called currying. One way to see what is going on is to have GHCI tell you what the type of s1 is:

ghci> :t s1
s1 :: Object -> Sentence

So s1 is a function taking an Object to a Sentence. Another way to think about is to start with the definition:

s1 = Sentence "dogs" "drool"

and using equational reasoning apply both sides to a value x:

s1 x = Sentence "dogs" "drool" x

So when you call s1 x it's the same as calling Sentence with the first two function arguments hard-coded to "dogs" and "drool", and x becomes the third argument to the Sentence function.

can someone explain what "No instance for (Show (Object -> Sentence))" means?

When you evaluate something in GHCI is it basically the same as asking Haskell to print it. That is,

ghci> 3+4

is effectively the same as:

ghci> print (3+4)

(This rule doesn't apply to IO-actions like getLine or even print itself. In those cases Haskell just runs the IO-action.)

In order to print something there has to be a Show instance for the type. But as we've seen above, s1 is a function of type Object -> Sentence, and there are no predefined Show instances for functions.

Note that there is a Show instance for Sentence values because you asked GHC to derive one with deriving (Eq, Show). So when you type at the GHCI prompt:

ghci> Sentence "Julie" "loves" "dogs"

you get back:

Sentence "Julie" "loves" "dogs"

because you are really asking GHCI to run print (Sentence "Julie" "loves" "dogs").

Note that print itself is defined as (link):

print x = putStrLn (show x)

and the call to show is the reason why a value needs to have a Show instance defined for it in order to print it.

Upvotes: 8

Related Questions