Why are there all these strange stumbling blocks in Julia?

You mean things like "abc"^3 == "abcabcabc"? Yes.

Conceptually, if not practically, I would love "start" \"startmiddleend"/"end" == "middle"

5 Likes
julia> oneunit(String) * "ab"
"ab"

The practical example I have in mind is that, because regular expressions follow a Kleene algebra in which concatenation is represented by (\cdot), then because we have chosen * for concatenation we can implement operations on regular expressions and they will be consistent with their theoretical properties. For example, we could implement an alternation operator for regular expressions using + as is common in formal language theory (or another + like operator such as |).

If we did implement more operators for regular expressions, it’d be a crying shame if string operations were dissimilar and had different operator precedence.

2 Likes

I have already expected this answer. I know Julia is not and should not be a Python clone, but my underlying question is: why reinvent things that worked easily and intuitively in another language? For the most part, @DNF has given understandable answers to my specific points.

Frankly this example and oneunit(String) * "ab" are not very convincing because they would work in the exact same way with addition: "hello" * 5, zerounit(String) + "ab". On the other hand I am fully convinced by "start" \ "startmiddleend" / "end", because it exploits fully the fact that multiplication is not commutative and has two inverses. And by the example with regexp’s and commutativity. Thanks to @gustaphe and @uniment, I was glad to see your examples.

Too bad these two are only suggestions and do not work in practice. It would have been great to have an example of a currently existing practical feature of the language / standard library which is enabled by the choice of * rather than +; if you have such an example I am all ears.

4 Likes

I see this from a more reality-based perspective, if I have two apples and add one, I have three apples. I can also multiply apples times 2, then I have four apples. Therefore it is difficult for me to understand this *, because I cannot multiply apples among each other and do not expect to get the result of an addition. This is very unintuitive for me.

1 Like

“elif” simply because it is understood just as well, but is shorter.

This has been said before, but once again:

  1. There are many other languages (you just happen to have a strong Python bias).
  2. “easily and intuitively” is largely subjective and heavily influenced by the language you’re coming from.

For example, elseif is “easy and intuitive” in MATLAB, which also uses 1-based-indexing.

8 Likes

I have programmed Java before Python. So “yes” I found the Python syntax a great relief from the bloated Java syntax. I don’t stick to a language like a tribe I have to defend, I see what is practical and what is not, so these two sentences are relatively independent.

If I would ask my girlfriend (who knows nothing about programming) what elif means she might have no clue.

Anyways, I’m going to stop here, because such a syntax discussion is neither fun (at least not for me) nor useful (it is what it is and it won’t change.).

8 Likes

“elif” is very quick to understand and easy to remember, but you’re right, of course I’ll still learn Julia because the language just has so many other advantages!

2 Likes

@Sandjan, I think your questions are reasonable and @DNF’s answer (Why are there all these strange stumbling blocks in Julia? - #2 by DNF) was a straightforward response that (you, presumably) acknowledged. So in a sense perhaps not much else needs to be said.

But I do care about how “learnable” Julia is and so these discussions are relevant. Areas of Julia that concern me most are things like the stacktraces and how complex types can occasionally become in Julia, but I notice that none of these made your list. It’s useful to hear the perspective of a newcomer, so thanks.

One point I can address directly, as an amusing discovery I made within my first 5 minutes of learning Python (unlike you, I learned Python after Julia, and perhaps unsurprisingly I have your experience in reverse when I look at Python code). This example illustrates how two sensible “conveniences” can in fact lead to inconsistency:

In Python,

>>> int("4")       # seems fine
4
>>> float("3.2")   # seems fine
3.2
>>> bool("False")  # huh?
True

There’s a good reason for the behavior of the last line: unlike Julia, Python automatically tests “truthiness” (which some might argue is an advantage of Python) and for containers truth means “non-empty”. So because "False" is not the empty string, it’s True. But obviously that result is a trap if you expect it to parse the string.

In general, I find Python slightly too willing to just mush forward, and often I’d rather it just throw an error. It’s not as if Julia doesn’t have it’s own amusing/concerning list of “wat?s”, but at least in this particular case I think Julia’s increased pickiness saves you from a trap.

Anyway, I think it’s great that you’re paying attention to what aspects of Julia make it easier or harder to learn & use, and I’d be curious how your perspective on this evolves with increased familiarity with the language.

49 Likes

How come these “wats” do not break regular Julia code?

Is it because they are “weird” patterns one rarely stumbles into?

For the same reason that these wats don’t break regular python code. These Most are just weird edge cases that you pretty much never see in the wild.

Edit: I did not want to dismiss the possibility that there are bugs happening because of these behaviors. But it’s not worse than for any other language IMHO. To elaborate, from the list linked by Tim Holy I consider only one entry (precedence of the range operator) a common footgun & two more entries as possible candidates for bugs that may appear in the wild

details

(typo changing _ to - and accidentally shadowing Base.:- and maybe ambigous float literal juxtaposition if somebody manages to combine questionable variable naming, questionable whitespace placement and complete unawareness of scientific notation)

As always, it’s good to be aware of the edge cases of any language you work with.

5 Likes

The parsing precedence of : and of &/== causes bugs all the time. Others like fld/div mentioned above may cause bugs as well.

I tend to be very liberal in my use of parentheses for this reason. I prefer being explicit.

8 Likes

The answer by @DNF is straightforward, every language has its own choices and doesn’t have to be a copy of any other language. However, I find two valid points in the OP:

I find this truly annoying. I don’t mean specifically parsing, I mean something like truncated integer division. In C and Fortran, I could do for example int x = 5.2 / 2 to get x = 2. To do the same thing in Julia, I have to write x = trunc(Int, 5.2 / 2), there is no straightforward function/symbol for truncated integer conversion, and Int(2.6) doesn’t help here. Whereas in C and Fortran you get it implicitly for free based on the types of variables.

Again, this is also a valid point. Using * for concatenating two strings is completely unintuitive, regardless of “commutativity” or any other algebraic property. No one (I assume) would think of stacking two strings beside each other using * instead of +. Simplicity matters, but Julia, at many times, chooses pickiness over simplicity.

3 Likes

This is only about familiarity, not intuition. Intuitively, concatenation is closer to multiplication than to addition, in mathematics that is even how you normally write multiplication: by concatenating symbols. xy is the product of x and y.

21 Likes

I think this is an “abstract algebra” v colloquial usage issue.

For non-mathematicians “x” concatenated with “x” gives “xx” and its natural to think of this as “two x’s”, hence + feels right.

In abstract algebra * is just an operation that satisfies different properties (I think “free group” and “word” are relevant terms). Since concatenation satisfies these properties it makes sense to use *.

Since Julia was designed with scientific computing (or numerical mathematics) in mind it makes a lot of sense they went with the mathematical version.

11 Likes

Isn’t it unusual for a * to have measure(s * t) == measure(s) + measure(t), where measure is length?