[Haskell-cafe] Typeclasses question in "Real World Haskell" book

Daniel Fischer daniel.is.fischer at web.de
Mon Jul 26 10:03:50 EDT 2010


On Monday 26 July 2010 15:16:36, Angel de Vicente wrote:
> Hi,
>
> I'm stuck at page 151 of Real World Haskell and hoping that perhaps some
> of you can give me a hand here...
>
> The code that is giving me trouble is below.
>
> data JValue = JString String
>
>              | JNumber Double
>              | JBool Bool
>              | JNull
>              | JObject [(String, JValue)]
>              | JArray [JValue]
>
>                deriving (Eq, Ord, Show)
>
> type JSONError = String
>
> class JSON a where
>      toJValue :: a -> JValue
>      fromJValue :: JValue -> Either JSONError a
>
> instance JSON JValue where
>      toJValue = id
>      fromJValue = Right
>
> instance JSON Bool where
>      toJValue = JBool
>      fromJValue (JBool b) = Right b
>      fromJValue _ = Left "not a JSON boolean"
>
>
> I don't understand how the JSON typeclass is defined, in particular the
> fromJValue definition.

Given a JValue and a type 
(like Bool, JValue, String, Maybe [(Integer, ())]), fromJValue returns 
either

Left errormessage

or 

Right (value of desired type)

>
> For instance, when defining the instance for Bool types, then I
> understand that both functions (toJValue and fromJValue) will be called
> upon when we supply a Bool type, but then the (JBool b) type in function
> fromJValue doesn't match....

fromJValue always takes a JValue as argument. That JValue can be a wrapped 
String, a wrapped Bool, a wrapped number (Double), ...

Depending on the result type (Either JSONError a), it returns a wrapped 
value of type a [Right a] or a wrapped error message [Left JSONError]

>
> toJValue is no problem, but I cannot understand how fromJValue is
> supposed to work, and the comments in the online book
> (http://book.realworldhaskell.org/read/using-typeclasses.html) don't
> help with this either.
>
>   *Main> :load ch6
> [1 of 1] Compiling Main             ( ch6.hs, interpreted )
> Ok, modules loaded: Main.
> *Main> toJValue False
> JBool False
> *Main> :type it
> it :: JValue
> *Main> fromJValue False
>
> <interactive>:1:11:
>      Couldn't match expected type `JValue' against inferred type `Bool'
>      In the first argument of `fromJValue', namely `False'
>      In the expression: fromJValue False
>      In the definition of `it': it = fromJValue False

That one should be pretty clear, fromJValue expects a JValue as argument 
and gets a Bool, it's like calling

fromInteger True

> *Main> fromJValue (JBool False)
>
> <interactive>:1:0:
>      Ambiguous type variable `a' in the constraint:
>        `JSON a' arising from a use of `fromJValue' at
> <interactive>:1:0-23 Probable fix: add a type signature that fixes these
> type variable(s) *Main>

That's less easy.
The compiler/interpreter doesn't know which result type to use.

fromJValue :: JSON a => JValue -> Either JSONError a

with which type should a be instantiated, should it use
- JValue, in which case the result would be 
Right (JBool False)

- Bool, in which case the result would be
Right False

- String, in which case the result woulde be something like
    No instance for (JValue [Char])
      arising from a use of `fromJValue' at ...

(unless you have such an instance in scope, then it would be something like
Left "not a JSON string")

- Int, in which case you'd get analogous behaviour
- ...

>
>
>
> Any pointers?

In an actual programme, there is usually enough context to fix the type 
variable a, then the compiler/interpreter knows what to do.
At the prompt or when there isn't enough context otherwise, you need to 
explicitly tell the compiler/interpreter which type to use,

*Main> fromJValue (JBool False) :: Either JSONError Bool
Right False
*Main> fromJValue (JBool False) :: Either JSONError JValue
Right (JBool False)

>
> Thanks a lot,
> Ángel de Vicente



More information about the Haskell-Cafe mailing list