【问题标题】:Extensible records (I think)可扩展记录(我认为)
【发布时间】:2017-06-21 04:02:14
【问题描述】:

我大致想要的是这样的:

data A = ...
data B = ...
data C = ...

class HasA t where
  getA :: t -> A

class HasB t where
  getB :: t -> B

class HasC t where
  getC :: t -> C

所以我可以这样做(伪代码如下):

a :: A
b :: B

x = mkRecord { elemA a, elemB b }
y = mkRecord { elemB b, elemA a }

-- type of `x` == type of `y`

当然,只有适当的get 函数才能工作,在上述情况下是getAgetB

我也想要以下功能

slice :: Subset a b => a -> b
slice x = -- just remove the bits of x that aren't in type b.

add :: e -> a -> a ++ e
add e x = -- add an element to the "record" (compile error if it's already there)

我觉得这不是一个新问题,因此可能已经存在解决方案。请注意,我不要求解决方案是可扩展的,我需要处理的类型数量是有限且已知的,但当然可扩展的也不会受到影响。

我发现了几个似乎在我正在寻找的领域中的包,即HListextensible(也许可扩展更好,因为我希望我的记录无序)。我在 Hackage 文档中有点迷失,所以我只想要一些示例代码(或一些示例代码的链接),它们大致实现了我正在寻找的内容。

【问题讨论】:

    标签: haskell


    【解决方案1】:

    这正是HList 的优势所在。但是,由于我现在没有正确的设置来使用HList 包测试某些东西(此外,它还有more confusing data definitions),这里是HList 的一个最小示例,它使用singletons 作为类型级列表的东西。

    {-# LANGUAGE DataKinds, TypeOperators, GADTs,TypeFamilies, UndecidableInstances,
        PolyKinds,  FlexibleInstances, MultiParamTypeClasses
      #-}
    
    import Data.Singletons
    import Data.Promotion.Prelude.List
    
    data HList (l :: [*]) where
      HNil :: HList '[]
      HCons :: x -> HList xs -> HList (x ': xs)
    

    add 函数最简单:就是HCons

    add :: x -> HList xs -> HList (x ': xs)
    add = HCons 
    

    更有趣的是合并两条记录:

    -- Notice we are using `:++` from singletons
    combine :: HList xs -> HList ys -> HList (xs :++ ys)
    combine HNil xs = xs
    combine (x `HCons` xs) ys = x `HCons` (xs `combine` ys)
    

    现在,对于您的get 函数,您需要根据类型级别列表进行调度。为此,您需要一个重叠类型类。

    class Has x xs where
      get :: xs -> x
    
    instance {-# OVERLAPS #-} Has x (HList (x ': xs)) where
      get (x `HCons` _) = x
    
    instance Has x (HList xs) => Has x (HList (y ': xs)) where
      get (_ `HCons` xs) = get xs
    

    最后,我们可以使用Has 来定义一个类似的Subset 类。和之前的想法一样。

    class Subset ys xs where
      slice :: xs -> ys
    
    instance Subset (HList '[]) (HList xs) where
      slice _ = HNil
    
    instance (Get y (HList xs), Subset (HList ys) (HList xs)) =>
               Subset (HList (y ': ys)) (HList xs) where
      slice xs = get xs `HCons` slice xs
    

    正如您在括号中提到的,简单的 HList 表单并不能确保您只有 一个 任何类型的字段(因此 get 只返回第一个字段,忽略其余字段)。如果你想要唯一性,你可以在 HList 构造函数中添加一个约束。

    data Record (l :: [*]) where
      Nil :: Record '[]
      Cons :: (NotElem x xs ~ 'True) => x -> Record xs -> Record (x ': xs)
    

    但是,使用Record 定义Subset 看起来需要一些证明。 :)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-05-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-09
      相关资源
      最近更新 更多