Pony语言学习(九)——泛型与模式匹配(终章)

一、泛型(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

兄弟们,再见咯!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值