我正在研究控制流的数据,它有一个值(多态,可以是其他),它也可以有一个验证器函数来检查值是否仍然很好,并且可以有一个"刷新值"的函数(返回带有新值的新数据).
在vanilla Haskell中,它看起来像这样:
data MyData a = MyData
{value :: a
,validator :: Maybe (a -> Bool)
,refresher :: Maybe (MyData a -> MyData a)}
Run Code Online (Sandbox Code Playgroud)
我真正想要的是这些类型:
data Refreshable = Refreshable | NotRefreshable
data Validatable = Validatable | NotValidatable
MyData (r :: Refreshable) (v :: Validatable)
Run Code Online (Sandbox Code Playgroud)
我已经做到了,但只有Refreshable.我也想这样做,Validatable但我遇到了构造函数的问题.仅仅因为Refreshable我需要有两个构造函数,一个用于可刷新数据,另一个用于不可刷新数据.有了可验证的,我需要有4个构造函数!(用于可刷新和可验证,不可刷新和可验证,可验证和不可刷新,以及不可刷新和不可验证的).想象一下,如果我以后需要另一个可选字段.更糟糕的是:几乎所有的领域都是相同的,除了那些正在改变的领域,因此有太多的重复.
我也尝试用类型类/类型系来修改这种情况.
例如,MyData 'Refreshable 'NotValidatable只是成为Refreshable data => data,我可以实例MyData或只是删除它可以作为一个实例的更具体的数据.
这也是有问题的,因为它们不再是真正的领域; 即我没有验证器就无法获取数据,并使用验证器(不是类型级别)将其更改为相同的数据.
这可能是一个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)}
-- examples
type FullData a = Data Identity Identity a
type EmptyData a = Data (Const ()) (Const ()) a
type ValidableData a = Data Identity (Const ()) a
Run Code Online (Sandbox Code Playgroud)
需要一些包装/展开(for 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
Run Code Online (Sandbox Code Playgroud)