【发布时间】:2018-07-19 12:14:47
【问题描述】:
我一直在尝试在 Purescript 中开发一个组件系统,使用指定 eval 函数的 Component 类型类。 eval 函数可以由组件为组件的每个子组件递归调用,实质上是获取输入的值。
由于组件可能希望使用运行时值,因此也会将一条记录传递给 eval。我的目标是要求顶级 eval 的 Record 参数中的行包含每个子组件的所有行。这对于本身不使用任何行的组件来说并不太难,但是它们的单个子组件会这样做,因为我们可以简单地将子组件行传递给组件的行。这显示在evalIncrement。
import Prelude ((+), one)
import Data.Symbol (class IsSymbol, SProxy(..))
import Record (get)
import Prim.Row (class Cons, class Union)
class Component a b c | a -> b where
eval :: a -> Record c -> b
data Const a = Const a
instance evalConst :: Component (Const a) a r where
eval (Const v) r = v
data Var (a::Symbol) (b::Type) = Var
instance evalVar ::
( IsSymbol a
, Cons a b r' r) => Component (Var a b) b r where
eval _ r = get (SProxy :: SProxy a) r
data Inc a = Inc a
instance evalInc ::
( Component a Int r
) => Component (Inc a) Int r where
eval (Inc a) r = (eval a r) + one
以上所有代码都可以正常工作。但是,一旦我尝试引入一个包含多个输入组件并合并它们的行的组件,我似乎无法让它工作。例如,当尝试使用来自Prim.Row 的class Union 时:
data Add a b = Add a b
instance evalAdd ::
( Component a Int r1
, Component b Int r2
, Union r1 r2 r3
) => Component (Add a b) Int r3 where
eval (Add a b) r = (eval a r) + (eval b r)
产生以下错误:
No type class instance was found for
Processor.Component a3
Int
r35
while applying a function eval
of type Component t0 t1 t2 => t0 -> { | t2 } -> t1
to argument a
while inferring the type of eval a
in value declaration evalAdd
where a3 is a rigid type variable
r35 is a rigid type variable
t0 is an unknown type
t1 is an unknown type
t2 is an unknown type
事实上,即使修改evalInc 实例以使用具有空行的虚拟联合也会产生类似的错误,如下所示:
instance evalInc :: (Component a Int r, Union r () r1)
=> Component (Increment a) Int r1 where
我是否错误地使用了 Union?还是我的课程需要更多的功能依赖 - 我不太了解它们。
我正在使用 purs 版本 0.12.0
【问题讨论】:
-
在
evalAdd.eval的定义中,您有r :: Record r3,但调用eval a r需要r :: Record r1,同样,调用eval b r需要r :: Record r2。所以如果它是一个普通函数,你会得到类型不匹配,但是由于eval被重载,编译器只是说a+r3或b+r3没有重载。 -
@FyodorSoikin 对于 evalAdd.eval 中的组件每次使用 eval,我是否必须构建一个仅包含组件所需行的新记录?我可以不使用开放行吗?只要 r1 是 r3 的子集,那么传递 r3 就可以了?
-
@JosephYoung 真的有必要使用一行来存储您的值吗?您可以使用
Map,其键为Strings,代表Symbols(类似于reflectSymbol),其值的类型为Value。Value可以为您要存储的每种类型使用不同的构造函数。我知道你会失去多态性,但你会节省类型级别的体操......或者你可以看看 purescript-variant 包是否满足你的需求。 -
@KartikSabharwal 我实际上已经使用原始方法解决了我的问题,但还没有时间形成正确的答案。我有一段时间没有从事这个项目了,但据我记得,我没有对两个输入的组件使用联合,而是对每个输入使用相同的变量。所以本质上用
r替换r1 r2 r3并删除联合约束。这达到了要求组件在类型级别的编译时需要其输入所需的所有变量的效果 -
@JosephYoung 感谢分享您的解决方案!如果您最终可以将其作为答案发布,那就太好了,因为这种情况是使用类型类的巧妙练习。
标签: typeclass purescript row-polymorphism