3 ms·
Yes, this is exactly what I was getting at. Speed is one of the reasons to choose Rust/C/C++ over Python, but there's other compelling ones: static types, more
by hexane360 5y ago
Yes, this is exactly what I was getting at. Speed is one of the reasons to choose Rust/C/C++ over Python, but there's other compelling ones: static types, more control over execution, tight coupling to the OS, etc. To truly solve the two language problem, Julia should have APIs and coding styles which allow greater control, even if they're not used by everyone.
Because it's so closely integrated with C, Python actually does a decent job at this, which IMO is one of the reasons it's been so successful as a general purpose language in addition to a scientific one. I really hope Julia can do the same.
Off the top of my head, here are a few features I think would really improve Julia for general purpose programming:
- Static type checking (JET.jl[1] looks promising)
- Faster startup (again, great strides have been made)
- Tagged, closed unions
- Pattern matching
[1]: https://github.com/aviatesk/JET.jl https://github.com/aviatesk/JET.jl
- eigenspace 5y ago> Pattern matching MLStyle.jl [1] is quite nice for this and has been around for a while. > Tagged, closed unions These are less general than 'real' unions and can be implemented using them. E.g. SumTypes.jl [2] has some macros to make it a bit more convenient to define them, it could use some other quality of life features though. [1] https://thautwarm.github.io/MLStyle.jl/latest/syntax/pattern.html https://thautwarm.github.io/MLStyle.jl/latest/syntax/pattern... [2] https://github.com/MasonProtter/SumTypes.jl https://github.com/MasonProtter/SumTypes.jl
- hexane360 5y agoMLStyle.jl is nice, but I think Julia would really benefit from having it enshrined in the language. Multiple dispatch is great for a lot of problems, but sometimes it makes more sense to do your pattern matching inline. For tagged unions, the difference is in memory layout. A real tagged union type would eliminate indirection and allow more code to be type stable. For instance, mutable struct NotTypeStable o::Union{Some{Int}, Nothing} end function not_type_stable() o = returns_option() if isnothing(o) 0 else 1 end end versus: enum Option{T} Some(T) None end mutable struct TypeStable o::Option{Int} end function type_stable() o = returns_option() match o Some(_) => 1 None => 0 end end Both of these features aren't strictly necessary, but they act as a compliment to Julia's existing dynamic mechanisms (multiple dispatch & Union types)
- celrod 5y agoTry Julia nightly (1.7): julia> @code_warntype not_type_stable() MethodInstance for not_type_stable() from not_type_stable() in Main at REPL[2]:1 Arguments #self#::Core.Const(not_type_stable) Locals o::Any Body::Int64 1 ─ (o = Main.returns_option()) │ %2 = Main.isnothing(o)::Bool └── goto #3 if not %2 2 ─ return 0 3 ─ return 1 julia> versioninfo() Julia Version 1.7.0-DEV.1169 Commit e5d7ef01b0* (2021-05-26 14:17 UTC) Note `Body::Int64`. With older Julia versions, it should still be type stable if you did `o === nothing` instead of `isnothing(o)`.
- hexane360 5y agoRight, I'm referring to the local o being Any, meaning that it must be behind a pointer lookup. I should have clarified because the more common usage is about the function's return value.
- eigenspace 5y agoHere's me copying your second example as closely as possible in julia: using MLStyle mutable struct TypeStable o::Union{Some{Int}, Nothing} end MLStyle.@as_record TypeStable MLStyle.@as_record Nothing function type_stable() o = returns_option() @match o.o begin Some(_) => 1 Nothing() => 0 end end returns_option() = TypeStable(rand(Bool) ? Some(rand(1:10)) : nothing) And what does the compiler have to say about it? julia> Core.Compiler.return_type(type_stable, Tuple{}) Int64 and julia> @code_warntype type_stable() Variables #self#::Core.Const(type_stable) o::TypeStable 259::Union{Nothing, Some{Int64}} return#257::Union{Nothing, Int64} Body::Int64 1 ─ (o = Main.returns_option()) │ (return#257 = Main.nothing) │ (259 = Base.getproperty(o, :o)) │ %4 = (259 isa Nothing)::Bool └── goto #3 if not %4 2 ─ (return#257 = 0) └── goto #5 3 ─ %8 = (259::Some{Int64} isa Some)::Core.Const(true) │ %8 │ %10 = (259::Some{Int64} !== Main.nothing)::Core.Const(true) │ %10 │ (return#257 = 1) └── goto #5 4 ─ Core.Const(:((error)("matching non-exhaustive, at #= REPL[9]:3 =#"))) 5 ┄ return return#257::Int64 This is on version 1.6.1 for me, but should work fine on earlier releases. I agree that having MLStyle.jl pattern matching bundled into julia would be great!
- yawaramin 5y agoThese suggested improvements are really reminding me of OCaml–which actually also has quite close integration with C, like python. There's even a numerical computing library: https://ocaml.xyz/ https://ocaml.xyz/