Summary
I have a typeclass for which I want to write some 'generic terms'. I have two questions:
- Using
:tto ask GHCi for the type of a generic term works, but using that inferred type fails- why? - How do I use
TypeApplicationswith the methods of a typeclass?
I am using GHC 8.8.4. For both problems I have the following example Main.hs containing a typeclass F and type Empty which is an instance of F.
{-# LANGUAGE NoStarIsType #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module Main where
import GHC.Types (Type)
class F (f :: k -> Type) where
type Plus f (a :: k) (b :: k) :: k
zero :: f a
plus :: f a -> f b -> f (Plus f a b)
data Empty (a :: Type) = Empty
instance F Empty where
type Plus Empty a b = (a, b)
zero = Empty
plus _ _ = Empty
1. Inferred Types don't work?
I would like to construct a generic term of the typeclass F. For example, plus zero zero.
When I ask GHCi for the type of this term it gives me what I would expect:
*Main> :t plus zero zero
plus zero zero :: F f => f (Plus f a b)
Surprisingly, if I try to assign this term, I get an error. Namely, if I add the following to Main.hs:
-- This doesn't work.
plusZero :: F f => f (Plus f a b)
plusZero = plus zero zero
Reloading the file in GHCi complains with the error:
• Couldn't match type ‘Plus f a0 b0’ with ‘Plus f a b’
Expected type: f (Plus f a b)
Actual type: f (Plus f a0 b0)
NB: ‘Plus’ is a non-injective type family
The type variables ‘a0’, ‘b0’ are ambiguous
• In the expression: plus zero zero
In an equation for ‘plusZero’: plusZero = plus zero zero
My first question is: why does GHCi seem to infer the type, but reject it when I annotate the term explicitly?
2. Using TypeApplications instead of annotations
I can get around the first problem simply by annotating the types of zero terms:
-- This works
plusZero1 :: forall f a b . F f => f (Plus f a b)
plusZero1 = plus (zero :: f a) (zero :: f b)
However, this is a little clunky when terms get large. What I want to do is use TypeApplications. I tried this:
-- This doesn't work
plusZero2 :: forall f a b . F f => f (Plus f a b)
plusZero2 = plus @f @a @b zero zero
But GHCi complains:
• Expecting one more argument to ‘f’
Expected a type, but ‘f’ has kind ‘k -> *’
• In the type ‘f’
In the expression: plus @f @a @b zero zero
In an equation for ‘plusZero2’: plusZero2 = plus @f @a @b zero zero
• Relevant bindings include
plusZero2 :: f (Plus f a b) (bound at Main.hs:36:1)
Strangely, if I first define additional functions plus' and zero' as follows, everything works as expected:
zero' :: forall f a . F f => f a
zero' = zero
plus' :: forall f a b . F f => f a -> f b -> f (Plus f a b)
plus' = plus
-- This works fine
plusZero3 :: forall f a b . F f => f (Plus f a b)
plusZero3 = plus' @f @a @b zero' zero'
So it seems I haven't understood how TypeApplications works with typeclass methods.
How can I use a type application with plus and zero without having to define the additional functions plus' and zero'?