You mean things like "abc"^3 == "abcabcabc"
? Yes.
Conceptually, if not practically, I would love "start" \"startmiddleend"/"end" == "middle"
You mean things like "abc"^3 == "abcabcabc"
? Yes.
Conceptually, if not practically, I would love "start" \"startmiddleend"/"end" == "middle"
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.
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.
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.
âelifâ simply because it is understood just as well, but is shorter.
This has been said before, but once again:
For example, elseif
is âeasy and intuitiveâ in MATLAB, which also uses 1-based-indexing.
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.).
â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!
@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.
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
(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.
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.
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.
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.
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.
Isnât it unusual for a *
to have measure(s * t) == measure(s) + measure(t)
, where measure
is length
?