Search code examples
haskelltype-parameter

Type deduction in function composition


Hello i have a type with a type parameter:

data A=A1| A2 deriving (Show)
data B=B1| B2 deriving(Show)
data C a=C{var::a,var2::Int}

getters=[show . var,show .var2]

I get the following error in the getters at show . var :

Ambiguous type variable `a0' arising from a use of `show'
      prevents the constraint `(Show a0)' from being solved.
      Relevant bindings include
        getters:: [Worker a0 -> String]

Do i have to explicitly state the type ,something like : show. (var::B).I do not really understand the error since both types A and B are implementing Show


Solution

  • Edit: updated below to answer your follow-up question.

    To answer your original question, this is just a situation where the type checker needs a little help. You can get your code to type check by adding an explicit signature for getters, as @WillemVanOnsem has suggested:

    data A = A1 | A2 deriving (Show)
    data B = B1 | B2 deriving (Show)
    data C a = C { var :: a, var2 :: Int }
    
    getters :: (Show a) => [C a -> String]
    getters = [show . var, show . var2]
    

    or, alternatively, by turning on the NoMonomorphismRestriction GHC extension, as @DanielWagner points out. After doing either of these, the following works fine:

    > map ($ C A1 10) getters
    ["A1","10"]
    > map ($ C B2 10) getters
    ["B2","10"]
    > 
    

    This signature doesn't make getters a "method", if your comment about methods was directed at Willem. It's still a polymorphic expression with type [C a -> String] (for any type a with a Show a constraint).

    Update: As per your comments, you think you want to define a data type with a constraint on the type of one or more of its fields. (You don't actually want to do this -- you just think you do -- but who am I to stop you?)

    So, to do this, you'll need to enable the DatatypeContexts extension and write:

    {-# LANGUAGE DatatypeContexts #-}
    
    class Mytypeclass a where
      whatever :: a -> String
    
    data Mytypeclass a => MyType a = M { var :: a, var2 :: a }
    

    The compiler will generate a warning that this extension was deprecated because it was widely considered a misfeature and removed from the language.

    Now, you can define:

    getters = [whatever . var, whatever . var2]
    

    This will give you an error, though a different error message than before, after which you can either add the signature:

    getters :: (Mytypeclass a) => [MyType a -> String]
    getters = [whatever . var, whatever . var2]
    

    or else enable the NoMonomorphismRestriction extension in order to get it to compile, just like before.