首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >行的Purescript联合

行的Purescript联合
EN

Stack Overflow用户
提问于 2018-07-19 20:14:47
回答 2查看 649关注 0票数 6

我一直在尝试用Purescript开发一个组件系统,使用一个指定eval函数的组件类型类。的eval函数可以由组件为组件的每个子组件递归调用,本质上是获取输入的值。

由于组件可能希望使用运行时值,因此也会将记录传递到eval中。我的目标是要求顶级eval的记录参数中的行包含每个子组件的所有行。对于那些本身不使用任何行的组件来说,这并不是太困难,但是它们的单个子组件使用,因为我们可以简单地将子组件的行传递给组件的行。这在evalIncrement中显示。

代码语言:javascript
复制
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

代码语言:javascript
复制
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)

会产生以下错误:

代码语言:javascript
复制
  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,也会产生类似的错误,如下所示:

代码语言:javascript
复制
instance evalInc :: (Component a Int r, Union r () r1) 
                       => Component (Increment a) Int r1 where

我是否错误地使用了Union?或者我的类需要更多的函数依赖--我不是很好的理解它们。

我使用的是purs版本0.12.0

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-04-05 16:03:54

因为Var的实例已经是多态的(或者在技术上是开放的?)由于使用了Row.Cons,即

代码语言:javascript
复制
eval (Var :: Var "a" Int) :: forall r. { "a" :: Int | r } -> Int

然后我们所要做的就是对左边和右边的求值使用相同的记录,类型系统可以推断出两者的组合,而不需要联合:

代码语言:javascript
复制
instance evalAdd :: 
  ( Component a Int r
  , Component b Int r
  ) => Component (Add a b) Int r where
  eval (Add a b) r = (eval a r) + (eval b r)

当不使用类型类时,这一点更加明显:

代码语言:javascript
复制
> f r = r.foo :: Int
> g r = r.bar :: Int
> :t f
forall r. { foo :: Int | r } -> Int
> :t g
forall r. { bar :: Int | r } -> Int
> fg r = (f r) + (g r)
> :t fg
forall r. { foo :: Int, bar :: Int | r } -> Int

我认为与@erisco相比,这种方法的缺点是打开的行必须在Var等实例的定义中,而不是在eval的定义中?它也不是强制的,所以如果一个组件不使用打开的行,那么像Add这样的组合子就不再起作用。

好处是缺少对RProxies的要求,除非eriscos实现实际上不需要它们,我还没有检查过。

更新:

我想出了一种要求关闭eval实例的方法,但它使用了purescript-record-extra中的pick,这让它变得非常丑陋。

我真的不确定为什么这会比上面的选项更好,感觉我只是重新实现了行多态性

代码语言:javascript
复制
import Record.Extra (pick, class Keys)

...

instance evalVar :: 
  ( IsSymbol a
  , Row.Cons a b () r
  ) => Component (Var a b) b r where
  eval _ r = R.get (SProxy :: SProxy a) r

data Add a b = Add a b

evalp :: forall c b r r_sub r_sub_rl trash
   . Component c b r_sub
  => Row.Union r_sub trash r
  => RL.RowToList r_sub r_sub_rl
  => Keys r_sub_rl
  => c -> Record r -> b
evalp c r = eval c (pick r)

instance evalAdd :: 
  ( Component a Int r_a
  , Component b Int r_b
  , Row.Union r_a r_b r
  , Row.Nub r r_nub
  , Row.Union r_a trash_a r_nub
  , Row.Union r_b trash_b r_nub
  , RL.RowToList r_a r_a_rl
  , RL.RowToList r_b r_b_rl
  , Keys r_a_rl
  , Keys r_b_rl
  ) => Component (Add a b) Int r_nub where
  eval (Add a b) r = (evalp a r) + (evalp b r)

eval (Add (Var :: Var "a" Int) (Var :: Var "b" Int) ) :: { a :: Int , b :: Int } -> Int  
eval (Add (Var :: Var "a" Int) (Var :: Var "a" Int) ) :: { a :: Int } -> Int 
票数 1
EN

Stack Overflow用户

发布于 2019-04-05 08:31:15

r ∷ r3,但它用于需要r1r2的地方,因此存在类型不匹配。不能在应为{a ∷ A}{b ∷ B}{}的位置提供记录{a ∷ A, b ∷ B}。然而,人们可以这样说:

代码语言:javascript
复制
f ∷ ∀ s r. Row.Cons "a" A s r ⇒ Record r → A
f {a} = a

换句话说,对于任何包含类型为A的标签"a"的记录,f都是一个多态函数。同样,您可以将eval更改为:

代码语言:javascript
复制
eval ∷ ∀ s r. Row.Union c s r ⇒ a → Record r → b

换句话说,对于任何至少包含c字段的记录,eval都是多态的。这引入了类型多义性,您必须使用代理来解决此问题。

代码语言:javascript
复制
eval ∷ ∀ proxy s r. Row.Union c s r ⇒ proxy c → a → Record r → b

Add的eval实例变为:

代码语言:javascript
复制
instance evalAdd ∷
  ( Component a Int r1
  , Component b Int r2
  , Union r1 s1 r3
  , Union r2 s2 r3
  ) => Component (Add a b) Int r3 where
  eval _ (Add a b) r = eval (RProxy ∷ RProxy r1) a r + eval (RProxy ∷ RProxy r2) b r

从这里开始,r1r2就变得模棱两可,因为它们不是由r3单独确定的。对于给定的约束,还必须知道s1s2。可能有一个你可以添加的函数依赖。我不确定什么是合适的,因为我不确定您正在设计的程序的目标是什么。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51422462

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档