Why does julia express this expression in this complex way? - julia

I followed the documentation of julia:
julia> :(a in (1,2,3))
:($(Expr(:in, :a, :((1,2,3)))))
Now that :(a in (1,2,3))==:($(Expr(:in, :a, :((1,2,3))))), why does julia express this expression in this way? And what does $ exactly means? It seems to me that $ just evaluates the next expression in a global scope. I found the documentation unclear about this.

The reason :(a in (1,2,3)) is displayed awkwardly as :($(Expr(...))) is because the show function for Expr typed objects (show_unquoted in show.jl) does not understand the in infix operator and fallbacks into a generic printing format.
Essentially it is the same as :(1 + 1) except that show_unquoted recognizes + as an infix operator and formats it nicely.
In any case, :(...) and $(...) are inverse operators in some sense, so :($(..thing..)) is exactly like ..thing.., which in this case is Expr(:in,:a,:((1,2,3))).
One can see this weirdness in :(1+1) for example. The output is of Expr type, as typeof(:(1+1))==Expr confirms. It is actually Expr(:+,1,1), but typing Expr(:+,1,1) on the REPL will show :($(Expr(:+,1,1))) - the generic formatting style of Expr typed objects.
Fixing show.jl to handle in could be a nice change. But the issue is harmless and concerns display formatting.

$ is the interpolation command, Julia use this notation to interpolate Strings as well as Expression:
julia> a=1;
julia> "test $a" # => "test 1"
julia> :(b+$a) # => :(b + 1)
When you type a command in Julia REPL, it tries to evaluates the command and if the code do not have ; char at the end it prints the result, so it's more related to printing functions, that what will be seen on REPL, when a command executes.
so if you want to see the real contents of a variable one possibility is to use dump function:
julia> dump(:(a+b))
Expr
head: Symbol call
args: Array(Any,(3,))
1: Symbol +
2: Symbol a
3: Symbol b
typ: Any
julia> dump(:(a in b))
Expr
head: Symbol in
args: Array(Any,(2,))
1: Symbol a
2: Symbol b
typ: Any
It's clear from above tests, that both expressions use a common data structure of Expr with head, args and typ without any $ inside.
Now try to evaluate and print result:
julia> :(a in b)
:($(Expr(:in, :a, :b)))
julia> :(a+b)
:(a + b)
We already know that both command create a same structure but REPL can't show the result of :(a in b) better that an Expr of result of another Expr and it's why there in a $ inside. But when dealing with :(a+b), REPL do more intelligently and understands that this:
Expr
head: Symbol call
args: Array(Any,(3,))
1: Symbol +
2: Symbol a
3: Symbol b
typ: Any
is equal to :(a+b).

Related

left arrow operator in Julia

I have a vector
a = collect(1:4)
What is the meaning of a <- a in Julia [result - false]
As per the documentation <- is not an assignment operator in Julia.
Here is a way to check it (so that in the future you will be able to solve similar problems more easily):
julia> dump(:(a <- a))
Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol <
2: Symbol a
3: Expr
head: Symbol call
args: Array{Any}((2,))
1: Symbol -
2: Symbol a
So as you can see a <- a is the same as a < (-a) thus you are comparing, using the < operator the a vector with the -a vector.

why could macros not understand a Dict parameter?

macro test1(name,arg)
println(arg.args[2])
typeof(arg.args[2])
end
#test1 test1 (
(arg1, (:max=>10))
)
I have the macro above and I'm trying to pass a Dict parameter as the second parameter. But the problem is that I cannot access it. The macro will always interpret the second parameter as an expression, and when I try to access arg.args[2].args it shows:
Vector{Any} (alias for Array{Any, 1})
so I don't know how to pass the Dict as it is.
I just want to get the second argument like:
Dict{Symbol, Int64} with 1 entry:
:max => 10
This is because macros work on code before the code is compiled. Source code is first parsed to Symbols, literals (integers, floats, strings, etc), or Expr (expressions). At this point, all expressions contain only these three things.** After the macro is done and returns an expression, that expression is compiled into runtime code where more complicated objects like Dicts can exist.
The code below illustrates the difference before and after compiling. Note how 1+5 and Dict() were expressions in the macro body, but is afterward evaluated to an Int64 and a Dict.
# splat arbitrary number of Expr
macro peekExpr(expr1, expr2, expr3tuple...)
println(typeof(expr1), " ", expr1)
println(typeof(expr2), " ", expr2)
println(typeof(expr3tuple), " ", expr3tuple)
:($expr1, $expr2, $expr3tuple)
end
evaluated = #peekExpr 1+5 Dict() Int64 10 max::Int64
#= printout
Expr 1 + 5
Expr Dict()
Tuple{Symbol,Int64,Expr} (:Int64, 10, :(max::Int64))
=#
for item in evaluated println(typeof(item), " ", item) end
#= printout
Int64 6
Dict{Any, Any} Dict{Any, Any}()
Tuple{Symbol,Int64,Expr} (:Int64, 10, :(max::Int64))
=#
**PS: Bear in mind that Expr can contain other objects if you interpolate runtime objects into them (x = Dict(); :(a in $x).args[end] vs :(a in Dict()).args[end]). It's just that macros do not work at a phase where it can access runtime objects. #peekExpr $x will only see a Expr(:$, :x).

Getting the whole AST of the file / complex code

Julia manual states:
Every Julia program starts life as a string:
julia> prog = "1 + 1"
"1 + 1"
I can easily get the AST of the simple expression, or even a function with the help of quote / code_*, or using Meta.parse / Meta.show_sexpr if I have the expression in a string.
The question: Is there any way to get the whole AST of the codepiece, possibly including several atomic expressions? Like, read the source file and convert it to AST?
If you want to do this from Julia instead of FemtoLisp, you can do
function parse_file(path::AbstractString)
code = read(path, String)
Meta.parse("begin $code end")
end
This takes in a file path, reads it and parses it to a big expression that can be evaluated.
This comes from #NHDaly's answer, here:
https://stackoverflow.com/a/54317201/751061
If you already have your file as a string and don’t want to have to read it again, you can instead do
parse_all(code::AbstractString) = Meta.parse("begin $code end")
It was pointed out on Slack by Nathan Daly and Taine Zhao that this code won't work for modules:
julia> eval(parse_all("module M x = 1 end"))
ERROR: syntax: "module" expression not at top level
Stacktrace:
[1] top-level scope at REPL[50]:1
[2] eval at ./boot.jl:331 [inlined]
[3] eval(::Expr) at ./client.jl:449
[4] |>(::Expr, ::typeof(eval)) at ./operators.jl:823
[5] top-level scope at REPL[50]:1
This can be fixed as follows:
julia> eval_all(ex::Expr) = ex.head == :block ? for e in ex eval_all(e) end : eval(e);
julia> eval_all(ex::Expr) = ex.head == :block ? eval.(ex.args) : eval(e);
julia> eval_all(parse_all("module M x = 1 end"));
julia> M.x
1
Since the question asker is not convinced that the above code produces a tree, here is a graph representation of the output of parse_all, clearly showing a tree structure.
In case you're curious, those leaves labelled #= none:1 =# are line number nodes, indicating the line on which each following expression takes place.
As suggested in the comments, one can also apply Meta.show_sexpr to an Expr object to get a more "lispy" representation of the AST without all the pretty printing julia does by default:
julia> (Meta.show_sexpr ∘ Meta.parse)("begin x = 1\n y = 2\n z = √(x^2 + y^2)\n end")
(:block,
:(#= none:1 =#),
(:(=), :x, 1),
:(#= none:2 =#),
(:(=), :y, 2),
:(#= none:3 =#),
(:(=), :z, (:call, :√, (:call, :+, (:call, :^, :x, 2), (:call, :^, :y, 2))))
)
There's jl-parse-file in the FemtoLisp implementation of the Julia parser. You can call it from the Lisp REPL (julia --lisp), and it returns an S-expression for the whole file. Since Julia's Expr is not much different from Lisp S-expressions, that might be enough for you purposes.
I still wonder how one would access the result of this from within Julia. If I understand correctly, the Lisp functions are not exported from libjulia, so there's no direct way to just use a ccall. But maybe a variant of jl_parse_eval_all can be implemented.

Julia : pass string to macro

suppose that I have a macro that is defined as :
macro foomacro(ex::Expr)
dump(ex)
ex
end
Currently I would like to pass my expression as a parsed string so that I may pass a rather complicated and case dependent expression that has been obtained via string concatenation.
However, trying :
#foomacro 1+2+3
gives the expected result 6 whereas
#foomacro parse("1+2+3")
returns the parsed expression :(1+2+3) instead of actually parsing it...
As far as I understand this both macros should be receiving the same expression but this is clearly not the case.
How do I get this MWE to work ?
ps: I figured out this fix but I feel like it is very dirty and "incorrect"
macro foomacro(ex::Expr)
if ex.head == :call
#in this case the user is calling the macro via a parsed string
dump(ex)
return ex
end
dump(ex)
ex
end
ps: if this is of any relevance, currently the code is running on 0.6.4 and if possible I'd rather not update to 1.0 yet since this would postpone my actual project to much...
You're mixing up levels. Let's introduce an intermediate function for clarity:
function foomacro_impl(expr)
dump(expr)
expr
end
macro foomacro(expr)
foomacro_impl(expr)
end
If run, the expression #foomacro <someexpr> will be parsed, the <someexpr> part passed to foomacro_impl, and the result treated as an expression and inserted instead of the original expression. That means that writing #foomacro 1+2+3 is equivalent to having written
let expr = :(1+2+3)
dump(expr)
expr
end
which returns
Expr
head: Symbol call
args: Array{Any}((4,))
1: Symbol +
2: Int64 1
3: Int64 2
4: Int64 3
:(1 + 2 + 3)
an Expr that evaluates to 6.
On the other hand, in #foomacro Meta.parse("1+2+3"), the whole argument, parse("1+2+3"), is used as expr:
julia> let expr = :(Meta.parse("1+2+3"))
dump(expr)
expr
end
Expr
head: Symbol call
args: Array{Any}((2,))
1: Expr
head: Symbol .
args: Array{Any}((2,))
1: Symbol Meta
2: QuoteNode
value: Symbol parse
2: String "1+2+3"
:(Meta.parse("1+2+3"))
So the result of the macro call is the expression Meta.parse("1+2+3"), which evaluates to another expression :(1 + 2 + 3), since it is a call to parse. The two forms are thus not receiving the same expression!
But there are ways to manually parse an expression and pass it to a macro:
You can do as I did, and use a separate "macro implementing function". Then, the expression returned by #foomacro bla is equivalent to foomacro_impl(Meta.parse(bla)). (This approach, BTW, is very useful for testing, and I recommend it most of the times.)
You can use the macro #eval to construct an expression, splice into it, and evaluate it immediately:
julia> #eval #foomacro $(Meta.parse("1+2+3"))
Expr
head: Symbol call
args: Array{Any}((4,))
1: Symbol +
2: Int64 1
3: Int64 2
4: Int64 3
6
(Or similarly, use eval and manually constructed Expr values.)

Evaluate expression with local variables

I'm writing a genetic program in order to test the fitness of randomly generated expressions. Shown here is the function to generate the expression as well a the main function. DIV and GT are defined elsewhere in the code:
function create_single_full_tree(depth, fs, ts)
"""
Creates a single AST with full depth
Inputs
depth Current depth of tree. Initially called from main() with max depth
fs Function Set - Array of allowed functions
ts Terminal Set - Array of allowed terminal values
Output
Full AST of typeof()==Expr
"""
# If we are at the bottom
if depth == 1
# End of tree, return function with two terminal nodes
return Expr(:call, fs[rand(1:length(fs))], ts[rand(1:length(ts))], ts[rand(1:length(ts))])
else
# Not end of expression, recurively go back through and create functions for each new node
return Expr(:call, fs[rand(1:length(fs))], create_single_full_tree(depth-1, fs, ts), create_single_full_tree(depth-1, fs, ts))
end
end
function main()
"""
Main function
"""
# Define functional and terminal sets
fs = [:+, :-, :DIV, :GT]
ts = [:x, :v, -1]
# Create the tree
ast = create_single_full_tree(4, fs, ts)
#println(typeof(ast))
#println(ast)
#println(dump(ast))
x = 1
v = 1
eval(ast) # Error out unless x and v are globals
end
main()
I am generating a random expression based on certain allowed functions and variables. As seen in the code, the expression can only have symbols x and v, as well as the value -1. I will need to test the expression with a variety of x and v values; here I am just using x=1 and v=1 to test the code.
The expression is being returned correctly, however, eval() can only be used with global variables, so it will error out when run unless I declare x and v to be global (ERROR: LoadError: UndefVarError: x not defined). I would like to avoid globals if possible. Is there a better way to generate and evaluate these generated expressions with locally defined variables?
Here is an example for generating an (anonymous) function. The result of eval can be called as a function and your variable can be passed as parameters:
myfun = eval(Expr(:->,:x, Expr(:block, Expr(:call,:*,3,:x) )))
myfun(14)
# returns 42
The dump function is very useful to inspect the expression that the parsers has created. For two input arguments you would use a tuple for example as args[1]:
julia> dump(parse("(x,y) -> 3x + y"))
Expr
head: Symbol ->
args: Array{Any}((2,))
1: Expr
head: Symbol tuple
args: Array{Any}((2,))
1: Symbol x
2: Symbol y
typ: Any
2: Expr
[...]
Does this help?
In the Metaprogramming part of the Julia documentation, there is a sentence under the eval() and effects section which says
Every module has its own eval() function that evaluates expressions in its global scope.
Similarly, the REPL help ?eval will give you, on Julia 0.6.2, the following help:
Evaluate an expression in the given module and return the result. Every Module (except those defined with baremodule) has its own 1-argument definition of eval, which evaluates expressions in that module.
I assume, you are working in the Main module in your example. That's why you need to have the globals defined there. For your problem, you can use macros and interpolate the values of x and y directly inside the macro.
A minimal working example would be:
macro eval_line(a, b, x)
isa(a, Real) || (warn("$a is not a real number."); return :(throw(DomainError())))
isa(b, Real) || (warn("$b is not a real number."); return :(throw(DomainError())))
return :($a * $x + $b) # interpolate the variables
end
Here, #eval_line macro does the following:
Main> #macroexpand #eval_line(5, 6, 2)
:(5 * 2 + 6)
As you can see, the values of macro's arguments are interpolated inside the macro and the expression is given to the user accordingly. When the user does not behave,
Main> #macroexpand #eval_line([1,2,3], 7, 8)
WARNING: [1, 2, 3] is not a real number.
:((Main.throw)((Main.DomainError)()))
a user-friendly warning message is provided to the user at parse-time, and a DomainError is thrown at run-time.
Of course, you can do these things within your functions, again by interpolating the variables --- you do not need to use macros. However, what you would like to achieve in the end is to combine eval with the output of a function that returns Expr. This is what the macro functionality is for. Finally, you would simply call your macros with an # sign preceding the macro name:
Main> #eval_line(5, 6, 2)
16
Main> #eval_line([1,2,3], 7, 8)
WARNING: [1, 2, 3] is not a real number.
ERROR: DomainError:
Stacktrace:
[1] eval(::Module, ::Any) at ./boot.jl:235
EDIT 1. You can take this one step further, and create functions accordingly:
macro define_lines(linedefs)
for (name, a, b) in eval(linedefs)
ex = quote
function $(Symbol(name))(x) # interpolate name
return $a * x + $b # interpolate a and b here
end
end
eval(ex) # evaluate the function definition expression in the module
end
end
Then, you can call this macro to create different line definitions in the form of functions to be called later on:
#define_lines([
("identity_line", 1, 0);
("null_line", 0, 0);
("unit_shift", 0, 1)
])
identity_line(5) # returns 5
null_line(5) # returns 0
unit_shift(5) # returns 1
EDIT 2. You can, I guess, achieve what you would like to achieve by using a macro similar to that below:
macro random_oper(depth, fs, ts)
operations = eval(fs)
oper = operations[rand(1:length(operations))]
terminals = eval(ts)
ts = terminals[rand(1:length(terminals), 2)]
ex = :($oper($ts...))
for d in 2:depth
oper = operations[rand(1:length(operations))]
t = terminals[rand(1:length(terminals))]
ex = :($oper($ex, $t))
end
return ex
end
which will give the following, for instance:
Main> #macroexpand #random_oper(1, [+, -, /], [1,2,3])
:((-)([3, 3]...))
Main> #macroexpand #random_oper(2, [+, -, /], [1,2,3])
:((+)((-)([2, 3]...), 3))
Thanks Arda for the thorough response! This helped, but part of me thinks there may be a better way to do this as it seems too roundabout. Since I am writing a genetic program, I will need to create 500 of these ASTs, all with random functions and terminals from a set of allowed functions and terminals (fs and ts in the code). I will also need to test each function with 20 different values of x and v.
In order to accomplish this with the information you have given, I have come up with the following macro:
macro create_function(defs)
for name in eval(defs)
ex = quote
function $(Symbol(name))(x,v)
fs = [:+, :-, :DIV, :GT]
ts = [x,v,-1]
return create_single_full_tree(4, fs, ts)
end
end
eval(ex)
end
end
I can then supply a list of 500 random function names in my main() function, such as ["func1, func2, func3,.....". Which I can eval with any x and v values in my main function. This has solved my issue, however, this seems to be a very roundabout way of doing this, and may make it difficult to evolve each AST with each iteration.

Resources