My advice is to progressively use the syntax that feels convenient/safe as you progress and become more confident. Julia has a friendly surface and simple enough syntax that can get you a long way.
However, the manual/documentation is not opinionated (as you would have the legacy-R vs Wickham’s sugars). We can agree that presenting all the facets and possibilities might look too much for an introductory reading.
Now, to address some of your worries.
Here is why this is not redundant: multiple dispatch.
Bear with me a little.
If you plan to stick with one definition, then the following two are not going to be different (let’s say, for calling purposes):
const f = x -> x + 1
ff(x) = x + 1
However, what if you want to define a new method? For ff
you would just go and specialize per your needs:
ff(x::MyCustomType) = ... something
How about the anonymous function? There we have a global constant that is set in stone (any other option would compromise the performance). Now we cannot assign to f
in a weird additive manner, but I can do this (which is actually the short-form definition):
f(x::MyCustomType) = ... something
So, now we are forced to mix them anyway - so why use the anonymous and do the extra keystrokes to have a const
in the first place?
The point is there is no good reason to define anonymous functions and assign them to global constants in the first place: that is not their purpose.
The redundancy would hold if they are actually doing the same thing - and they are not: as you can see, if you give up the short form definition, the global constant naming thing would ruin the definition of multiple methods for your function - so now you’ll need to rely on the standard definition for your additional methods even if they are just “one expression” long (and it is obvious that the short form requires naming - that implies you cannot just pass an anonymous ad-hoc defined function value to another function - as in: map(x -> x + 1, 1:10)
scenario).
Conclusion: no redundancy in essence (although we can admit the existence of a corner-case scenario where they seem interchangeable).
So, I hope that it is pretty clear at this point that standard form (with its single-(compound)-expression syntactic sugar) is essentially different from anonymous functions, and there is no redundancy.
There is a cost not worth paying only if you are frequently doing the multiple lines switch - and if you are doing that frequently, then it is clear that you are going to be more aware of your code design decisions (and use the standard form to start with). On the other hand, if you only occasionally refactor a function to include multiple lines, then it is a small penalty of switching to compound expression or standard form (but you already saved a lot of keystrokes by defining a myriad of no-need-to-change functions using the short-form). I know that Julia is putting lots of responsibilities on the developer’s shoulders - in a way, it is too powerful (and dangerous sometimes). Imagine that it is powerful enough to allow the encoding of R-like syntax (at least the Hadley Wickham flavor).
But what about begin ... end
and let ... end
forms?
begin
and let
blocks are not specifically related to functions. There is convenience syntax in Julia that allows the developer to achieve all kinds of stuff. You can also use begin ... end
and let ... end
to define variables.
If someone doesn’t have anything in principle against begin
and let
blocks, then… why should there be an issue that the language allows to glue things together in a syntactically valid fashion?
Yes, we can agree that Julia is waaaay more complex than R - and although there is idiomatic Julia code, there are still a lot of gray areas where kind of anything goes (usually the idiomatic Julia is to prevent certain pitfalls, especially performance-related).