I have the following type classes:
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
class Interpretation i where
type Context i :: * -> *
class Interpretation i => Interpret i a where
type Interpreted i a :: *
int :: a -> Context i (Interpreted i a)
Which basically encode interpretation functions from a to Interpreted i a wrapped in some context.
I've written these instances:
data X = X
instance Interpretation X where
type Context X = Identity
instance Interpret X Int where
type Interpreted X Int = Bool
int :: Int -> Identity Bool
int x = pure (x == 0)
Now suppose that I need to use int above to interpret an Int into a Bool, for instance in the following function:
f :: Int -> Bool
f = -- use int somehow
If I define f as follows:
f x = int x
The compiler will complain:
• Couldn't match type ‘Interpreted i0 Int’ with ‘Bool’
Expected type: Int -> Identity Bool
Actual type: Int -> Context i0 (Interpreted i0 Int)
The type variable ‘i0’ is ambiguous
• In the second argument of ‘(.)’, namely ‘int’
In the expression: runIdentity . int
In an equation for ‘f’: f = runIdentity . int
By using TypeApplications I can select the right (and only available) instance:
{-# LANGUAGE TypeApplications #-}
-- ...
f :: Int -> Bool
f = runIdentity . int @X
But I was wondering whether such instance could be selected without resorting to TypeApplications.