Create signals based reactivitiy (like leptos.rs)
Opened this issue · 1 comments
Dale-Black commented
Can you see a potential path for being able to compile Function types using WASM? I am interested in creating a Signals based reactive frontend, similar to leptos.rs but both WasmCompiler.jl and WebAssemblyCompiler.jl do not allow for this
Something like:
# ╔═╡ daa402d1-b23d-499d-af9b-bee91c31a4ed
md"""
## Basic Signals
"""
# ╔═╡ dbe725ba-ef60-4e2e-b6e3-dcd1109f2f6a
begin
mutable struct Signal{T}
_value::T
subscribers::Vector{Function}
end
function Base.getproperty(s::Signal, name::Symbol)
if name === :value
return getfield(s, :_value)
else
return getfield(s, name)
end
end
function Base.setproperty!(s::Signal, name::Symbol, value)
if name === :value
setfield!(s, :_value, value)
notify(s)
else
setfield!(s, name, value)
end
end
end
# ╔═╡ 06d8be6e-bbf0-4fe1-8a1a-9515ee8945c2
begin
function notify(s::Signal)
for subscriber in s.subscribers
subscriber(s._value)
end
end
function subscribe(s::Signal, subscriber::Function)
push!(s.subscribers, subscriber)
end
function create_signal(initial_value)
return Signal(initial_value, Function[])
end
end
# ╔═╡ e3f50388-e8ce-4ee2-b7af-9660f1fb023a
md"""
This example demonstrates the reactivity of the signal. Whenever the value of my_signal is updated, the subscribed function is automatically called, printing the updated value. This showcases how the signal can be used to trigger reactions and update values in a reactive manner.
You can extend this example further by creating more complex subscribers or using the signal values to perform specific actions or update other parts of your Julia program.
"""
# ╔═╡ 9cfd3651-dff8-4274-aa7d-1e0db80661d5
begin
my_signal = create_signal("")
subscribe(my_signal, x -> println("Value changed to: $x"))
my_signal.value = "Hello, World!"
my_signal.value = "Signals are reactive!"
my_signal.value = "Updating the value again..."
end
But fails to compile like:
# ╔═╡ 269baa16-48c3-4941-8ae9-1ee1eecc0d30
function create_counter_signal()
return create_signal(0)
end
# ╔═╡ b0b14f0f-eee6-4a6f-8b91-bf5bcf9fc6f2
@code_wasm create_counter_signal()
With this error:
compiling
create_counter_signal(): ErrorException("type Function cannot be represented in wasm")
Stack trace
Here is what happened, the most recent locations are first:
emit_func!(ctx::WasmCompiler.CodegenContext, types::Type) @ [compiler.jl:1378](https://github.com/Pangoraw/WasmCompiler.jl/tree/6f38c79e6f6e50cc5b2e79e371c597bd085a0550//src/compiler.jl#L1349)
emit_func! @ compiler.jl:1347
emit_func(f::Function, types::Type; debug::Bool) @ [compiler.jl:1345](https://github.com/Pangoraw/WasmCompiler.jl/tree/6f38c79e6f6e50cc5b2e79e371c597bd085a0550//src/compiler.jl#L1345)
emit_func @ compiler.jl:1345
macro expansion @ [This cell: line 114](http://localhost:1234/edit?id=f145b294-4dca-11ef-0369-a38da8221f0d#b0b14f0f-eee6-4a6f-8b91-bf5bcf9fc6f2)
[This cell: line 1](http://localhost:1234/edit?id=f145b294-4dca-11ef-0369-a38da8221f0d#b0b14f0f-eee6-4a6f-8b91-bf5bcf9fc6f2)
[@code_wasm create_counter_signal()](http://localhost:1234/edit?id=f145b294-4dca-11ef-0369-a38da8221f0d#b0b14f0f-eee6-4a6f-8b91-bf5bcf9fc6f2)
Pangoraw commented
The issue is the same as the one mentioned in tshort/WebAssemblyCompiler.jl#17 (comment). Basically, the function signature is unknown and therefore it is uncallable/unrepresentable.
I can see two workaround here:
- to have a custom ABI for calling functions to support varargs and emit a wrapper which calls the specialized function signature similarly to the virtual calling convention in http://wingolog.org/archives/2023/03/20/a-world-to-win-webassembly-for-the-rest-of-us and use
call_indirect
onfunc (param i32) (result i32)
- write a specialized struct whose calling signature is known and emit
call_indirect
values accordingly. I played around having a specialwasmcall
intrinsic which allows users to inject custom wasm in the compiled module, here we would havecall_indirect
instead:
Lines 16 to 17 in 6f38c79
The remaining problem would be to know which functions can be called since we donot want to compile the entire Base.