语言漫游
一个程序,从上到下读完,每个小节几行代码就把事情说清楚。 Emo 的设计规则是:一切所见即所得 —— 调用有括号,返回写出来,可变性永远拼出来。 把各节拼成一个 main.emo,emo run main.emo 即可运行。
1绑定与字符串
const 不变,var 可变但出不了块。字符串永远双引号,插值只有一种形状,花括号里可以装任意表达式。
// ---- 1 · 绑定与字符串 ----
const who = "世界"
var visits = 6
visits = visits * 7
println("hello, ${who} — six sevens are ${visits}")
// 插值可以装任何表达式, 中文就是普通的字符串数据
const item = "伞"
println("one ${item} costs ${19 + 9} yuan")2类:不可变值类型
字段只在 init 里诞生,之后冻结;两个内容相同的实例 ==。谓词方法以 ? 结尾。
// ---- 2 · 类: 不可变值类型 ----
class User {
def init(name String, age Int64) {
self.name = name
self.age = age
}
def describe() String {
return "${self.name},age ${self.age}"
}
def adult?() Bool {
return self.age >= 18
}
}
const ming = User.new(name: "王晓明", age: 28)
println(ming.describe())
println("adult? ${ming.adult?()}")
// 字段在 init 之后冻结, 相等比较的是内容:
println(ming == User.new(name: "王晓明", age: 28))3接口:形状即契约
接口属于调用方。类不必声明实现任何接口 —— 形状对上,契约就成立。
// ---- 3 · 接口: 形状即契约 ----
// 哪里都没有 implements: 形状匹配, 即满足接口。
interface Greeter {
def greet() String
}
class Machine {
def greet() String {
return "[beep] 你好"
}
}
class Friend {
def greet() String {
return "很高兴见到你"
}
}
def welcome(g Greeter) String {
if g.is(Machine) {
return "a machine says: ${g.greet()}"
}
return "a friend says: ${g.greet()}"
}
println(welcome(Machine.new()))
println(welcome(Friend.new()))4枚举与标签惯用法
枚举成员不携带数据。需要数据时,标签骑在元组里,case 按位置解构,还能带守卫。
// ---- 4 · 枚举与标签惯用法 ----
// 枚举是封闭的有名值集合, 成员不携带数据。
// 需要携带数据时, 标签放进元组, case 按位置解构。
enum Outcome { ok, err }
def classify(age Int64) (Outcome, String) {
if age >= 18 {
return (Outcome.ok, "adult")
}
return (Outcome.err, "minor")
}
case classify(28) {
(Outcome.ok, label) -> { println("28 -> ${label}") }
(Outcome.err, label) -> { println("28 -> ${label}") }
}
// 分支可以带守卫:
case (Outcome.ok, 42) {
(Outcome.ok, v) when v > 40 -> { println("a big answer: ${v}") }
(Outcome.ok, v) -> { println("a small answer: ${v}") }
(Outcome.err, label) -> { println("no answer: ${label}") }
}5数组:定长、变换、永不就地修改
变换总是返回新数组;== 逐元素比较内容。
// ---- 5 · 数组: 定长、变换、永不就地修改 ----
const nums = [3, 1, 2]
println(nums[0] + nums.length())
// 变换返回新数组, 原数组纹丝不动:
const more = nums.append(9)
println(more.length())
println(nums.length())
println([1, 2] == [1, 2])6可变性,一层一层来
var 活不过它的块;长期状态只有 Box,只有 read 与 replace。
// ---- 6 · 可变性, 一层一层来 ----
// var 显式且块作用域; Box 是一个进程拥有的唯一长期可变单元
// —— 只有 read 与 replace, 刻意没有别的。
var tally = 0
tally = tally + 1
tally = tally + 10
println(tally)
const total = Box.new(0)
total.replace(total.read() + 5)
total.replace(total.read() + 6)
println(total.read())7代码块:唯一的函数记法
-> (x) { ... } 是匿名函数;def 是它的命名形态。尾随代码块让回调与迭代保持同一种记法。
// ---- 7 · 代码块: 唯一的函数记法 ----
// 箭头块是匿名函数; 调用可以尾随一个代码块,
// 回调与迭代因此保持同一种记法。
const twice = -> (n Int64) { return n * 2 }
println(twice(21))
def walk(xs Array[Int64], i Int64, f Block) Int64 {
if i == xs.length() {
return 0
}
f(xs[i])
return walk(xs, i + 1, f)
}
walk([10, 20, 30], 0) -> (v Int64) {
println(v)
return v
}8进程:一条消息链
do 起进程,<- 发消息,receive 选择性接收。尾调用有保证,递归就是收信循环。
// ---- 8 · 进程: 三个进程, 一条消息链 ----
def feed(wh Pid, i Int64) Int64 {
if i > 3 {
wh <- "done"
return 0
}
wh <- (i, i)
return feed(wh, i + 1)
}
const rep = do reporter.run(0, self_pid())
const wh = do warehouse.run(rep)
feed(wh, 1)
receive {
(who, handled) -> { println("handled ${handled} orders") }
}