将 class 关键字替换为 actor 会导致错误
Replacing class keyword with actor causes an error
这是我的代码:
class Eapproximator
var step : F64
new create(step' :F64) =>
step = step'
fun evaluate() :F64 =>
var total = F64(0)
var value = F64(1)
while total < 1 do
total = total + step
value = value + (value * step)
end
value
actor Main
new create(env: Env) =>
var e_approx = Eapproximator(0.00001)
var e_val = e_approx.evaluate()
env.out.print(e_val.string())
它运行良好并打印(如预期)2.7183。但是,如果我在 Eapproximator
定义中将 class
替换为 actor
,我会得到一堆错误:
Error:
/src/main/main.pony:18:34: receiver type is not a subtype of target type
var e_val = e_approx.evaluate()
^
Info:
/src/main/main.pony:18:17: receiver type: Eapproximator tag
var e_val = e_approx.evaluate()
^
/src/main/main.pony:6:3: target type: Eapproximator box
fun evaluate() :F64 =>
^
/src/main/main.pony:3:3: Eapproximator tag is not a subtype of Eapproxim
ator box: tag is not a subcap of box
new create(step' :F64) =>
^
Error:
/src/main/main.pony:19:19: cannot infer type of e_val
env.out.print(e_val.string())
我该怎么做才能解决这个问题?
Actor是Pony中的并发单位。这意味着同一节目中的许多不同演员可以同时 运行,包括您的 Main
和 Eapproximator
演员。现在如果一个 actor 的字段被多个 actor 同时修改会发生什么?由于并发程序在现代硬件上的工作方式,您最终很可能会得到一些垃圾值。这称为数据竞争,它是并发编程中许多错误的根源。 Pony 的目标之一是在编译时检测数据竞争,这个错误消息是编译器告诉你你正在尝试做的事情可能不安全。
让我们浏览一下该错误消息。
receiver type is not a subtype of target type
接收者类型为被调用对象的类型,这里e_approx
。目标类型是方法内部this
的类型,这里是Eapproximator.evaluate
。子类型意味着子类型的对象可以像超类型的对象一样使用。所以那部分告诉你 evaluate
由于类型不匹配而无法在 e_approx
上调用。
receiver type: Eapproximator tag
e_approx
是一个 Eapproximator tag
。 tag
对象既不能读也不能写。我稍后会详细说明为什么 e_approx
是 tag
。
target type: Eapproximator box
this
在 evaluate
里面是一个 Eapproximator box
。可以读取 box
对象,但不能写入。 this
是 box
因为 evaluate
被声明为 fun evaluate
,这隐含地意味着 fun box evaluate
(这意味着默认情况下,方法不能修改它们的接收者。)
Eapproximator tag is not a subtype of Eapproxim
ator box: tag is not a subcap of box
根据此错误消息,tag
对象不是 box
对象的子类型,这意味着 tag
不能像 box
。如果我们看看 tag
和 box
允许什么,这是合乎逻辑的。 box
比 tag
允许更多的东西:它可以被读取而 tag
不能。如果一个类型允许的东西比超类型少(或多),那么它只能是另一种类型的子类型。
那么为什么用 actor
替换 class
会使对象 tag
?这与我之前谈到的数据竞争问题有关。演员可以自由支配自己的领域。它可以读取它们并写入它们。由于参与者可以 运行 并发,因此必须拒绝他们访问彼此的字段,以避免与字段所有者发生数据竞争。类型系统中有一些东西可以做到这一点:tag
。一个 actor 只能将其他 actor 视为 tag
,因为读取或写入它们是不安全的。它可以对那些 tag
引用做的主要有用的事情是发送异步消息(通过调用 be
方法或行为),因为这既不是读也不是写。
当然,由于您没有在程序中对 Eapproximator
进行任何更改,因此您的具体情况是安全的。但是尝试禁止所有不安全的程序比尝试允许除此之外的所有安全程序要容易得多。
总而言之,除了将 Eapproximator
保持为 class 之外,您的程序并没有真正的修复方法。没有任何东西需要成为 Pony 程序中的演员。 Actor 是并发的单位,但这意味着它也是顺序的单位。需要顺序和同步的计算必须存在于单个参与者中。然后,您可以将这些计算分解为各种 classes 以获得良好的代码卫生。
这是我的代码:
class Eapproximator
var step : F64
new create(step' :F64) =>
step = step'
fun evaluate() :F64 =>
var total = F64(0)
var value = F64(1)
while total < 1 do
total = total + step
value = value + (value * step)
end
value
actor Main
new create(env: Env) =>
var e_approx = Eapproximator(0.00001)
var e_val = e_approx.evaluate()
env.out.print(e_val.string())
它运行良好并打印(如预期)2.7183。但是,如果我在 Eapproximator
定义中将 class
替换为 actor
,我会得到一堆错误:
Error:
/src/main/main.pony:18:34: receiver type is not a subtype of target type
var e_val = e_approx.evaluate()
^
Info:
/src/main/main.pony:18:17: receiver type: Eapproximator tag
var e_val = e_approx.evaluate()
^
/src/main/main.pony:6:3: target type: Eapproximator box
fun evaluate() :F64 =>
^
/src/main/main.pony:3:3: Eapproximator tag is not a subtype of Eapproxim
ator box: tag is not a subcap of box
new create(step' :F64) =>
^
Error:
/src/main/main.pony:19:19: cannot infer type of e_val
env.out.print(e_val.string())
我该怎么做才能解决这个问题?
Actor是Pony中的并发单位。这意味着同一节目中的许多不同演员可以同时 运行,包括您的 Main
和 Eapproximator
演员。现在如果一个 actor 的字段被多个 actor 同时修改会发生什么?由于并发程序在现代硬件上的工作方式,您最终很可能会得到一些垃圾值。这称为数据竞争,它是并发编程中许多错误的根源。 Pony 的目标之一是在编译时检测数据竞争,这个错误消息是编译器告诉你你正在尝试做的事情可能不安全。
让我们浏览一下该错误消息。
receiver type is not a subtype of target type
接收者类型为被调用对象的类型,这里e_approx
。目标类型是方法内部this
的类型,这里是Eapproximator.evaluate
。子类型意味着子类型的对象可以像超类型的对象一样使用。所以那部分告诉你 evaluate
由于类型不匹配而无法在 e_approx
上调用。
receiver type: Eapproximator tag
e_approx
是一个 Eapproximator tag
。 tag
对象既不能读也不能写。我稍后会详细说明为什么 e_approx
是 tag
。
target type: Eapproximator box
this
在 evaluate
里面是一个 Eapproximator box
。可以读取 box
对象,但不能写入。 this
是 box
因为 evaluate
被声明为 fun evaluate
,这隐含地意味着 fun box evaluate
(这意味着默认情况下,方法不能修改它们的接收者。)
Eapproximator tag is not a subtype of Eapproxim ator box: tag is not a subcap of box
根据此错误消息,tag
对象不是 box
对象的子类型,这意味着 tag
不能像 box
。如果我们看看 tag
和 box
允许什么,这是合乎逻辑的。 box
比 tag
允许更多的东西:它可以被读取而 tag
不能。如果一个类型允许的东西比超类型少(或多),那么它只能是另一种类型的子类型。
那么为什么用 actor
替换 class
会使对象 tag
?这与我之前谈到的数据竞争问题有关。演员可以自由支配自己的领域。它可以读取它们并写入它们。由于参与者可以 运行 并发,因此必须拒绝他们访问彼此的字段,以避免与字段所有者发生数据竞争。类型系统中有一些东西可以做到这一点:tag
。一个 actor 只能将其他 actor 视为 tag
,因为读取或写入它们是不安全的。它可以对那些 tag
引用做的主要有用的事情是发送异步消息(通过调用 be
方法或行为),因为这既不是读也不是写。
当然,由于您没有在程序中对 Eapproximator
进行任何更改,因此您的具体情况是安全的。但是尝试禁止所有不安全的程序比尝试允许除此之外的所有安全程序要容易得多。
总而言之,除了将 Eapproximator
保持为 class 之外,您的程序并没有真正的修复方法。没有任何东西需要成为 Pony 程序中的演员。 Actor 是并发的单位,但这意味着它也是顺序的单位。需要顺序和同步的计算必须存在于单个参与者中。然后,您可以将这些计算分解为各种 classes 以获得良好的代码卫生。