Preview¶
Rock Paper Scissors¶
Here’s a trivial example of MLStyle.jl in action:
using MLStyle
@data Shape begin # Define an algebraic data type Shape
Rock()
Paper()
Scissors()
end
# Determine who wins a game of rock paper scissors with pattern matching
play(a::Shape, b::Shape) = @match (a, b) begin
(Paper(), Rock()) => "Paper Wins!";
(Rock(), Scissors()) => "Rock Wins!";
(Scissors(), Paper()) => "Scissors Wins!";
(a, b) => a == b ? "Tie!" : play(b, a)
end
For a pattern like A()
, there’s a chance for them to get used with A
:
# use pattern `A()` with the syntax `A`
MLStyle.is_enum(::Type{Rock}) = true
MLStyle.is_enum(::Type{Paper}) = true
MLStyle.is_enum(::Type{Scissors}) = true
play(a::Shape, b::Shape) = @match (a, b) begin
(Paper, Rock) => "Paper Wins!";
(Rock, Scissors) => "Rock Wins!";
(Scissors, Paper) => "Scissors Wins!";
(a, b) => a == b ? "Tie!" : play(b, a)
end
Homoiconic pattern matching for Julia ASTs¶
Here’s a less trivial use of MLStyle.jl for deconstructing and pattern matching Julia code.
rmlines = @λ begin
e :: Expr => Expr(e.head, filter(x -> x !== :magic_symbol_oh_really, map(rmlines, e.args))...)
:: LineNumberNode => :magic_symbol_oh_really
a => a
end
expr = quote
struct S{T}
a :: Int
b :: T
end
end |> rmlines
@match expr begin
quote
struct $name{$tvar}
$f1 :: $t1
$f2 :: $t2
end
end =>
quote
struct $name{$tvar}
$f1 :: $t1
$f2 :: $t2
end
end |> rmlines == expr
end
Generalized Algebraic Data Types¶
@use GADT
@data public Exp{T} begin
Sym{A} :: Symbol => Exp{A}
Val{A} :: A => Exp{A}
Lam{A, B} :: (Symbol, Exp{B}) => Exp{Fun{A, B}}
If{A} :: (Exp{Bool}, Exp{A}, Exp{A}) => Exp{A}
App{A, B, A′<:A} :: (Exp{Fun{A, B}}, Exp{A′}) => Exp{B}
end
A simple interpreter implemented via GADTs could be found at test/untyped_lam.jl
.
Active Patterns¶
Currently, MLStyle does not have fully featured active patterns, but the subset of parametric active patterns that are implemented are very powerful.
@active Re{r :: Regex}(x) begin
ret = match(r, x)
ret !== nothing && return Some(ret)
end
@match "123" begin
Re{r"\d+"}(x) => x
_ => @error ""
end # RegexMatch("123")
@active IsEven(x) begin
x % 2 == 0
end
@match (1, 2, 3) begin
(1, IsEven, a) => a
end # => 3