PolySubML 是一种实验性的类 ML 编程语言,具有以下特征:
- 结构子类型(又名“编译时鸭式类型”)
- 全局类型推断(单态代码完全无需类型注解)
- 多态函数
- 高阶类型
- 存在类型
- 等价递归类型
- 无不可命名或不可想象类型
- 最坏情况多项式时间类型检查
可在浏览器中在线尝试这里,它不用于生产,而是作为新型类型推断算法和其他语言设计技术的演示。
示例
递归函数实现的 FizzBuzz:
let rec fizzbuzz = fun i -> ( print (if i % 3!= 0 then if i % 5!= 0 then "FizzBuzz" else "Fizz" else if i % 5!= 0 then "Buzz" else i ); if i < 50 then fizzbuzz (i+1) else 0 ); fizzbuzz 0;
循环和可变计数器实现的 FizzBuzz:
let vars = {mut i=0}; loop if vars.i >= 50 then `Break 0 else ( let i = vars.i <- vars.i + 1; print (if i % 3 == 0 then if i % 5 == 0 then "FizzBuzz" else "Fizz" else if i % 5 == 0 then "Buzz" else i ); `Continue 0 );
入门
可在浏览器中在线尝试[https://storyyeller.github.io...],从源码构建需安装lalrpop
和wasm-pack
,然后运行:
lalrpop src/grammar.lalr
wasm-pack build --target web
PolySubML 快速游览
- 基本语法:支持
(* style *)
和// style
注释, whitespace 除避免歧义外无意义,有bool
、int
、float
、str
等基本类型,整数和浮点数运算符不同,字符串连接用^
。 - 表达式和语句:是表达式导向语言,几乎一切都是表达式,有
let v = expr1 in expr2
和let v = expr1; expr2
两种赋值语法,还有print
语句,语句块必须以表达式结尾,顶层可以只有语句。 - 条件语句:
if
是表达式,else
部分需用括号包裹,避免歧义。 - 记录和字段:通过
{name1=val1; name2=val2;...}
定义,可通过.name
访问字段,有特殊简写语法,记录匿名且结构类型,多字段记录是少字段记录的子类型,访问未定义字段会导致编译错误。 - 解构和 let 模式:可通过
let {field1=foo; field2=bar; field3=baz} = record;
解构记录,可省略=
部分,记录解构可嵌套,变量模式_
用于忽略匹配值。 - 可变字段:变量不可重新赋值,仅可变记录字段有可变状态,通过
mut
前缀创建和更新可变字段,记录使用引用语义,字段赋值表达式返回旧值,可用于值交换。 - 元组:通过
a, b, c,...
创建和解构元组,元组是特殊的记录,有._0
、._1
等访问方式,无 0 或 1 元素元组的简写语法,元组不支持可变字段和存在类型。 - 函数:所有函数需接受一个参数,通过
fun <arg> -> <expr>
定义,函数调用通过后缀参数,函数应用是右结合的,可通过元组或记录模拟多参数函数,也可使用柯里化,但不推荐,可使用递归 let 表达式定义递归函数,let rec
允许多个变量绑定实现相互递归。 - 变体和匹配:通过
\
tag value定义变体,可使用变体模式解构变体值,需使用
match表达式根据变体值执行不同代码,每个匹配分支可访问原始值的静态类型,
match`表达式必须 exhaustive,匹配是顺序无关的,仅匹配最顶层标签,可使用通配符匹配未处理的变体。 - 类型注解:强大的类型推断使除多态代码外无需类型注解,可在表达式、变量模式、记录字面量、函数定义等位置添加类型注解,
any
表示任意值,never
表示不可达值,注意函数类型注解的细节,如fun x: int ->...
中int
注解返回类型等。 - 多态性:为实现代码复用需要多态性,定义通用函数需添加类型参数,类型参数在函数体中可被推断,通用函数类型可作为普通类型传递,需显式实例化通用函数,部分实例化需显式注解,并非所有代码都需要多态性,存在类型是通用类型的镜像,与记录相关,可通过
type name;
解构和注解,存在类型可显式或隐式实例化,遵循与通用类型相同的规则,PolySubML 支持有限形式的显式类型联合和交集。 - 循环和递归:递归函数在处理大型数据时可能因递归深度限制而崩溃,PolySubML 也支持通过
loop
表达式实现 imperative 风格的迭代,需使用可变状态(带可变字段的记录)。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。