在HList中查找元素

时间:2016-02-07 10:54:15

标签: haskell hlist

我正在尝试编写两个函数来从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

2 个答案:

答案 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

编写Containsextract'非常容易。但是,编写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相同的模式。