如何在类型级别使用可选字段来表示数据? [英] How can I represent data with optional fields at the type-level?
问题描述
我正在处理一个具有值(多态,可以是任何)的控制流的数据,而且可以具有验证器函数,该函数检查值是否仍然很好,以及 可以有一个刷新值的函数(用新值返回一个新数据)。
在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屋!