According to the documentation, all this keyword does is create a lambda and insert it as the first argument of function call before it. This task could be accomplished with a macro with very similar syntax:
map(coll) x -> x^2
I wanted to know the reason behind integrating this feature into the language as a keyword.
I don’t know if this is the reason, but I often use it for very complex lambdas. For example, the
by() function for dataframes:
by(my_df, :col) do df
# a whole mess of stuff...
I’m not sure how easy your macro would be with a multiline expression, but it also doesn’t seem all that much simpler in the example you provided (in that case I’d probably just stick the lambda inside the function call).
You can use
begin blocks for complex lambdas, they’re apparently just syntactic sugar for anonymous functions. Here’s a macro which does exactly what
do keyword does:
macro m_do(func, callable)
if !isa(func, Expr) || func.head != :call
insert!(func.args, 2, callable)
@m_do map([1,2,3]) x -> begin
It isn’t very complicated because the parser translates the map call and the lambda to two bulk
sexprs (I’m not familiar with the source, but it certainly appears so).
We don’t need it, we want it.
@do f(y) x -> begin
is longer and less pretty than
f(y) do x
This is an operation we want to be pretty because it is really commonly used.
It basically takes the place of python’s
with statements and more.
Basically this is a really common and useful pattern.
Some examples of the places I tend to use it:
open("file.txt") do fh
pmap(1:100) do x
mapreduce(hcat, 1:100) do x
# return a vector
mktempdir() do dirname
I wouldn’t like to make all those uglier.
At the end of the day, why do we do anything?
Since julia is just going to compile to machine code anyway.
Or less reductio ad absurdum: why allow anon functions?
Why not just have people write functions normally?
Or use a macro to create real functions out of lambdas syntax?
Looking at the history
do blocks have been in the language since before 0.1 release.
Which is before my time.
The original reason is thus probably buried in a email chain somewhere.
So it’s an ergonomic feature. I didn’t know about these use cases since I’m new to julia. Thanks for clarification.
I never use do blocks because of #15276. There’s no way to combine the
let block trick with
I don’t use
do blocks, because I can never quite grok them, or at least only for a few hours at a time. I read the documentation, and then sort of get it, but next time around I have no idea.
I think it might be related to the spelling. “Who is
doing what to whom?” I can never read it correctly. Sometimes I try to replace it with Python’s “with … as …” and then it’s ok, but most of the time that doesn’t work. In summary, it might not be the concept that’s so hard, but the word
do really throws me off.
Does anyone have a tip for how to make it sound right inside my head?
I am not sure I understand the requirement, but I would not invest too much in mapping it to a construct/sentence in a natural language, and would recommend trying to understand them as they are.
There is not much to
do blocks, just syntactic sugar.
f((x, y) -> body..., args...)
is equivalent to/can be written as
f(args...) do x, y
The reason I try to do that is that even when I occasionally figure out what it means, it never sticks, but seems to slip through my mental fingers, as it were. I don’t have this problem with other parts of the syntax. The substitution/reshuffling of the sequence of ‘things’, as well as the word
do itself trips me up. I can’t help reading it as “do x, y” (how can I do ‘x, y’?)
Anyway, thanks for the explanation. Maybe there is no ‘trick’.
I guess the closest you could get is to read
f(args...) do inner_args...
f(args...) does with inner_args...
which makes sense in many cases:
map(rand(10)) does with x
does to x?
Thanks. Maybe a sort of visual substitution is the way to go.