Skip to content
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

@lift macro? #10

Closed
stevengj opened this issue Jun 13, 2014 · 8 comments
Closed

@lift macro? #10

stevengj opened this issue Jun 13, 2014 · 8 comments

Comments

@stevengj
Copy link
Contributor

I find the lift(x -> foo(x), y) syntax a little awkward. What you really want to do is to be able to just write foo(y) and have it automatically create a Lift.

Doing that directly seems too hard, because it would involve overriding every conceivable function to have Signal variants. But something like this could be accomplished by a macro:

x = Input(3)
y = Input(7)
@lift x + y - 5*sin(x)

What the @lift macro would do would be to walk the expression tree, and any symbol that it finds corresponding to a defined value of type <: Signal would get converted into a function parameter in a call to lift.

It's a little tricky to get the metaprogramming right here, but I think it should be do-able and will make this a lot easier to use.

@shashi
Copy link
Member

shashi commented Jun 13, 2014

This is nice! Yes! I will work on this.

There is this idea of creating macros that feel like Mathematica's Manipulate.
e.g. @manipulate plot(sin([0:0.0001:2pi]*x)) x=Slider(440, 440, 880)
which would create a slider labeled "x" and connect it to x in the first expression.

cc @one-more-minute

@stevengj
Copy link
Contributor Author

With my suggestions, you could do:

x = Slider(...)
@lift plot(sin([0:0.001:2pi] * x))

and it should work.

@stevengj
Copy link
Contributor Author

Something like this should work, as a start:

# return a list of symbols in expr that are defined and of type T
function find_type(ex::Expr, T::Type)
    unique(find_type!(Symbol[], current_module(), ex, T))
end

find_type!(L::Vector{Symbol}, M::Module, ex, T::Type) = L
function find_type!(L::Vector{Symbol}, M::Module, ex::Symbol, T::Type)
    isdefined(M, ex) && isa(eval(M, ex), T) ? push!(L, ex) : L
end   
function find_type!(L::Vector{Symbol}, M::Module, ex::Expr, T::Type)
    if ex.head == :call
        for arg in ex.args
            find_type!(L, M, arg, T)
        end
    end
    L
end

macro lift(ex)
    S = find_type(ex, Signal)
    Expr(:call, :lift,
         Expr(:->, Expr(:tuple, S...), ex),
         S...)
end

@stevengj
Copy link
Contributor Author

Note that this only really works for interactive use because of the reliance on eval. Inside functions and modules you would still need to explicitly call lift. But I suppose that's not too bad.

@stevengj
Copy link
Contributor Author

One could potentially do even better: instead of just looking for Symbols, one could recursively (bottom-up) evaluate the expression tree and substitute the resulting value into tree. You would stop whenever you encounter a value of type Signal and store that in a temporary variable. Once the Signals are eliminated, you would wrap the remaining expression in a lift.

But this seems rather tricky to get right. A @lift macro that just recognized symbols, like above, seems like it would already be a big usability improvement.

@shashi
Copy link
Member

shashi commented Aug 8, 2014

@stevengj I do the bottom-up evaluation and try to find values to which a signal function is applicable. Then I replace these values with new symbols while maintaining a mapping of which symbol corresponds to which signal. Take a look at this: https://github.com/shashi/React.jl/blob/master/src/macros.jl

@shashi
Copy link
Member

shashi commented Aug 8, 2014

since we have the signal function defined for Interact widgets, this means we can simply use @lift with widgets:

s = Slider(0:0.01:1)
@lift s^2

works.

Also take a look at this simple @manipulate macro: https://github.com/shashi/Interact.jl/blob/master/src/manipulate.jl

@stevengj
Copy link
Contributor Author

stevengj commented Aug 8, 2014

That seems reasonable.

(As I mentioned above, anything that uses eval like this is a bit of hack, because it can't be used in e.g. a function. In Julia 0.4 we might think about replacing this with a staged function (JuliaLang/julia#7311), which is sort of like a macro but has access to the types, not just to the symbolic expressions.)

@stevengj stevengj closed this as completed Aug 8, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants