After reading the excellent and interesting paper Typed Tagless-Final Interpretations: Introductory Course from Oleg Kiselyov I tried to convert normal Haskell lists to Tagless Final lists and failed:
This is working:
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE MultiParamTypeClasses #-}
module TList
where
class TList repr where
tnil :: repr
tcons :: Int -> repr -> repr
tlist0 :: TList repr => repr
tlist0 = tnil
tlist1 :: TList repr => repr
tlist1 = tcons 1 $ tcons 2 $ tcons 3 tnil
instance TList String where
tnil = "tnil"
tcons hd tail = "tcons " ++ show hd ++ " $ " ++ tail
view :: String -> String
view = id
Executing view tlist1 in GHCi gives tcons 1 $ tcons 2 $ tcons 3 $ tnil
With the definition above the type of the elements of the list is restricted to Int! I would like to have polymorphic lists and tried:
class PList repr a where
pnil :: repr a
pcons :: a -> repr a -> repr a
plist0 :: forall (repr :: * -> *) a. PList repr a => repr a
plist0 = pnil
plist1 :: PList repr Int => repr Int
plist1 = pcons 1 $ pcons 2 $ pcons 3 pnil
This compiles, but I'm unable to define the instance for the interpreter:
instance PList String where
pnil = "pnil"
pcons hd tail = "pcons " ++ show hd ++ " $ " ++ tail
This gives the error:
[1 of 1] Compiling TList ( List/List.hs, interpreted ) [Source file changed]
List/List.hs:30:10: error:
• Expecting one more argument to ‘PList String’
Expected a constraint,
but ‘PList String’ has kind ‘* -> Constraint’
• In the instance declaration for ‘PList String’
|
30 | instance PList String where
| ^^^^^^^^^^^^
List/List.hs:30:16: error:
• Expected kind ‘* -> *’, but ‘String’ has kind ‘*’
• In the first argument of ‘PList’, namely ‘String’
In the instance declaration for ‘PList String’
|
30 | instance PList String where
| ^^^^^^
So PList should have 2 type arguments! Changing to instance PList String Int where gives:
List/List.hs:30:16: error:
• Expected kind ‘* -> *’, but ‘String’ has kind ‘*’
• In the first argument of ‘PList’, namely ‘String’
In the instance declaration for ‘PList String Int’
|
30 | instance PList String Int where
| ^^^^^^
So I should have a type with kind (* -> *) like Functor or so, but I don't have it. How to fix?
It depends on whether or not you want to retain the type information of the "contained" element in the representation. You could certainly keep it, through a phantom argument
which has a suitable kind for the current class.
But I think it's more in the spirit of the original to not carry that information around. Then it doesn't make sense for
repr ato appear in the signatures. It doesn't need to, you can just have a plainreprthere. GHC may balk at you for having ambiguous types, but that's not a problem any longer with the right extension:and then you can define
...or more generally,
The examples will however need to take care of the ambiguity:
This is quite awkward, and also unnecessary because the
reprargument isn't actually ambiguous, rather theaone is. It's nicer when the type arguments are swapped:You wouldn't even need the
@Intapplications for thepconses if the number-literals where unambiguous, which they aren't in Haskell but could be made to be: