我有一个简单的异类List
data HList (ts :: [Type]) where
HNil :: HList '[]
(:&:) :: t -> HList ts -> HList (t ': ts)
infixr 5 :&:
我正在尝试定义异构地图:
type family ToDomain (xs :: [Type]) :: [Type] where
ToDomain '[] = '[];
ToDomain ((a -> b) ': r) = a ': ToDomain r
type family ToCoDomain (xs :: [Type]) :: [Type] where
ToCoDomain '[] = '[];
ToCoDomain ((a -> b) ': r) = b ': ToCoDomain r
mapH :: forall mappings input output.
(input ~ ToDomain mappings, output ~ ToCoDomain mappings)
=> HList mappings -> HList input -> HList output
mapH HNil HNil = HNil
mapH (f :&: maps) (x :&: ins) = (f x) :&: mapH maps ins
这不会编译(如果这实际上有效,我会感到非常惊讶),因为GHC无法推断递归的正确性。我不确定如何说服GHC的正确性。
答案 0 :(得分:2)
您需要证明mappings
内部只有函数。这可以通过额外的GADT进行。
data OnlyFunctions (ts :: [*]) where
FNil :: OnlyFunctions '[]
FCons :: OnlyFunctions ts -> OnlyFunctions ((a->b) ': ts)
mapH :: forall mappings input output.
(input ~ ToDomain mappings, output ~ ToCoDomain mappings)
=> OnlyFunctions mappings -> HList mappings -> HList input -> HList output
mapH FNil HNil HNil = HNil
mapH (FCons h) (f :&: maps) (x :&: ins) = (f x) :&: mapH h maps ins
问题是ToDomain '[Int,Bool]
是有效类型 - 即使ToDomain
在这种情况下不简化。因此,GHC不能假设mappings
类型列表仅由函数组成。
如果需要,您可以通过自定义类型类删除其他参数。
class OF (ts :: [*]) where
onlyFun :: OnlyFunctions ts
instance OF '[] where
onlyFun = FNil
instance OF ts => OF ((a -> b) ': ts) where
onlyFun = FCons onlyFun
mapHsimple :: forall mappings input output.
(input ~ ToDomain mappings, output ~ ToCoDomain mappings, OF mappings)
=> HList mappings -> HList input -> HList output
mapHsimple = mapH onlyFun
答案 1 :(得分:2)
如果你想进行地图操作,你几乎肯定会更好地定义
data Rec (f :: k -> *) (ts :: [k]) where
RNil :: Rec f '[]
(:&:) :: f t -> Rec f ts -> Rec f (t ': ts)
infixr 5 :&:
现在你可以定义一般的
rmap :: (forall a. f a -> g a) -> Rec f ts -> Rec g ts
和
rzipWith :: (forall a. f a -> g a -> h a) -> Rec f ts -> Rec g ts -> Rec h ts
这通常会让事情变得更加容易。在这种情况下,您可以使用
data Fun :: * -> * where
Fun :: (a -> b) -> Fun (a -> b)
data Dom :: * -> * where
Dom :: a -> Dom (a -> b)
data Cod :: * -> * where
Cod :: b -> Cod (a -> b)
现在你可以写
了zoop :: Rec Fun fs -> Rec Dom fs -> Rec Cod fs
zoop = rzipWith (\(Fun f) (Dom x) -> Cod (f x))
事实上,其中一些是轻微的过度杀伤;你应该能够使用包裹Dom
类型系列的newtype来逃脱。您甚至可以为Cod
执行此操作,但这可能会降低结果的可用性。