我一直在尝试使用Purescript开发组件系统,使用一个Component类型类来指定eval函数。每个子组件都可以通过调用组件的eval函数进行递归调用,从而获取输入的值。
由于组件可能希望使用运行时值,因此还传递了一个记录到eval中。我的目标是要求顶层eval的Record参数中的行包括每个子组件的所有行。对于不使用任何行的组件来说,这并不太困难,但是它们的单个子组件确实使用了行,因此我们可以将子组件的行简单地传递给组件。这在evalIncrement中展示了出来。
以下错误信息被产生:
实际上,即使修改
由于组件可能希望使用运行时值,因此还传递了一个记录到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
实例以使用一个带有空行的虚拟Union也会产生类似的错误,如下所示: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
。 - Fyodor SoikinMap
,其键是表示Symbol
s(类似于reflectSymbol
)的String
,其值具有类型Value
。Value
可以为您要存储的每种类型具有不同的构造函数。 我知道您将失去多态性,但您将在类型层面上节省费力... 或者,您可以查看 purescript-variant 包是否满足您的需求。 - Kartik Sabharwalr
替换了r1 r2 r3
,并移除了联合约束。这实现了在编译时需要输入所需变量的所有组件的效果。 - Joseph Young