Stream: helpdesk (published)

Topic: StaticArrays.jl macro constructors


view this post on Zulip Michael Fiano (Jun 27 2022 at 16:48):

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:])

view this post on Zulip chriselrod (Jun 27 2022 at 17:34):

@macroexpand is your best friend when it comes to understanding Julia macros.

view this post on Zulip chriselrod (Jun 27 2022 at 17:35):

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}))

view this post on Zulip Michael Fiano (Jun 27 2022 at 17:36):

Yes I tried that, but I'm not sure what it is doing apart from the obvious syntactical abstraction.

view this post on Zulip Sukera (Jun 27 2022 at 17:38):

that's all a julia macro is though

view this post on Zulip Sukera (Jun 27 2022 at 17:38):

just an AST transform

view this post on Zulip Michael Fiano (Jun 27 2022 at 17:38):

Oh in Lisp macros are for 2 things, that being 1 of them.

view this post on Zulip Sukera (Jun 27 2022 at 17:38):

what's the other?

view this post on Zulip Michael Fiano (Jun 27 2022 at 17:39):

Evaluation control

view this post on Zulip Sukera (Jun 27 2022 at 17:40):

in what sense?

view this post on Zulip Sukera (Jun 27 2022 at 17:40):

sorry, not too familiar with common lisp :)

view this post on Zulip Michael Fiano (Jun 27 2022 at 17:41):

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.

view this post on Zulip Sukera (Jun 27 2022 at 17:42):

yeah, that doesn't really fit into julias compilation model

view this post on Zulip Sukera (Jun 27 2022 at 17:42):

restricting when and how eval works is a big part of what allows julia to be a compiled language in the first place

view this post on Zulip Sukera (Jun 27 2022 at 17:43):

unlike LISP, in julia there's a clear difference between the expression object and the runtime objects described by that expression

view this post on Zulip Sukera (Jun 27 2022 at 17:43):

julias macros run just after parsing, where no values are assigned to variables yet (except for literals of course)

view this post on Zulip Michael Fiano (Jun 27 2022 at 17:43):

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.

view this post on Zulip Sukera (Jun 27 2022 at 17:44):

yes, the famous homoiconicity part of LISP allows that

view this post on Zulip Michael Fiano (Jun 27 2022 at 17:44):

No, that has nothing to do with homoiconicity

view this post on Zulip Michael Fiano (Jun 27 2022 at 17:44):

That is called a meta-circular evaluator.

view this post on Zulip Michael Fiano (Jun 27 2022 at 17:45):

Homoiconicity just means that the internal AST representation is identical to the suface syntax of the language.

view this post on Zulip Sukera (Jun 27 2022 at 17:46):

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

view this post on Zulip Sukera (Jun 27 2022 at 17:46):

I suppose you could have the laziness without homoiconicity, but it wouldn't be as nice anymore :thinking:

view this post on Zulip Michael Fiano (Jun 27 2022 at 17:46):

The deferral is just a special case of the 1st case (syntactic abstraction). The eager evaluation is the second case

view this post on Zulip Sukera (Jun 27 2022 at 17:47):

right

view this post on Zulip Michael Fiano (Jun 27 2022 at 17:47):

After all, to defer, you just have to expand into a closure/thunk

view this post on Zulip Sukera (Jun 27 2022 at 17:47):

there's no eager evaluation in julia, by design :)

view this post on Zulip Sukera (Jun 27 2022 at 17:47):

you can sort of mimic it, but you can't evaluate code in the context of e.g. a local function

view this post on Zulip Michael Fiano (Jun 27 2022 at 17:47):

I see

view this post on Zulip Sukera (Jun 27 2022 at 17:47):

eval always happens at global scope

view this post on Zulip Michael Fiano (Jun 27 2022 at 17:48):

Hmm, in my startup.jl I am using @eval inside a function.

view this post on Zulip Sukera (Jun 27 2022 at 17:49):

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

view this post on Zulip Sukera (Jun 27 2022 at 17:49):

you e.g. can't eval a new variable into a function, for example

view this post on Zulip Michael Fiano (Jun 27 2022 at 17:50):

Interesting. I guess that expains why there are no macrolet's or symbol macros, or compiler macros, only regular macros.

view this post on Zulip Sukera (Jun 27 2022 at 17:51):

not sure what those are, but yeah, that's in part why macros are just AST transforms

view this post on Zulip Sukera (Jun 27 2022 at 17:51):

I can really recommend Jeff's Phd thesis, explaining the whole world age mechanism that allows for this

view this post on Zulip Sukera (Jun 27 2022 at 17:52):

https://github.com/JeffBezanson/phdthesis

view this post on Zulip Michael Fiano (Jun 27 2022 at 17:53):

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

view this post on Zulip Michael Fiano (Jun 27 2022 at 17:53):

anyway, I am getting off-topic again :sweat_smile:

view this post on Zulip Sukera (Jun 27 2022 at 17:56):

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?

view this post on Zulip Sukera (Jun 27 2022 at 17:57):

haha, it's fine

view this post on Zulip Michael Fiano (Jun 27 2022 at 17:57):

Oh and I forgot reader macros, which are expanded early on by the parser. ( for example is a reader macro.

view this post on Zulip Michael Fiano (Jun 27 2022 at 17:58):

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.

view this post on Zulip Michael Fiano (Jun 27 2022 at 17:59):

and variable bindings would shadow them if they are introduced in an inner scope, or vice versa if using local symbol macros.

view this post on Zulip Sukera (Jun 27 2022 at 18:00):

in julia, that would just be a @foo macro that doesn't take any arguments, returning the expression :(call_something(x,y,z))

view this post on Zulip Michael Fiano (Jun 27 2022 at 18:00):

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.

view this post on Zulip Sukera (Jun 27 2022 at 18:01):

though they wouldn't be shadowed, since @foo is always parsed as a macro call and can't be an identifier

view this post on Zulip Michael Fiano (Jun 27 2022 at 18:01):

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:

view this post on Zulip Sukera (Jun 27 2022 at 18:01):

if you like iterators, I think you may enjoy the most underused interface Base has to offer - the Sampler API in Random

view this post on Zulip Sukera (Jun 27 2022 at 18:02):

the docs are a little dense, but once you figure it out - they're very nice

view this post on Zulip Michael Fiano (Jun 27 2022 at 18:02):

I looked at that today. Pretty cool stuff.

view this post on Zulip Michael Fiano (Jun 27 2022 at 18:06):

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.

view this post on Zulip Sukera (Jun 27 2022 at 18:07):

haha yeah I get that

view this post on Zulip Sukera (Jun 27 2022 at 18:07):

I've spent quite a lot of time implementing a julia property based testing framework based on a single haskell blog post about Hedgehog

view this post on Zulip Michael Fiano (Jun 27 2022 at 18:07):

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.

view this post on Zulip Michael Fiano (Jun 27 2022 at 18:08):

Yeah, I saw your property based testing framework.


Last updated: Oct 02 2023 at 04:34 UTC