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