MLStyle.jl, 博采众长的可扩展pattern matching与代数数据结构

repo: https://github.com/thautwarm/MLStyle.jl
英文站帖子链接 : https://discourse.julialang.org/t/mlstyle-jl-a-package-to-supply-algebraic-data-types-and-all-kinds-of-pattern-matching/13580

了解一下集成各个语言的pattern matching后一个语言是有多好用?

3 个赞

@thautwarm
我macroexpand 了用来对ADT支持的@data宏 结果发现一堆nothing,但是功能却确实能正确使用!
然后去看源代码发现里面用了@eval,为什么? 用@eval是由什么特别的优点吗?

运行时没有eval也没有nothing啊emmmm
eval是重写ast时拿module的元信息用的,准确的说,一般是拿struct的fields

额…我再研究研究。 结构定义能用,但quote住之后expand开还是nothing。不知道是不是macroexpand函数要用的module不应该用MLStyle。

不过我有一个问题,就是这种abstract type的方法和Haskell那种天生的sum type相比会不会有性能损失啊? 同样类似的情况是 Union{nothing,Int} 来充当 Maybe Int,总感觉有种用运行时多态的感觉。觉得性能不如加个Tag(但github有个issue中有人提到说julia编译器内部已经隐形的加了Tag来提升性能,因此用户自己再加Tag就没必要了,不知是否属实)。

损失肯定是有的。abstract type里面存了所有它的实现类型,你觉得这个和union有啥本质上的差别?

现在的实现还不是很高效,未来的bootstrap后会优化到你人工写不出或者说非常难写出的高效代码。pattern matching不仅仅是语法糖啊。

Union{nothing,T} 特别优化过 这是0.7的一个主要提升 没有性能损失 可以放心使用

1 个赞
julia> maybex(x) = x < 0 ? nothing : x
maybex (generic function with 1 method)

julia> @btime maybex(1)
  0.025 ns (0 allocations: 0 bytes)
1

julia> @btime identity(1)
  0.025 ns (0 allocations: 0 bytes)
1

julia> @code_warntype maybex(1)
Body::Union{Nothing, Int64}
1 1 ─ %1 = (Base.slt_int)(x, 0)::Bool                                                                                                                    │╻ <
  └──      goto #3 if not %1                                                                                                                             │
  2 ─      return Main.nothing                                                                                                                           │
  3 ─      return x                                                                                                                                      │

证明

Match.jl 有一个Deep Matching with Composite Types功能很好用,但是我不能直接原封不动地删去 using Match 改为 using MLStyle

struct Address
    street::String
    city::String
    zip::String
end

struct Person
    firstname::String
    lastname::String
    address::Address
end

personinfo(person) = @match person begin
  Person("Julia", lname,  _)           => "Found Julia $lname"
  Person(fname, "Julia", _)            => "$fname Julia was here!"
  Person(fname, lname,
         Address(_, "Cambridge", zip)) => "$fname $lname lives in zip $zip"
  Person(_...)                         => "Unknown person!"
end

MLStyle.jl 怎么做呢?

@Junars

可以这么定义一个结构体@as_record struct
也可以定义完了之后用@as_record 类型名, 第二种用法不要加类型参数

文档见 https://thautwarm.github.io/MLStyle.jl/latest/syntax/pattern.html#deconstructions-of-composite-datahttps://thautwarm.github.io/MLStyle.jl/latest/syntax/records.html#records

稍微复杂点的代码可以比Match.jl快几个数量级。

1 个赞

非常感谢!感觉有点像Scala里的 case :grinning: