Haskell hobbyist here - is it possible to map a polymorphic constant over a list of types in a generic way?
More precisely, consider this snippet:
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
module Main where
import Data.Kind( Type )
class HasName a where name :: String
instance HasName Int where name = "Int"
instance HasName Double where name = "Double"
class AllNames (ts :: [Type]) where
allNames :: [String]
instance AllNames '[] where
allNames = []
instance (HasName t, AllNames rest) => AllNames (t ': rest) where
allNames = name @t : allNames @rest
main :: IO ()
main = print $ allNames @'[Int, Double]
Unsurprisingly, this works as expected, i.e., it prints ["Int","Double"]. However, if I try to generalize the pattern above so that the code can be used with any name-like polymorphic constant, then I run into problems.
Here's my attempt at generalization:
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
module Main where
import Data.Kind( Constraint, Type )
--
-- Intended to be the generalized version of 'name'
--
type PolymorphicConstant (c :: Type -> Constraint) (a :: Type) = forall t . c t => a
--
-- Intended to be the generalized version of 'AllNames'
--
class AllPolymorphicConstants (c :: Type -> Constraint) (ts :: [Type]) (a :: Type) where
allPolymorphicConstants :: PolymorphicConstant c a -> [ a ]
instance AllPolymorphicConstants c '[] a where
allPolymorphicConstants _ = []
instance (c t, AllPolymorphicConstants c rest a) => AllPolymorphicConstants c (t ': rest) a where
allPolymorphicConstants f = f @t : allPolymorphicConstants @c @rest @a f
Alas, this doesn't compile (btw, I'm using GHC 8.10.7):
Main.hs:31:74: error:
• Could not deduce: c t0 arising from a use of ‘f’
from the context: (c t, AllPolymorphicConstants c rest a)
bound by the instance declaration at Main.hs:30:10-91
or from: c t1
bound by a type expected by the context:
PolymorphicConstant c a
at Main.hs:31:74
• In the fourth argument of ‘allPolymorphicConstants’, namely ‘f’
In the second argument of ‘(:)’, namely
‘allPolymorphicConstants @c @rest @a f’
In the expression: f @t : allPolymorphicConstants @c @rest @a f
• Relevant bindings include
f :: PolymorphicConstant c a (bound at Main.hs:31:27)
allPolymorphicConstants :: PolymorphicConstant c a -> [a]
(bound at Main.hs:31:3)
|
31 | allPolymorphicConstants f = f @t : allPolymorphicConstants @c @rest @a f
| ^
I'm probably missing something basic that makes this sort of generalization impossible - but what is it exactly?