eval(x::Expr) has type ::Any


#1

I have the following example:

function create_simple_function()
    str = "v -> v[1]^2*v[3]^-1"
    expr::Expr = parse(str)
    f::Function = eval(expr)
    return f
end
@code_warntype(create_simple_function())

Variables:
  #self#::#create_simple_function
  str::String
  expr::Expr
  f::F

Body:
  begin 
      str::String = "v -> v[1]^2*v[3]^-1" # line 3:
      SSAValue(0) = $(Expr(:invoke, LambdaInfo for #parse#311(::Bool, ::Function, ::String), :(Base.#parse#311), true, :(Main.parse), :(str)))
      expr::Expr = (Base.convert)(Main.Expr,SSAValue(0))::Expr # line 4:
      SSAValue(1) = (Core.eval)(Core.Main,expr::Expr)::Any
      f::F = (Base.convert)(Main.Function,SSAValue(1))::F # line 5:
      return f::F
  end::F

My question is : why is SSAValue(1) of type Any?

In my working code I have thousands of functions to be created this way (with 1 and 2 replaced by is and js etc) and I found by profiling that this line is taking the most time.

Is this the way I should be creating those functions anyway?


#2

You usually shouldn’t create functions this way. Even if you have to for some reason, you must not create it by parsing a string unless the string is user input. You’ll also get an error on master when you call the function. It’s unclear what you actually want to do so it’s hard to say what’s the right solution for you.


#3

I’m trying to implement Automorphism Groups of Groups. For abelian groups these are given by GL(N,ZZ), so I can get away with matrices; For more general groups I need a generic object/function mapping one generating set to another, e.g. if G = <x,y| some relations> automorphism may be given by prescribing its values on the generators, say (x→x*y, y→x).
If I want to compose two of such automorphisms then – I need to compose those functions.

so If a user gave me input (x→x*y, y→x) I want to create a function
(x,y) = v → (v[1]*v[2], v[1]) = (x*y, x)


#4
g(f1, f2) = v->(f1(v[1]), f2(v[2]))

??


#5

maybe I don’t quite understand what You suggest…

I gave only a minimal example in the working code this is what I am doing: first generating f1 and f2 then returning the vector of their values. Side-note: f1 needs the whole v;

The thing is – Automorphism Groups are large and I need to generate many of simple functions like those, not a single one.


#6

The function I give should do


#7

So it’s even simpler.

g(f1, f2) = v->(f1(v), f2(v))

#8

Sure, and this is exactly what I am doing;-)

Now if You look at automorphisms of a group with 10 (or more) generators It contains at least hundreds similar automorphisms (ones comming from multiplication on the left/right by a generator/its inverse) do You have to write all of them by hand as You suggested? I thought: Metaprogramming, i.e. make julia generate code for me.

hence the function in the first post (which is just one of fis) should probably be like

 function create_automorphism(i,j)
    str = "v -> v[i]*v[j]"
    expr::Expr = parse(str)
    f::Function = eval(expr)
    return f
end

Now, when it should be somehow clear what I want to do, the question remains:
Why is eval ::Any and is this the way to go?


#9

Julia performs type inference based purely upon the types of the variables in your function. In that function, you take a string and evaluate it. That string could contain anything, including run(`rm -rf /`), so it could similarly return anything.

Just return the function directly. This returns a closure and holds on to i and j for you:

julia> function create_automorphism(i,j)
            return v->v[i]*v[j]
       end
create_automorphism (generic function with 1 method)

julia> f = create_automorphism(2,3)
(::#5) (generic function with 1 method)

julia> f(1:3)
6

#10

Thanks! for generators this should do;

If the user supplied me a different automorphism (with many operations to do) I should probably do something like this:

ex = :(GroupIdentity())
for (idx,power) in table
    ex = :($ex * v[$idx]^$power)
end
return eval(:(v -> $ex))

operating directly on the level of expressions this will save me parse operation.
eval is still there.

Probably I wanted to be too generic…


#11

Again, you don’t need to be using eval in this case.

julia> function create_automorphism(op, table)
            return v->(op * prod(v[idx]^power for (idx, power) in table))
       end

julia> f = create_automorphism(1, ((1,1), (2,2), (3,3)))
(::#5) (generic function with 1 method)

julia> f([.1,.2,.3])
0.00010800000000000002

julia> .1 * .2^2 * .3^3
0.00010800000000000002

This could have just as easily been create_automorphism(GroupIdentity(), table).


#12

@mbauman Thanks a lot!

So obvious when I look at it now…