-
-
Notifications
You must be signed in to change notification settings - Fork 292
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Macros and DSLs #196
Comments
Pluto doesn't understand macros, it just looks at the code as if the macro wasn't there. For example, if a macro turns @hello dog into dog = 123 then Pluto things that this cell references This is a difficult problem: the macro is not known to Pluto - it is only defined inside the notebook process. To understand (i.e. expand) macros, there needs to be an analysis step after normal analysis, but before runtime (because the analysis determines which cells will run). So Domain Specific Languages like Soss.jl might not work well. |
Hello Fons, thank you for the fast answer. Hm. I was afraid it would come out this way. :-) Kind greetings, z. |
About DSLs: I am thinking that maybe Pluto can include a "mock version" for a bunch of popular packages with DSLs. So not the original macro, but a macro that transforms the code into something that has the intended references and definitions. For example, Replace=with~.jl could transform @replaceit x ~ y + 1 (Pluto thinks that x = y + 1 Another exampleAnother example is @bind x Slider(1:3) looks like a reference to |
Although, looking at this issue again - the problem is not the DSL, it's a bug in Pluto's syntax analysis! #225 |
Your original issue should be fixed now! But the DSL problem is still there |
Yes! It now works (even if I understand that there are no guarantees :-) ) Thank you for the hint. (Sorry for the late answer. I took an offline time ....) |
Another important case when this issue happens is with the |
See a workaround for some macros here: #371 |
Query.jl and VegaLite.jl both use This will fix those two packages, but it might break others. Which other packages use |
I was wondering how to make things like |
Yep! But you need to figure out what other packages are affected |
Excellent. I know half a dozen who use this notation (some of which I'm guilty of writing) and can't immediately think of other macros using |
Although note that |
Yes, only with |
Here are three options for solving this: Option 1Pluto will This introduces a new problem: not all macros are just code rewrites, some macros have side effects, as part of the expansion process. Something that has a side effect but is not an example of what I mean: julia> macroexpand(Main, :(@show x))
quote
Base.println("x = ", Base.repr(begin
#= show.jl:613 =#
var"#2#value" = x
end))
var"#2#value"
end (note that nothing was printed to stdout except the expanded expression) But macros are not guaranteed to have this nice property. I can't think of an example of a macro that runs a side effect during the expansion, but please comment if you know of one. Option 2Pluto will check for popular macros and run special AST analysis. I don't like this, because the AST analysis scripts are often spaghetti code, and having lots of them will become a burden to work with. Option 3Pluto will contain mock versions of popular macros. This is a stripped down version of the real macro that converts the expression into something that does the correct assignments and references. So not the original macro, just something that works enough for practical use. ExampleI will use
And our example will be: @gensym x y Without special treatment, Pluto will see
Option 1Note: because this macro is in Base, we can just expand the macro without the problems of #177 (comment) . (And this is what Pluto actually does already but shhh that messes with my example.) julia> macroexpand(Main, :(@gensym x y))
quote
x = Base.gensym("x")
y = Base.gensym("y")
Base.nothing
end And we see:
Perfect! But hard to implement Option 2We write an extra check in https://github.com/fonsp/Pluto.jl/blob/v0.11.14/src/analysis/ExpressionExplorer.jl like: elseif ex.head === :macrocall && ex.args[1] === Symbol("@gensym")
symbols_to_be_assigned = ex.args[2:end]
return SymbolsState(Set{Symbol}(), Set{Symbol}(symbols_to_be_assigned))
... fairly simple for this macro, but it can get very complicated. Option 3We would write a mock version, for example function mock_gensym(s::Symbol, ss...)
:($(s) = $(mock_gensym(ss...)))
end
mock_gensym() = 1234 (It doesn't need to be a macro, it can just be a function if that is easier to write.) mock_gensym(:x, :y)
:(x = (y = 1234)) Which, as far as Pluto's analysis is concerned, is equivalent to the expanded macro. I think that Option 3 might be best, with Option 1 as something to revisit in a couple of months/years |
So under option 3, nothing has to be re-written if you change how Where should these mock macros / mocking functions live? Just some And should they be a little bit namespaced? Presumably Pluto knows what packages have been loaded, so it might make sense to restrict each rule to notebooks which have loaded its owner, to avoid surprises because of some package you've never heard of. |
Since macros are non-deterministic, we can't know what code will be executed before a macro call. For this reason, the static analyzers have little to no support for macros. One common solution is to do what has been done with Pluto for now, which is to write package+macro specific static handlers. However, in the case of Pluto, the goals are different than those of static analysis tools because we actually need to know which variables/functions will referenced/assigned after a macro call. So instead of doing static analysis which would require a lot of work to deal with package version and to support every package, the idea would be to do the analysis in several steps. First we macro expand the macro call, which will give us the expression to be executed. After analyzing this expression, we have the dependencies/children for this cell and can run the cell but by replacing the call with the expanded expression so that there is no surprise with regard to non-deterministic macro expansion. The whole process would look something like this:
|
What about macros like |
That's not yet possible inside Pluto, but it will be an exciting possibility after it is fixed! HypertextLiteral.jl has "JSX-like" macro too, and it uses a regular macro instead of a @htl(""""
<hello>$(world)</world>
""")
Also, @schneiderfelipe you should come to PlutoCon next week to discuss JSX! Or send me an email: [email protected]. |
Nice! I developed something similar to HypertextLiteral.jl, but it is a standard I was hoping to make it work in Pluto.jl just like
Definitely! 🥳 👍🏽 |
@schneiderfelipe string macros appear as regular macro calls in the expression tree so this should be fixed once #1032 is merged: |
adding an issue with ModelingToolkit.jl that seems to be related. discussed here. in short:
gives "cannot assign a value to variable workspace3.t from module workspace6" |
I believe the following problem is related. Deleting a The example is as follows:
Deleting the second cell still keeps |
I try out Soss.jl, a tool for probabilistic programming.
I can't attach .jl files, so I changed them to txt.
This (non Pluto) works fine:
sosstest01.txt
This (Pluto) doesn't:
sosstest02.txt
Error message:
Cyclic references among m, y and forwardModel:
Combine all definitions into a single reactive cell using a
begin ... end
block.Am I doing sth wrong, or has Pluto an issue with macros?
Kind greetings, z.
The text was updated successfully, but these errors were encountered: