一、泛型(Generics):
(一)、泛型与引用能力(Generics and Reference Capabilities):
!掌握泛型基础语法:[TypeName: ClassName ReferenceCapability]
class Foo[T: Any val]
如果不指明引用能力,编译器便不会在引用能力上限制:
class Foo[A: Any]
如果你想对任意类型适用泛型,甚至可以直接略去类型注记:
class Foo[A]
现在启动你的开发环境,试试下面的代码会不会运行,结果如何?
class Foo[A]
var _c: A
new create(c: A) =>
_c = c
fun get(): A => _c
fun ref set(c: A) => _c = c
actor Main
new create(env:Env) =>
let a = Foo[U32](42)
env.out.print(a.get().string())
a.set(21)
env.out.print(a.get().string())
不幸的是,编译器不会通过上述代码。因为如果要编译一个泛型类,它必须是对所有满足条件的可能类型及引用能力都是可编译的。这样的话,上述代码描述了一个对任意引用能力的任意类型。这样的类对val引用能力的运行结果你已经看到了,那么对ref来说是怎样的呢?
class Foo
var _c: String ref
new create(c: String ref) =>
_c = c
fun get(): String ref => _c
fun ref set(c: String ref) => _c = c
actor Main
new create(env:Env) =>
let a = Foo(recover ref String end)
env.out.print(a.get().string())
a.set(recover ref String end)
env.out.print(a.get().string())
事实上,这也不会编译。编译器抱怨说,get()并不返回String ref而是this->String ref。我们显然需要改变类型注记来修复这个bug,但会发生什么呢?
class Foo
var _c: String ref
new create(c: String ref) =>
_c = c
fun get(): this->String ref => _c
fun ref set(c: String ref) => _c = c
actor Main
new create(env:Env) =>
let a = Foo(recover ref String end)
env.out.print(a.get().string())
a.set(recover ref String end)
env.out.print(a.get().string())
这下代码就合法了。
一个iso特制类:
class Foo
var _c: String iso
new create(c: String iso) =>
_c = c
fun get(): this->String iso => _c
fun ref set(c: String iso) => _c = c
actor Main
new create(env:Env) =>
let a = Foo(recover iso String end)
env.out.print(a.get().string())
a.set(recover iso String end)
env.out.print(a.get().string())
编译器抱怨说:
main.pony:5:8: right side must be a subtype of left side
_c = c
^
Info:
main.pony:4:17: String iso! is not a subtype of String iso: iso! is not a subtype of iso
new create(c: String iso) =>
^
这里的叹号意思是它是某一个存在的iso变量的假名。我们试图把c作为一个iso赋值给_c,这样的话就对同一对象创建了两个假名,违背了iso的原则。还记得吗,我们要使用consume语句进行iso的赋值,所以应该这样写构造器:
new create(c: String iso) =>
_c = consume c
set方法也应该改进改进:
fun set(c: String iso) => _c = consume c
但是要注意一点,在get方法处,由于应用了视角适应,我们得到了ref -> iso,多亏了接收方自动复原,我们可以这样调用一个方法:
class Foo
var _c: String iso
new create(c: String iso) =>
_c = consume c
fun get(): this->String iso => _c
fun ref set(c: String iso) => _c = consume c
actor Main
new create(env:Env) =>
let a = Foo(recover iso String end)
env.out.print(a.get().string())
a.set(recover iso String end)
env.out.print(a.get().string())
(二)一个真正的泛型类:
只要能对iso成立,说明这个类对其他任意引用能力都成立:
class Foo[A]
var _c: A
new create(c: A) =>
_c = consume c
fun get(): this->A => _c
fun ref set(c: A) => _c = consume c
actor Main
new create(env:Env) =>
let a = Foo[String iso]("Hello".clone())
env.out.print(a.get().string())
let b = Foo[String ref](recover ref "World".clone() end)
env.out.print(b.get().string())
let c = Foo[U8](42)
env.out.print(c.get().string())
阅读任务:限制条件
二、模式匹配(Pattern Matching):
(一)Match表达式:
我们在控制流那节碰到了万物皆表达式的概念,其实那里有match的相关应用,只不过为了顺序我放到了这里介绍。
match更像是switch的强化版本,实现了许多C/C++程序员的夙愿——switch匹配的范围从整数扩展到了任意类型:
match x
| 2 => "int"
| 2.0 => "float"
| "2" => "string"
else
"something else"
end
时刻记住它是一个表达式,不要简单地当成case块,那样会严重限制你的思路。
为了支持任意类型,你需要为你的自定义类添加一个eq方法:
class Foo
var _x: U32
new create(x: U32) =>
_x = x
fun eq(that: Foo): Bool =>
_x == that._x
actor Main
fun f(x: Foo): String =>
match x
| Foo(1) => "one"
| Foo(2) => "two"
| Foo(3) => "three"
| Foo(5) => "not four"
else
"something else"
end
对类型和值进行匹配:
fun f(x: (U32 | String | None)): String =>
match x
| None => "none"
| 2 => "two"
| 3 => "three"
| "5" => "not four"
else
"something else"
end
捕获(Capture):
fun f(x: (U32 | String | None)): String =>
match x
| None => "none"
| 2 => "two"
| 3 => "three"
| let u: U32 => "other integer"
| let s: String => s
end
解构元组:
fun f(x: (String | None), y: U32): String =>
match (x, y)
| (None, _) => "none"
| (let s: String, 2) => s + " two"
| (let s: String, 3) => s + " three"
| (let s: String, _) => s + " other integer"
else
"something else"
end
卫语句:
卫语句实际上是Erlang里的概念,用来高效地进行模式匹配。它和if关键字同时引进(where关键字则是在0.2.1):
fun f(x: (String | None), y: U32): String =>
match (x, y)
| (None, _) => "none"
| (let s: String, 2) => s + " two"
| (let s: String, 3) => s + " three"
| (let s: String, let u: U32) if u > 14 => s + " other big integer"
| (let s: String, _) => s + " other small integer"
else
"something else"
end
隐式匹配带引用能力注记的组合类型成员(Implicit matching on capabilities in the context of union types):
例如我们需要在(A iso | B ref | None)中匹配_x,假设匹配到了A类型,那么_x必须要是A iso:
class A
fun ref sendable() =>
None
class B
fun ref update() =>
None
actor Main
var _x: (A iso | B ref | None)
new create(env: Env) =>
_x = None
be f(a': A iso) =>
match (_x = None) // type of this expression: (A iso^ | B ref | None)
| let a: A iso => f(consume a)
| let b: B ref => b.update()
end
(模式匹配这块内容推荐学习swift和erlang中的相关部分)
阅读任务:As操作符(As语法我们在数组那里涉及了一点点,内容并不多)
写在后面的(这次真的是最后了):
官网教程还有很多知识点并没有涉及,但我们已经把基础语法吞了一个遍了。我比较推荐读者认知学习一下C FFI技术。在Where Next里,官方列出了几个可以的发展方向。当然,俺还是要继续翻译Pony Patterns的,感兴趣的同学可以加我QQ:762420244。回头有时间写一篇补记,好好唠唠C FFI
兄弟们,再见咯!