我正在处理具有值(多态,可以是其他)的控制流的数据,并且还可以具有验证值是否仍然良好的验证器函数,并且可以具有“刷新值”的功能(使用新值返回新数据)。
在vanilla 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
,或者只删除它以获取更多可以作为实例的特定数据。
这也有问题,因为它们不再是真正的领域了;即,我不能在没有验证器的情况下获取数据,并使用验证器将其更改为相同的数据(不在类型级别)。
这可能是一个XY问题;我认为更简洁的做法是制作Refreshable a
和Validatable a
等数据类型,并在MyData
中构建它们,但我不知道如何做到这一点。我无法包装它们,因为订单会改变一切。
有干净的方法吗?或者我应该坚持使用4个构造函数?或者Haskell还没准备好做这类事情呢? (没有双关语:P)。
答案 0 :(得分:6)
这样的事情会满足你的要求吗?
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 = Identity
和type 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
答案 1 :(得分: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)
...
}