我正在尝试编写两个函数来从HList
中提取值,但我似乎无法让GHC满意。
第一个函数将具有签名extract :: HList a -> [b]
,它从列表中提取b
类型的所有元素。我只是通过询问a
中的类型Typeable
个实例来成功编写它。
class OfType a b where
oftype :: a -> [Maybe b]
instance OfType (HList '[]) b where
oftype = const []
instance (Typeable t, Typeable b, OfType (HList ts) b) => OfType (HList (t ': ts)) b where
oftype (x :- xs) = (cast x :: Maybe b) : oftype xs
extract :: OfType a b => a -> [b]
extract = catMaybes . oftype
这是次优的,因为人们并不需要Typeable
约束来编写任何提取实例。
我试图在约束中使用类型等式和不等式,但这只给了我重叠的实例。
我正在尝试编写的第二个函数将具有签名extract' :: Contains h n => HList h -> n
,它在列表中提取类型n
的第一个元素,并且上下文表明该列表实际上包含该类型的一个元素
是否可以在没有extract
约束的情况下编写Typeable
?
是否可以在没有extract'
约束的情况下编写Typeable
?
如何写Contains
?
答案 0 :(得分:3)
由于你想在编译时检查类型是否相等,我相信重叠的实例是不可避免的(我不喜欢那些......)。
另外,我并非100%确定我的重叠pragma是正确的。
{-# LANGUAGE DataKinds, TypeOperators, ScopedTypeVariables,
MultiParamTypeClasses, FlexibleInstances, FlexibleContexts #-}
{-# OPTIONS -Wall #-}
module HListFilter where
import Data.HList.HList
class OfType a b where
oftype :: a -> [b]
instance OfType (HList '[]) b where
oftype = const []
instance {-# OVERLAPS #-} (OfType (HList ts) t) => OfType (HList (t ': ts)) t where
oftype (HCons x xs) = x : oftype xs
instance {-# OVERLAPPABLE #-} (OfType (HList ts) b) => OfType (HList (t ': ts)) b where
oftype (HCons _ xs) = oftype xs
test :: HList '[Int, Char, [Char], Char, Bool]
test = HCons (1::Int) (HCons 'a' (HCons "foo" (HCons 'b' (HCons True HNil))))
test_result :: [Char]
test_result = oftype test -- "ab"
答案 1 :(得分:3)
AndrásKovács提到了一种家庭式的方法。这是一种方法:
type family Equal (x :: *) (y :: *) where
Equal x x = 'True
Equal x y = 'False
type family Check (b :: *) (as :: [*]) :: [Bool] where
Check b '[] = '[]
Check b (a ': as) = (b `Equal` a) ': Check b as
class ps ~ Check b as =>
OfType (ps :: [Bool]) (as :: [*]) b where
extract :: HList as -> [b]
ps ~ Check b as
超类上下文在这里至关重要,作为时间问题。 GHC总是在检查实例约束之前提交一个实例,但它甚至在解决超类约束之后才尝试查找实例。因此,我们需要使用超类约束来修复要选择的实例。
instance OfType '[] '[] b where
extract HNil = []
instance (OfType ps as b, a ~ b) =>
OfType ('True ': ps) (a ': as) b where
extract (HCons x xs) = x : extract xs
instance (OfType ps as b, Equal b a ~ 'False) =>
OfType ('False ': ps) (a ': as) b where
extract (HCons _ xs) = extract xs
完成此操作后,您实际上可以编写一个避免“额外”类参数的接口:
class OfType' (as :: [*]) (b :: *) where
extract' :: HList as -> [b]
instance OfType ps as b => OfType' as b where
extract' = extract
编写Contains
和extract'
非常容易。但是,编写Contains
的良好实例需要与OfType
完全相同的跳环。你想要的课程是:
class Contains xs y where
contains :: y `Elem` xs
-- Elem is part of the dependently typed folklore.
data Elem y xs where
Here :: Elem y (y ': xs)
There :: Elem y xs -> Elem y (x ': xs)
但是编写实例会再次迫使您进入重叠或封闭类型的系列。我知道我已经把这段代码写在了SO附近,但你应该能够很容易地计算出重叠版本;类型系列版本通常采用与OfType
相同的模式。