WHITECOLOR
WHITECOLOR

Reputation: 26122

No type class instance was found, the instance head contains unknown type variables

Well, just simplified as possible:

There is a function that takes functor and does whatever

sToInt :: ∀ a s. Functor s => s a -> Int
sToInt val = unsafeCoerce val

Usage of this function with functor S which param (v) is functor too.

-- declare date type S that is functor
data S (v :: Type -> Type) a = S (v a)

instance functorS :: Functor v => Functor (S v) where
    map f (S a) = S (map f a)

sV :: ∀ v a. S v a
sV = unsafeCoerce 1

sss :: Int
sss = sToInt sV -- get the error here

No type class instance was found for

    Data.Functor.Functor t2

  The instance head contains unknown type variables. Consider adding a type annotation.

while applying a function sToInt
  of type Functor t0 => t0 t1 -> Int
  to argument sV
while checking that expression sToInt sV
  has type Int
in value declaration sss

where t0 is an unknown type
      t1 is an unknown type
      t2 is an unknown type

So it doesn't like S Functor instance has v param Functor constraint, I wonder why getting this error and how to fix it for this case.

Upvotes: 0

Views: 346

Answers (1)

Fyodor Soikin
Fyodor Soikin

Reputation: 80734

This doesn't have to do with v or with the specific shape of S. Try this instead:

sV :: forall f a. Functor f => f a
sV = unsafeCoerce 1

sss :: Int
sss = sToInt sV

You get a similar error.

Or here's an even more simplified version:

sV :: forall a. a
sV = unsafeCoerce 1

sss :: Int
sss = sToInt sV

Again, same error.


The problem is that sToInt must get a Functor instance as a parameter (that's what the Functor s => bit in its type signature says), and in order to pick which Functor instance to pass, the compiler needs to know the type of the value. Like, if it's Maybe a, it will pass the Functor Maybe instance, and if it's Array a, it will pass the Functor Array instance, and so on.

Usually the type can be inferred from the context. For example when you say map show [1,2,3], the compiler knows that map should come from Functor Array, because [1,2,3] :: Array Int.

But in your case there is nowhere to get that information: sV can return S v for any v, and sToInt can also take any functor type. There is nothing to tell the compiler what the type should be.

And the way to fix this is obvious: if there is no context information for the compiler to get the type from, you have to tell it what the type is yourself:

sss :: Int
sss = sToInt (sV :: S Maybe _)

This will be enough for the compiler to know that v ~ Maybe, and it will be able to construct a Functor (S Maybe) instance and pass it to sToInt.


Alternatively, if you want the consumer of sss to decide what v is, you can add an extra dummy parameter to capture the type, and require that the consumer pass in a Functor v instance:

sss :: forall v. Functor v => FProxy v -> Int
sss _ = sToInt (sV :: S v _)

ddd :: Int
ddd = sss (FProxy :: FProxy Maybe)

In Haskell you can do this with visible type applications instead of FProxy, but PureScript, sadly, doesn't support that yet.


Even more alternatively, if sToInt doesn't actually care for a Functor instance, you can remove that constraint from it, and everything will work as-is:

sToInt :: forall s a. s a -> Int
sToInt a = unsafeCoerce a

sV :: forall v a. S v a
sV = unsafeCoerce 1

sss :: Int
sss = sToInt sV

This works because PureScript allows for ambiguous (aka "unknown") types to exist as long as they're not used for selecting instances.

Upvotes: 3

Related Questions