jeans
jeans

Reputation: 51

Why do I get an ambiguous type variable error using GHC.Generics?

I' ve tried the example from http://www.haskell.org/haskellwiki/GHC.Generics#A_generic_function

that is:

import GHC.Generics

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DefaultSignaturesGeneric #-}

data Bin = O | I deriving Show

data UserTree a = Node a (UserTree a) (UserTree a) | Leaf deriving Generic

class GSerialize f where
    gput :: f a -> [Bin]

instance GSerialize U1 where
    gput U1 = []

instance (GSerialize a, GSerialize b) => GSerialize (a :*: b) where
    gput (x :*: y) = gput x ++ gput y

instance (GSerialize a, GSerialize b) => GSerialize (a :+: b) where
    gput (L1 x) = O : gput x
    gput (R1 x) = I : gput x

instance (GSerialize a) => GSerialize (M1 i c a) where
    gput (M1 x) = gput x

instance (Serialize a) => GSerialize (K1 i a) where
    gput (K1 x) = put x

class Serialize a where
    put :: a -> [Bin]
    default put :: (Generic a, GSerialize (Rep a)) => a -> [Bin]
    put = gput . from

instance Serialize Int
instance Serialize Char
instance Serialize Bool
instance (Serialize a) => Serialize (UserTree a)

toserialize :: (Serialize (UserTree a) ) => UserTree a -> [Bin]
toserialize tree = put tree

I tested the function with the call:

toserialize Leaf

here the compiler gives an error-message:

 Ambiguous type variable `a0' in the constraint:
   (Serialize a0) arising from a use of `toserialize'
 Probable fix: add a type signature that fixes these type variable(s)
 In the expression: serialize Leaf
 In an equation for `it': it = toserialize Leaf

What is missing here?

Upvotes: 2

Views: 192

Answers (1)

shang
shang

Reputation: 24802

The problem is that since the type variable a in UserTree a doesn't appear at all in the Leaf constructor, GHC cannot determine which a you want for your value. You can fix this by adding an explicit type signature such as

toserialize (Leaf :: UserTree Int)

UPDATE: The example toserialize (Node 2 Leaf Leaf) hangs because the generic representation for Int is recursive

type Rep Int = D1 D_Int (C1 C_Int (S1 NoSelector (Rec0 Int)))

For "primitive" types which don't have "normal" constructors, you usually have to define instances by hand. However, since for example Bool is simply

data Bool = True | False

This works with the generic Serialize instance

toserialize (Node True Leaf Leaf)

Upvotes: 2

Related Questions