如何在类型级别使用可选字段来表示数据? [英] How can I represent data with optional fields at the type-level?

查看:90
本文介绍了如何在类型级别使用可选字段来表示数据?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在处理一个具有值(多态,可以是任何)的控制流的数据,而且可以具有验证器函数,该函数检查值是否仍然很好,以及 可以有一个刷新值的函数(用新值返回一个新数据)。

在vanilla Haskell中,它看起来像this:

  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 来完成,但我遇到了构造函数的问题。只用于可刷新我需要有两个构造函数,一个用于可刷新数据,另一个用于不可刷新数据。有了validatable,我需要4个构造函数! (用于可刷新和验证,用于不可刷新和验证,用于验证和不可刷新,以及用于不可刷新和不验证)。想象一下如果我以后需要另一个可选字段。更糟的是:几乎所有的字段都是一样的,除了那些正在改变的字段,所以有太多的重复。






我也试着用typeclasses / type-families来修改这个情况。

例如, MyData'Refreshable'NotValidatable 就变成了可刷新数据=> data ,我可以实例 MyData 或者只删除它以获取更多特定数据,这些数据可以是实例。



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



< hr />

这可能是一个XY问题;我认为一个更清晰的方法是使数据类型像 Refreshable a Validatable a ,并将它们写入 MyData 但我不知道该怎么做。我不能包装它们,因为订单会改变一切。



有没有一种干净的方法可以做到这一点?或者我应该坚持4个构造函数?或者也许Haskell尚未准备好这种类型的东西呢? (没有双关语意图:P)。

解决方案

会这样满足您的要求吗?

  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)}

- - 例子
类型FullData a =数据身份标识a
类型EmptyData a = Data(Const())(Const())a
类型ValidableData a = Data Identity(Const())a

需要一些打包/解包(对于 Identity )。
$ b

可以定义助记符别名 type Present = Identity type Missing = $($ $ $ $ $ $ $ $
$ b

)或者,



  data MyData(v :: Opt)(r :: Opt)a = MyData 
{value :: a
,validator :: Validator v a
,复习::复习r a}

数据选项=是|否

类型验证器(o :: Opt)a其中
验证器是=(a - > Bool)
验证器否=()
- etc 。

- 示例
类型FullData a =数据是是a
类型EmptyData a =数据否否a
类型ValidableData a =数据是否a


I'm working on a data for control flow that has a value (polymorphic, can be whatever), and it also could have a validator function that checks if the value is still good, and could have a function that "refreshes the value" (returns a new data with a new value).

In vanilla Haskell it can look like this:

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

What I actually want are these types:

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

I've done just that but only with Refreshable. I want to do it with Validatable too but I'm having a problem with constructors. Just for Refreshable I need to have two constructors, one for refreshable data and another for non-refreshable data. With validatable, I'll need to have 4 constructors! (for refreshable and validatable, for non-refreshable and validatable, for validatable and non-refreshable, and for non-refreshable and non-validatable). And imagine if I'll need another optional field later. Even worse: almost all the fields are the same except for the ones that are changing so there's so much duplication.


I've also tried to amend the situation with typeclasses / type-families.
For example, MyData 'Refreshable 'NotValidatable just becomes Refreshable data => data and I could instance MyData or just remove it for more specific-data that can be an instance.

This is also problematic, because they're not really fields anymore; i.e. I can't take a data without a validator and change it to the same data with a validator (not at the type-level).


This is probably an XY problem; I think a cleaner approach would be to make data types like Refreshable a and Validatable a and compose them in MyData but I don't know how to do that. I can't wrap them because order would change everything.

Is there a clean way to do this? or should I just stick with the 4 constructors? or maybe Haskell isn't ready for this type of things yet? (no pun intended :P).

解决方案

Would something like this satisfy your requirements?

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

Some wrapping/unwrapping required (for Identity).

It is possible to define mnemonic aliases type Present = Identity and type Missing = Const (), with a few extensions on.


Alternatively,

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

这篇关于如何在类型级别使用可选字段来表示数据?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆