Reading other peoples Julia code

(I am not criticising Julia I am starting of learning Julia with a clean slate).

There is this site with tips and tricks by a knowledgeable member and expert programmer of the Julia community: Functional One-Liners in Julia. Practical everyday tricks to improve… | by Erik Engheim | Level Up Coding

He then gives the following example:

julia> map(s → parse(Int, s), [“7”, “42”, “1331”])
3-element Array{Int64,1}:
7
42
133

But then it comes and readability gets and worse:

julia> broadcast(parse, Int, [“7”, “42”, “1331”])
3-element Array{Int64,1}:
7
42
1331

and:

julia> parse.(Int, [“7”, “42”, “1331”])
3-element Array{Int64,1}:
7
42
1331

And I sit here and ask myself ‘why’? What is wrong with the ‘classical map’. It is a whole lot more readable than the other 2 versions. It reminds me on Python’s matplotlib mess: even after 15 years I cannot write a new plotting function without using google because it is so inconsistent and there a trillion ways of doing the same thing.

I would love to hear what other people think and why they would prefer either one of the other 2 code versions posted above.

For me it’s a matter of taste. I can’t see one easier than the other, but that’s also on the eye of the beholder, so, don’t think too much about this. Use whatever you like. The part with Pythons matplotlib seems a bit exaggerated in this context. Having several ways to solve the same problem is not an exception, but typical for most languages. Sometimes it’s useful to do some benchmarking to decide for a specific way.

TLDR: nested map calls can’t be fused by the compiler, and also broadcast differs in semantics from map when you have multiple arguments of different shapes. But yes, there is overlap in functionality.

(In other languages, e.g. Matlab or Numpy, there are lots of “vectorized” functions with implicit map or broadcast semantics, and many people have gotten used to the convenience of not having to write an explicit map call. We wanted to keep that convenience in Julia, but support loop fusion, and apply it to every function, not just ones their authors decided to “vectorize”. Hence the f.(x) syntax was born for elementwise/broadcasted application of functions. It’s pretty handy once you get used to it!)

11 Likes

(Although I use Python more or less every other day but I am not a software engineer by definition. But I never really learnt it. I still have no idea what broadcast does in Python. Probably I have used in the past when blindly copying code from stack exchange without having the slightest clue what the code does. By the way: For my PhD in physics I used the Bigloo Scheme compiler from our French friends).

In any case I should have asked: what of the 3 posted code snippets is good Julia code. Or does it matter in the end? I know it depends on the context (will look up that code fusion business) but still.

I think all of them are fine, but the third one is by far the most common, and has the advantage of possible loop fusion. The dot syntax works for every function, thus it is extremely convenient and one gets used to using and reading it quite quickly.

Because of the generality of these options, probably the comparison with matplotlib is not precise. You will find package-specific mini-languages in Julia as well, and these readability issues arise there, more similarly to what you experience with matplotlib, numpy, jax, etc.

2 Likes

This one is the best, and definitely the most readable of those three. Dot broadcasting also works consistently throughout the language. It’s easy to learn, elegant to write, and you can use it all over the place.

I don’t see any reason to do this:

It’s verbose, awkward and hard to grasp.

It’s verbose, in a hard-to-read way. And it gets progressively worse in more complicated expressions, where performance is also worse.

Dot broadcasting is fantastic, one of the strongest features of the language, imo. You should really read that blog post.

11 Likes