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

Make Select return arbitrary values instead of strings #3

Closed
karlwessel opened this issue May 11, 2020 · 6 comments · Fixed by #148
Closed

Make Select return arbitrary values instead of strings #3

karlwessel opened this issue May 11, 2020 · 6 comments · Fixed by #148
Labels
needs Pluto.jl feature Can't be done right now because something is missing from Pluto.jl itself

Comments

@karlwessel
Copy link
Contributor

I would like to use the Select element to select different matrices with different names in the select combobox. I want the name to be displayed in the combobox and the matrix bound to the variable I @bind the Select to. So

@bind M Select([M1 => "Diagonal Matrix", M2 => "Symmetric Matrix"])

This currently does not work since Select binds the keys (which currently have to be strings) of the passed dict to the variable and displays the values.

Can we just change Select to support arbitrary keys instead just Strings?

@fonsp
Copy link
Member

fonsp commented May 11, 2020

Unfortunately not :( but I agree that this would be very useful!

The @bind mechanism takes the latest value from JavaScript, and converts it to a Julia object using JSON serialisation (browser impl) and deserialisation (using JSON.jl). This means that it is currently not possible to have @bind return arbitrary Julia objects.

I have some ideas that would solve that issue, the main one being the possibility of giving a preprocessor function to @bind, which will be called to convert the JavaScript returned object to Julia. This would make it possible for Select to have arbitrary keys.

I am not yet sure how to expose this API. Options are:

  • check if the bound object has a preprocessor field, if so, run it, otherwise, use Base.identity
  • allow the returned JSON object to contain arbitrary Julia code to be executed. Probably bad for performance
  • (ab)use another function extension from Base, like Base.convert, or Base.peek with the returned (JSON) object as second argument

As you probably found yourself, the workaround is to create two cells:

@bind M_name Select(["M1" => "Diagonal Matrix", "M2" => "Symmetric Matrix"])
M = Dict("M1" => M1, "M2" => M2)[M_name]

@fonsp
Copy link
Member

fonsp commented May 11, 2020

Also, do you think that the Slider constructor should give a custom error message when trying to use non-strings, to clarify this limitation?

@karlwessel
Copy link
Contributor Author

I'm not very familiar with how Observables work and how exactly every variable (cell) in Pluto is an observable but shouldn't it be possible to have some observable that listens to the current string "returned" by select and which then itself changes its value according to a map from the string to an actual other observable.

So something along the lines of

macro bindselect(def, dict)
    quote
        local el = Select([x => x for x in keys($(esc(dict)))])
        global some_unique_identifier = Core.applicable(Base.peek, el) ? Base.peek(el) : missing
	global $(esc(def)) = dict[some_unique_identifier]
        el
    end
end

@fonsp
Copy link
Member

fonsp commented May 11, 2020

In response to the first sentence: Pluto doesn't use variables as the unit of reactivity, but cells - your variables are not observables in the traditional sense. For example, in these two cells:

for i in 1:4
    global a = i
end
dosomething(a)

the function dosomething only gets called once, with 4 as argument.

This problem cannot be solved by adding a new macro to your notebook, or to this package 😢. I could explain this in more detail if you like, but the gist is:

Setting a Bond from JS eventually runs this line. If you want a line of code to 'respond' to that change (like global $(esc(def)) = dict[some_unique_identifier] ), it has to be in a different cell, which gets evaluated automatically after the bound value is set. (Like the workaround from my previous comment.)

@karlwessel
Copy link
Contributor Author

Ok. That clarifies it a bit! Thank you.

Regarding your other question: maybe some description in the example notebook or the documentation of Slider together with the two-cell-pattern-solution would be good.

@fonsp
Copy link
Member

fonsp commented May 17, 2020

(note to self)

One option is to check for a two-argument overload for Base.get, i.e.

Base.get(x::MyType) = # intial value
Base.get(x::MyType, val) = # value returned to julia as function of val

this function is used as the preprocessor, with the JS-returned object as second argument.

EDIT: we changed Base.peek to Base.get

@fonsp fonsp added the needs Pluto.jl feature Can't be done right now because something is missing from Pluto.jl itself label Oct 5, 2020
@fonsp fonsp linked a pull request Nov 1, 2021 that will close this issue
@fonsp fonsp closed this as completed in #148 Nov 3, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs Pluto.jl feature Can't be done right now because something is missing from Pluto.jl itself
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants