Can't infer (simple) Typeclass out of context

I am getting an error on a fairly simple example and I cannot figure out what is wrong. What I am doing is very similar to mempty

in Monoid

, here is a simple version (I include my quantified type in case something is related to the problem):

data Field a = Field String
              | forall b. EsMappable b => ReferenceField String (a -> b)

class EsMappable a where
    fields :: [Field a]

toMapping :: (EsMappable a) => a -> String
toMapping a = go "" fields
  where go str [] = str                        
        go str (ReferenceField name _ : xs) = go (name ++ str) xs
        go str (Field name : xs) =go (name ++ str) xs

      

The error I am getting:

Could not deduce (EsMappable t0) arising from a use of β€˜fields’
from the context (EsMappable a)
  bound by the type signature for
             toMapping :: EsMappable a => a -> String
  at search2.hs:11:14-42
The type variable β€˜t0’ is ambiguous
In the second argument of β€˜go’, namely β€˜fields’
In the expression: go "" fields
In an equation for β€˜toMapping’:
    toMapping a
      = go "" fields
      where
          go str [] = str
          go str (ReferenceField name _ : xs) = go (name ++ str) xs
          go str (Field name : xs) = go (name ++ str) xs

      

Note : if I change the class EsMapapble

to: fields :: a -> [Field a]

and then toMapping

change to go "" (fields a)

, it works.

My question is , why am I getting this error? This is not the same as mempty

? What prevents the GHC from resolving correctly fields

?

Thank!

+3


source to share


1 answer


The problem is that the compiler has no way to bind the usage fields

to your argument a

. Your function go

can accept [Field a]

for anyone a

, so you need to somehow specifically constrain it to the same as a

in your argument type.

You can do it with ScopedTypeVariables

:

toMapping :: forall a. (EsMappable a) => a -> String
toMapping _ = go "" (fields :: [Field a])

      

You will need an additional forall a

one to explicitly use the type variable a

. This limitation is ScopedTypeVariables

in the interest of backward compatibility.



It also wouldn't be a problem if you used your argument a

with values Field a

in go

. As a contrived example, the following typechecks are not explicitly signed:

go str (ReferenceField name f : xs) = go (name ++ str) xs `const` f a

      

This forces you f

to accept a type argument a

that holds the entire list for that particular one a

. So you could use this trick to avoid expanding ScopedTypeVariables

if you really wanted to! I wouldn't say that though: the extension is about as harmless as they are, and makes the code clearer. This example was just to illustrate my point.

+5


source







All Articles