Hi, community,
I’m a fan of ML, however in addition to F#, it’s a bit difficult to integrate them into daily works. If you’re struggling for this like me, I’m to bring you something makes a big convenience to stop your anxieties such as
“if efficiency and readability conflicts, which should I choose?”. (if you’re kind of urgent, just see https://github.com/thautwarm/MLStyle.jl.)
P.S
The documenter support is added and you can check out https://thautwarm.github.io/MLStyle.jl/latest where I’m trying to figure out some meaningful introductions of this package and corresponding concepts like ADT and pattern-matching, just as @evanfields suggested.
For @ontouchstart 's proposal, the ML here is not machine learning but Meta. ML language family has various features to help with general purpose programming.
Currently, as we are introducing many easy, intuitive and useful features into MLStyle.jl
from other languages like Rust
(range pattern, if let (in developing)), Elixir
(partially dictionary matching), Python
(dynamic array packing) and so on, MLStyle.jl
is becoming more and more powerful.
The Original
As we all know, Julia is so convenient in practical usage, and recently I’ve found the design pattern of Julia is so compatible to ML family.
For example, the struct with no fields would create a singleton when instantiating, which solved the problem to distinguish a enum from variable to capture without case sensitive unification like
match data with
| E -> // E is a enum here
| e -> // e is a pattern to capture here
But in Julia we can do
@match data begin
&E -> # use & to announce that it's using the variable in current context.
# that's a pattern(`^`) from Elixir lang.
E() -> # for E() construct a singleton, and we destruct it in the same way.
e -> # capture
end
Then I wrote a package to do such stuffs(all the pattern matching with ADT in the world to solve problems easilier.)
The syntax might seems similar to Match.jl
in basic usage, but actually this syntax is originally from my head…(actually I think anyone writes Scala, Rust and ML will have the same inspiration).
The difference is that, I have implemented all kinds of commonly used pattern matching from various languages(ML family, Rust, Haskell, Elixir, Scala) in MLStyle.jl
, anditionally you can even simply extend it without much efforts.
Here is the docs of patterns.
The whole documents are not finished yet, but you can have a preview at here.
The implementation might not be very efficient, but in the future bootstrap of this packages’ core will tell that it could do optimizations that human can hardly make, while these functional programming infrastructures does make coding easy and pleasant.
Here are some snippets from docs.
Some commonly used patterns
dict = Dict(1 => 2, "3" => 4, 5 => Dict(6 => 7))
@match dict begin
Dict("3" => four::Int,
5 => Dict(6 => sev)){four < sev} => sev
end
# => 7
@match (1, 2, (3, 4, (5, )))
(a, b, (c, d, (5, ))) => (a, b, c, d)
end
# => (1, 2, 3, 4)
lst = List.List!(1, 2, 3)
@match lst begin
1 ^ a ^ tail => a
end
# => (2, MLStyle.Data.List.Cons{Int64}(3, MLStyle.Data.List.Nil{Int64}()))
Vector matching is practical, too. As the result of using array view, we’re able to avoid copying data when packing, while Python cannot do such easily.
julia> it = @match [1, 2, 3, 4] begin
[1, pack..., a] => (pack, a)
end
([2, 3], 4)
julia> first(it)
2-element view(::Array{Int64,1}, 2:3) with eltype Int64:
2
3
julia> it[2]
4
Make extensions
uisng MLStyle
# define custom application-like pattern
PatternDef.App(*) do args, guard, tag, mod
@match (args) begin
(l::QuoteNode, r :: QuoteNode) => MLStyle.Err.SyntaxError("both sides of (*) are symbols!")
(l::QuoteNode, r) =>
quote
$(eval(l)) = $tag / ($r)
end
(l, r :: QuoteNode) =>
quote
$(eval(r)) = $tag / ($l)
end
end
end
@match 10 begin
5 * :a => a
end
# => 2.0
Many other features to help your daily coding in Julia.
Pattern function is available, too.
@def f begin
# patterns here
x => 1
(x, (1, 2)){x > 3} => 5
(x, y) => 2
::String => "is string"
_ => "is any"
end
f(1) # => 1
f(4, (1, 2)) # => 5
f(1, (1, 2)) # => 2
f("") # => "is string"