Re-posted from: https://white.ucc.asn.au/2018/06/01/Julia-Favourite-New-Things.html
If I were more musically talented I would be writing a song
♫ Arguments that are destructured, and operator characters combine-ed; Loop binding changes and convert redefine-ed… ♫
no none of that, please stop.
Technically speaking these are a few of my Favourite Things that are in julia 0.7-alpha.
But since since 1.0 is going to be 0.7 with deprecations removed,
We can look at it as a 1.0 list.
Many people are getting excited about big changes like
Pkg3,
named tuples,
field access overloading,
lazy broadcasting,
or the parallel task runtime (which isn’t in 0.7 alpha, but I am hopeful for 1.0)
I am excited about them too, but I think they’re going to get all the attention they need.
(If not then they deserve a post of their own each, not going to try and squeeze them into this one.)
Here are some of the smaller changes I am excited about.
These are excepts from 0.7-alpha NEWS.md
Function argument De-structuring
Destructuring in function arguments: when an expression such as
(x, y)
is used as
a function argument name, the argument is unpacked into local variablesx
andy
as in the assignment(x, y) = arg
([#6614]).
This will mean the way I attempt to write do blocks with multiple arguments will work
foo do (a,b)
Also it makes the following work:
julia> foo((a,b), c) = (a+b)c
foo (generic function with 1 method)
julia> foo((1,2),3)
9
This is one of the more obscure python features that I missed when switching to julia.
Combining Characters in Operators
Custom infix operators can now be defined by appending Unicode
combining marks, primes, and sub/superscripts to other operators.
For example,+̂ₐ″
is parsed as an infix operator with the same
precedence as+
([#22089]).
This means unlimited infix operations can be defined, without allowing some of the unread-ability that just letting words be used as infix would do.
Obviously should still be used with caution, and I think the need to use unicode will cause surficent caution and strike a good balance.
It should be understood that julia supporting editors (including vim and emacs) transform e.g. \_2
into ₂ on a tab.
But still, that extra combination of key-presses I think will make people use them with caution.
Which should stop us seeing an unreadable mess.
Overall I’m not sure how often it will be used, but I think adding a subscript/superscript letter/number or two will show up.
Silly example:
julia> +₂(a,b) = sqrt(a^2 + b^2)
+₂ (generic function with 1 method)
julia> 1 +₂ 2
2.23606797749979
The missing value
The
missing
singleton object (of typeMissing
) has been added to represent
missing values ([#24653]). It propagates through standard operators and mathematical functions,
and implements three-valued logic, similar to SQLsNULL
and R’sNA
.
It is kind of like nan
for floating point numbers – it propagates contaminating everything it touches.
But unlike nan
it doesn’t (semantically) represent a particular notion of mathematical/IEEE error,
but represents the notion of the data being in some sense missing.
It will hopefully make a lot of code clearer.
It becomes possible because of the small unions optimization.
One can contrast Array(Union{Missing,T})
with DataArray{T}
from DataArrays.jl, especially if you go back and look at the older tags.
One can also contrast Array(Union{Nothing,T})
with Array{Nullable{T}}
and NullableArray{T}
from the deprecated NullableArrays.jl.
It will be interesting to see exactly how nothing
and missing
play out in-practice.
I feel like if all things go well, then in most circumstances operating with a missing
should return a missing
(i.e. propagation),
and operating with a nothing
should throw an error (Much like a NullReferenceError
).
Use begin blocks when defining enum values
Values for
Enum
s can now be specified inside of abegin
block when using the
@enum
macro ([#25424]).
Nice little QOL improvement:
Consider:
@enum Fruit apple=1 orange=2 kiwi=4
vs
@enum Fruit begin
apple=1
orange=2
kiwi=4
end
Constructors no longer falls back to convert
The fallback constructor that calls
convert
is deprecated. Instead, new types should
prefer to define constructors, and addconvert
methods that call those constructors
only as necessary ([#15120]).
Conversion and construction are not really the same thing at all.
And this will get rid of the long error message in:
julia> type Foo end
julia> Foo(2)
ERROR: MethodError: Cannot `convert` an object of type Int64 to an object of type Foo
This may have arisen from a call to the constructor Foo(...),
since type constructors fall back to convert methods.
Stacktrace:
[1] Foo(::Int64) at ./sysimg.jl:77
Parse braces expressions like circumfix operators
{ }
expressions now usebraces
andbracescat
as expression heads instead
ofcell1d
andcell2d
, and parse similarly tovect
andvcat
([#8470]).
Everyone is familiar with infix operator overloading, and prefix operator overloading.
Julia actually has what I would call circumfix operator overloading.
[a b]
can be overloaded by overloading the hcat
function.
[a; b]
can be overloaded by overloading the vcat
, function.
Normally these are used for arrays concatenation/construction.
But overloading them is cool, I do it in TensorFlow.jl.
In TensorFlow.jl, this constructs nodes in the symbolic graph that perform the concatenations,
once the graph is executed.
I am using it right now in a project and it is fairly nice, espcially when combining with slicing syntax.
This is how I output colors in HSV.
It is better to train to target the sin/cos of hue,
so I need that in my graph.
Y_sat = nn.sigmoid(Y_logit[:,3])
Y_val = nn.sigmoid(Y_logit[:,4])
Y_hue_sin = tanh(Y_logit[:,1])
Y_hue_cos = tanh(Y_logit[:,2])
Y_hue_o1 = Ops.atan2(Y_hue_sin, Y_hue_cos)
Y_hue = reshape(Y_hue_o2, [-1]) # force shape, this I should fix
Y = [Y_hue Y_sat Y_val]
Anyway, to get back on point:
This language change means that braces ({}
) can be overloaded in the same away.
They’ve basically been sitting there wasting since julia 0.4 when there use as Matlab-style cell arrays was replace by just having an array typed as Any
.
Not sure I have any uses for it right now, but it open’s up options.
There are a few other backet-type unicode characters, so maybe in the 1.x timeframe we might also be able to overload those as circumfix.
(and maybe use them for call/getindex
type overload too).
For loop variable binding changes
- In
for i = ...
, if a local variablei
already existed it would be overwritten
during the loop. This behavior is deprecated, and in the futurefor
loop variables
will always be new variables local to the loop ([#22314]).
The old behavior of overwriting an existing variable is available viafor outer i = ...
.- In
for i in x
,x
used to be evaluated in a new scope enclosing thefor
loop.
Now it is evaluated in the scope outside thefor
loop.- In
for i in x, j in y
, all variables now have fresh bindings on each iteration of the
innermost loop. For example, an assignment toi
will not be visible on the nextj
loop iteration ([#330]).
These are just nice.
Messing with your iteration variables of the for-loop leads to hard to read/comprehend code.
This makes it harder get any advantage out of doing so, so discourages people from doing it.
Also, #330 is the oldest issue closed by this release it is from 2012 – about 6 years ago.
To this day there remains only 8 open issues older than it.
Contrast the newest issue/PR number in this is #27212,
an improvement to interface for matrix factorizations, written under 2 weeks ago.
ccall is now less magic in how it uses ampersand
Prefix
&
for by-reference arguments toccall
has been deprecated in favor of
Ref
argument types ([#6080]).
ccall
used to be really magic.
While #18754 made it less so – it is still pretty magic.
It looks like a function, but it is not a function at all, but an actual part of the language.
Notice from example that in both 0.6 and 0.7 it’s first parameter needs to be resolve-able at compile time:
so
julia> ccall((Symbol("clock"), "libc"), Int32, ())
ERROR: TypeError: anonymous: in ccall: first argument not a pointer or valid constant expression, expected Ptr, got Tuple{Symbol,String}
julia> ccall((:clock, "libc"), Int32, ())
6978742
But at least with this change it doesn’t use the &
operator entirely differently to the rest of the language.
Begin keyword in indexing expression
begin
is disallowed inside indexing expressions, in order to enable the syntax
a[begin]
(for selecting the first element) in the future ([#23354]).
This isn’t a 0.7 feature so much as a promise of a 1.x feature (hopefully 1.0).
Like we can say x[end]
we’ll be able to say x[begin]
, not a huge advantage most of the time,
for single use we have first
, and for standard arrays we can say x[1]
but for arrays with unusual indexing, like in OffsetArrays.jl,
this will make code a lot clearer.
Consider being able to write x[begin:2:end]
for every second element.
@__DIR__
inside REPL now is same as pwd()
@__DIR__
returns the current working directory rather thannothing
when not run
from a file ([#21759]).
This is just nice, it is what you expect to happen.
DataDeps.jl has some specialized code to deal with exactly this case.
(and said code had bugs, which I am not proud of considering it should be a simple work-around; but I was over using default arguments at the time.)
When promoting types, it is now an error if the types don’t change
The
promote
function now raises an error if its arguments are of different types
and if attempting to convert them to a common type fails to change any of their types.
This avoids stack overflows in the common case of definitions like
f(x, y) = f(promote(x, y)...)
([#22801]).
This is good.
I encountered similar issues when writing this line, though it doesn’t use promote.
The fact that you are hitting a promote
at all, means that nothing accepting these argument types exists,
so if you’re promoting them and not getting a new type, then there is no hope for you to find something better to do with them anyway.
Logging changes
The logging system has been redesigned –
info
andwarn
are deprecated
and replaced with the logging macros@info
,@warn
,@debug
and
@error
. Thelogging
function is also deprecated and replaced with
AbstractLogger
and the functions from the new standardLogging
library.
([#24490])
The new logging system, is really cool.
It is a lot prettier in the console for a start.
@warn
includes a 1-line backtrace showing where it was triggered.
The whole system is configurable and extendable.
Its nice.
Getting the name of things is now easier
module_name
has been deprecated in favor of a new, generalnameof
function. Similarly,
the unexportedBase.function_name
andBase.datatype_name
have been deprecated in favor
ofnameof
methods ([#25622]).
It was annoying not having Base.function_name
and Base.datatype_name
exported.
I kept forgetting how to do it, and having around inside fields of the type.
Explicit imports via using now has just one syntax
The syntax
using A.B
can now only be used whenA.B
is a module, and the syntax
using A: B
can only be used for adding single bindings ([#8000]).
I’ve never been a fan of TMTOWTDI.
While julia is a TMTOWTDI language, there is no need to be excessive.
As they say TIMTOWTDIBSCINABTE.
Also importall
is gone
- The keyword
importall
is deprecated. Useusing
and/or individualimport
statements
instead ([#22789]).
This is good, it was generally a bad-idea, and I don’t know anyone who was using it seriously.
It is easy enough to recreate with some metaprogramming and names(::Module, true)
.
Non-recursive transpose is now done with permutedims
permutedims(m::AbstractMatrix)
is now short forpermutedims(m, (2,1))
, and is now a
more convenient way of making a “shallow transpose” of a 2D array. This is the
recommended approach for manipulating arrays of data, rather than the recursively
defined, linear-algebra functiontranspose
. Similarly,
permutedims(v::AbstractVector)
will create a row matrix ([#24839]).
Good, being unable to transpose arrays of strings in 0.6 (without giving dims) was annoying.
No more:
julia> ["alpha" "omega"]'
1×2 RowVector{Any,ConjArray{String,1,Array{String,1}}}:
Error showing value of type RowVector{Any,ConjArray{String,1,Array{String,1}}}:
ERROR: MethodError: no method matching transpose(::String)
julia> #err no mistakes have been made, that hasn't worked since 0.5
julia> reshape(["alpha", "omega"], (:,1) )
2×1 Array{String,2}:
"alpha"
"omega"
julia> ["alpha" "omega"; "beta" "gamma"]'
ERROR: MethodError: no method matching transpose(::String)
julia> # Oh No. mistakes have been made, AGAIN, that hasn't worked since 0.5
julia> permutedims(["alpha" "omega"; "beta" "gamma"], (2,1))
2×2 Array{String,2}:
"alpha" "beta"
"omega" "gamma"
0.6 used reshape
to transpose vectors of strings, and permutedims
to transpose matrices of strings.
Now we can just do permutedims
without specifying the dims for both.
julia> permutedims(["alpha", "omega"])
1×2 Array{String,2}:
"alpha" "omega"
julia> permutedims(["alpha" "omega"; "beta" "gamma"])
2×2 Array{String,2}:
"alpha" "beta"
"omega" "gamma"
Not quiet as brief as a apostrophe, but still permutedims
for both is a lot clearer.
And this means we are taking transpose seriously still (read these 417 comments, over 3 years to know what that means).
Most functions that create or modify a file/folder return the path
mv
,cp
,touch
,mkdir
,mkpath
, now return the path that was created/modified
rather thannothing
([#27071]).
This is my only real contribution to Base
in the 0.7 timeframe.
It is just one of those tiny “I assumed it always worked that way” things that are technically breaking changes, that are good to get in before they become blocked until Julia 2.0.
Iterator Changes
These seem to be missing from the change log, but they are going to make a whole lot of iterators easier to define.
In 0.6 one had to define 3 functions:
start(iterable)
which returned the initialstate
next(iterable, state)
which return(element, newstate)
- and
done(iterable, state)
returning a boolean to indicate if done.
This often mean one would have to read-ahead and store future element in the state so that one could check if a source was going to be empty when done was called.
It was an annoying pattern.
The changes proposed in Julep #18823 are now in.
Now there is just one function iterate
iterate(iterable)
returns the initialstate
and the first element (basically same asstart
+next
combined)iterate(iterable, state)
returns thenewstate
and the next element (basically same asnext
)- However both the 1-arg and 2-arg forms return
nothing
when there are no more items (i.e. what uesd to be check for indone
.)
This is going to make a lot of code easier to write.
See the docs,
and Eric Davies excellent blog post on the changes
More pure julia math
It’s not in the changelog, because it is basically invisible to users,
but thanks to the work of @pkofod and several others,
julia is now really close to not shipping with a C based math library.
A pure julia math library at the lowest level.
I suggested that here,
though I am sure I wasn’t the first to suggest it.
It will be much nicer to maintain,
and I think, given the people working with julia, will in time exceed(/become) the state of the art, in performance and accuracy.
(To my knowledge there are no speed regressions with this change, so it can only get better.)
Julia 0.7-alpha is still shipping openlibm.
There is a solid chance that 1.0 won’t, and if not 1.0 then definitely gone in the 1.x timeframe.
Compiler/Runtime improvements
- The inlining heuristic now models the approximate runtime cost of
a method (using some strongly-simplifying assumptions). Functions
are inlined unless their estimated runtime cost substantially
exceeds the cost of setting up and issuing a subroutine
call. ([#22210], [#22732])- Inference recursion-detection heuristics are now more precise,
allowing them to be triggered less often, but being more agressive when they
are triggered to drive the inference computation to a solution ([#23912]).- Inference now propagates constants inter-procedurally, and can compute
various constants expressions at compile-time ([#24362]).
I love me some optimizations.
My code, becoming just faster, without me doing anything about it?
What is not to like about that?
From what I hear there will be a lot of optimization work being done in the 1.x timeframe.
Since the language itself will not be changing so much anymore.
Conclusion
So yes,
these are a few of my favourite things in julia 0.7-alpha, and thus what I anticipate for Julia 1.0.
It is a big release,
there are a whole lot more things that I’ve not included in this list.
But they might be a few of your favourite things.
Here is the link to the NEWS.md again