【问题标题】:How can I represent data with optional fields at the type-level?如何在类型级别使用可选字段表示数据?
【发布时间】:2016-07-06 16:37:25
【问题描述】:

我正在处理具有值(多态,可以是任何值)的控制流数据,并且它还 可以具有一个验证器函数来检查该值是否仍然是好的,并且可以具有“刷新值”的功能(返回具有新值的新数据)。

在原版 Haskell 中,它看起来像这样:

data MyData a = MyData
  {value :: a
  ,validator :: Maybe (a -> Bool)
  ,refresher :: Maybe (MyData a -> MyData a)}

我真正想要的是这些类型:

data Refreshable = Refreshable | NotRefreshable
data Validatable = Validatable | NotValidatable
MyData (r :: Refreshable) (v :: Validatable)

我已经做到了,但仅限于 Refreshable。我也想用Validatable 来做,但是构造函数有问题。仅对于Refreshable,我需要有两个构造函数,一个用于可刷新数据,另一个用于不可刷新数据。使用可验证的,我需要有 4 个构造函数! (对于可刷新和可验证,对于不可刷新和可验证,对于可验证和不可刷新,以及对于不可刷新和不可验证)。想象一下我以后是否需要另一个可选字段。更糟糕的是:几乎所有字段都是相同的,除了那些正在变化的字段,所以有很多重复。


我还尝试使用类型类/类型族来修正这种情况。
例如,MyData 'Refreshable 'NotValidatable 只是变成了Refreshable data => data,我可以实例化MyData,或者只是删除它以获得可以作为实例的更具体的数据。

这也是有问题的,因为它们不再是真正的字段;即,我不能在没有验证器的情况下获取数据并将其更改为相同的数据 with 验证器(不在类型级别)。


这可能是一个 XY 问题;我认为更简洁的方法是制作Refreshable aValidatable a 之类的数据类型并将它们组合在MyData 中,但我不知道该怎么做。我不能把它们包装起来,因为顺序会改变一切。

有没有一种干净的方法可以做到这一点?还是我应该坚持使用 4 个构造函数?或者 Haskell 可能还没有为这类事情做好准备? (没有双关语:P)。

【问题讨论】:

    标签: haskell


    【解决方案1】:

    这样的东西能满足你的要求吗?

    import Control.Applicative (Const)
    import Data.Functor.Identity
    
    data MyData kv kr a = MyData
      {value :: a
      ,validator :: kv (a -> Bool)
      ,refresher :: kr (MyData a -> MyData a)}
    
    -- examples
    type FullData a      = Data Identity   Identity   a
    type EmptyData a     = Data (Const ()) (Const ()) a
    type ValidableData a = Data Identity   (Const ()) a
    

    需要一些包装/展开(对于Identity)。

    可以定义助记别名type Present = Identitytype Missing = Const (),加上一些扩展名。


    或者,

    data MyData (v :: Opt) (r :: Opt) a = MyData
      {value :: a
      ,validator :: Validator v a
      ,refresher :: Refresher r a}
    
    data Opt = Yes | No
    
    type family Validator (o :: Opt) a where
        Validator Yes = (a -> Bool)
        Validator No  = ()
    -- etc.
    
    -- examples
    type FullData a      = Data Yes Yes a
    type EmptyData a     = Data No  No  a
    type ValidableData a = Data Yes No  a
    

    【讨论】:

      【解决方案2】:

      我最终得到了 chi 的答案的变体:

      data Opt = Yes | No
      
      type family TOpt (o :: Opt) a where
        TOpt Yes a = a
        TOpt No a = ()
      

      然后是这样的:

      data MyData (v :: Opt) a = MyData
        {val :: a
        ,validator :: TOpt v (MyData 'Yes a -> Bool)
        ...
        }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-02-28
        • 2020-02-17
        • 2013-04-18
        • 1970-01-01
        • 2012-06-04
        • 1970-01-01
        • 1970-01-01
        • 2017-04-04
        相关资源
        最近更新 更多