Can someone explain to a beginner what the macro constructors are doing at compile time? The generator function evaluated at compile time seems to be doing mostly type checking. I'm curious if it also evaluates the element-wise expressions at compile time, or what it is trying to solve: https://github.com/JuliaArrays/StaticArrays.jl/blob/master/src/SMatrix.jl
(I am so not used to Julian macros yet coming from the land of Lisp where I can write a complicated macro in my sleep [and actually have before :smile:])
@macroexpand
is your best friend when it comes to understanding Julia macros.
julia> @macroexpand @SVector rand(3)
:(rand((SVector){3}))
julia> @macroexpand @SVector [1, 2, 3]
:((SVector){3}(tuple(1, 2, 3)))
julia> @macroexpand @SMatrix randn(Float32, 2, 3)
:(randn((SMatrix){2, 3, Float32}))
Yes I tried that, but I'm not sure what it is doing apart from the obvious syntactical abstraction.
that's all a julia macro is though
just an AST transform
Oh in Lisp macros are for 2 things, that being 1 of them.
what's the other?
Evaluation control
in what sense?
sorry, not too familiar with common lisp :)
Well suppose you had a function that took complex math expressions as arguments, but enough information is available to compute them at compile time. Or the opposite, deferring them until later.
yeah, that doesn't really fit into julias compilation model
restricting when and how eval
works is a big part of what allows julia to be a compiled language in the first place
unlike LISP, in julia there's a clear difference between the expression object and the runtime objects described by that expression
julias macros run just after parsing, where no values are assigned to variables yet (except for literals of course)
Ah ok. In Lisp each phase of the compiler, the Read (parser), Eval (compiler), Print[er] are recursive in that they can call any other phase.
yes, the famous homoiconicity part of LISP allows that
No, that has nothing to do with homoiconicity
That is called a meta-circular evaluator.
Homoiconicity just means that the internal AST representation is identical to the suface syntax of the language.
hmm, yes, but I was trying to span a bridge from that property to LISP macros deferring the interpretation of the expression until some later date
I suppose you could have the laziness without homoiconicity, but it wouldn't be as nice anymore :thinking:
The deferral is just a special case of the 1st case (syntactic abstraction). The eager evaluation is the second case
right
After all, to defer, you just have to expand into a closure/thunk
there's no eager evaluation in julia, by design :)
you can sort of mimic it, but you can't evaluate code in the context of e.g. a local function
I see
eval
always happens at global scope
Hmm, in my startup.jl I am using @eval inside a function.
oh you can use it in a function, but the code will be evaluated at the top level, not in the scope of the function
you e.g. can't eval
a new variable into a function, for example
Interesting. I guess that expains why there are no macrolet's or symbol macros, or compiler macros, only regular macros.
not sure what those are, but yeah, that's in part why macros are just AST transforms
I can really recommend Jeff's Phd thesis, explaining the whole world age mechanism that allows for this
https://github.com/JeffBezanson/phdthesis
macrolet is the equivalent of a lexically scoped (local macro), like let, but for binding macros instead of variables.
symbol macros are for expanding plain old variables instead of expressions into compound forms.
compiler macros are attached to functions to have them expand or not expand into more efficient code based on the lexical environment they are surrounded in
anyway, I am getting off-topic again :sweat_smile:
not sure why you'd need to bind macros differently :thinking: with symbol macros, are you referring to expanding the object/expression a variable is/was assigned from (represents?) into its original form?
haha, it's fine
Oh and I forgot reader macros, which are expanded early on by the parser. (
for example is a reader macro.
Forget what I said about variables. I meant symbols. you could have :foo
, whenever encounterd in source code to expand into call_something(x, y, z)
. There are both global and lexical variants of these.
and variable bindings would shadow them if they are introduced in an inner scope, or vice versa if using local symbol macros.
in julia, that would just be a @foo
macro that doesn't take any arguments, returning the expression :(call_something(x,y,z))
Lisp macros do pretty much everything. In Julia though, we rely much less on them as the interface abstractions (I love iterators) are just great.
though they wouldn't be shadowed, since @foo
is always parsed as a macro call and can't be an identifier
It's a bit different than a nullary regular macro. They don't map the same. But I will let you breathe a bit :smile:
if you like iterators, I think you may enjoy the most underused interface Base has to offer - the Sampler
API in Random
the docs are a little dense, but once you figure it out - they're very nice
I looked at that today. Pretty cool stuff.
Lisp is like Haskell in that it is a community of a bunch of people working independently trying to bend the language to do interesting things due to how flexibile it is. As a result, nobody works together, and we have a bunch of single developers just writing papers and cool language extensions. They don't work together because well, macros being so flexible means that it is often much easier to re-implement an existing library than to take the time to try to understand the original author's convoluted macro code. After all, code is just a projection of one's own thought processes, and Lisp lets you model code the way you think.
haha yeah I get that
I've spent quite a lot of time implementing a julia property based testing framework based on a single haskell blog post about Hedgehog
It's both a blessing and a curse. I am tired of it to be honest. I've been using it exclusively for about 20 years.
Yeah, I saw your property based testing framework.
docs are coming along, but I think there may be some breaking changes sometime.. I'm not quite happy with the experience yet
With Lisp I feel like I am working backwards, always having to implement my own stuff, because of the above. The plus side is, that the language standard is so well designed that it hasn't ever needed to change. Code from the 60's still runs on modern implementations for example.
But I had enough of it. I want to feel productive again, even if it means throwing away my life's work.
Ah well good luck on it. By the way, what else do you work on? I'm curious since you are the only other Julia developer I know that only has a general CS background.
I'm trying to finish my bachelor's thesis :joy:
but I procrastinate too much with other cool julia stuff
Aha.
It looks like I've been dabbling with Julia on and off for 7 years already. I first started playing with it when @Tamas K. Papp moved from CL to Julia, and I wanted to ever since, but I was in the midst of a years-long project until recently.
https://tamaspapp.eu/post/common-lisp-to-julia/
I don't agree with every point he makes, especially 7 years later, but it does bring up a lot of good points. I am no data scientist though.
The equivalent of reader macros are called non-standard string literals in Julia.
Sadly the link to the Julia docs is dead, and the new Metaprogramming chapter doesn't have a section on them, so I don't know if they are user-extensible or not.
Oh it does. I was looking in the wrong place.
Michael Fiano has marked this topic as resolved.
Last updated: Nov 06 2024 at 04:40 UTC