Coder Social home page Coder Social logo

observables.jl's People

Contributors

asinghvi17 avatar csertegt3 avatar femtocleaner[bot] avatar ffreyer avatar fingolfin avatar fredrikekre avatar github-actions[bot] avatar hgeorgako avatar jkrumbiegel avatar jobjob avatar juliatagbot avatar milesfrain avatar mortenpi avatar musm avatar pfitzseb avatar piever avatar ranocha avatar rasmushenningsson avatar sagnac avatar shashi avatar simondanisch avatar timholy avatar tkelman avatar twavv avatar yha avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

observables.jl's Issues

Add v0.5 changelog

This changelog is missing making it hard to figure out what changed between v0.4 and v0.5

Feature request: off(o::AbstractObservable) off(o::AbstractObservable, index)

Hi everyone,

I have a project where I need to remove listeners from many observables.
Sometimes it's about removing all, sometimes about removing all except the first one.

I don't want to keep track of all the observer functions so I wondered whether it was possible to remove the listeners by index.
Hence I came up with the follwing code.

export off!, nlistener

nlistener(@nospecialize(o::Observable)) = length(Observables.listeners(o))

function off!(@nospecialize(o::Observables.AbstractObservable), index::Union{AbstractRange{<:Integer}, Vector{<:Integer}})
  allunique(index) || (@info("All indices must be distinct"); return BitVector(zeros(len)))

  len = length(index)
  callbacks = Observables.listeners(o)
  success = BitVector(zeros(len))

  for (i, n) in enumerate(reverse(sort(index)))
    if 0 < n <= length(callbacks)
      for g in Observables.removehandler_callbacks
        g(observable, callbacks[n])
      end
      deleteat!(callbacks, n)
      success[len - i + 1] = true
    end
  end

  success
end

off!(@nospecialize(o::Observables.AbstractObservable), index::Integer) = off!(o, [index])[1]

off!(@nospecialize(o::Observables.AbstractObservable)) = off!(o, 1:length(Observables.listeners(o)))

Does that look like something you'd like to integrate in Observables?

If so, please let me know. I'd be happy to submit a PR.

Use |> operator!

I'm very excited to see this project!

Previously, I tried to implement Rx in julia. Then, I change my mind to implement additional functionality to Reactive.jl.

I plan to leverage the |> operator

result = from([1,2,3,4,5])
         |> map(x -> x + 1)
         |> subscribe()

but I'm busy recently...

0.4: map function doesn't support union types

map!(f, Observable(f(arg1[], map(to_value, args)...)), arg1, args...; update=false)

For context, I have code that returns different things based on the state of observable:

app = Canvas()
hbox(app, map(app) do img_matrix
        if img_matrix === nothing
            return html"<b>Draw something!</b>"
        end
        heatmap_digit(img_matrix; size=(300, 300))
    end
)

This causes issues™:

MethodError: Cannot `convert` an object of type Plots.Plot{Plots.GRBackend} to an object of type HTML{String}
Closest candidates are:
  convert(::Type{T}, ::T) where T at essentials.jl:171
  HTML{String}(::Any) where T at docs/utils.jl:23

I think it's the line referenced above that creates an Observable{T} where T is the type of the return value of the mapper function the first time it's executed.

CC @rajraomichigan

Trying to solve design issues with Observables2

I'd like some input on my redesign idea https://github.com/jkrumbiegel/Observables2.jl

I've noticed that in Makie, it gets quite problematic that observables don't tell you who they are listening to. That means you can't disconnect easily and that means often resources are never garbage collected until the whole Scene is destroyed.

I've written my reasoning down in the README and it would be great if those who are interested take a look.

Feature request: Option for `on` to also get previous value

Hi,

first time I thought Observables.jl are just the right thing for my task, however it seems they are not. I want to listen on updates of on object, however then I also want to compute more detailed update information. For this I would need the previous value.

As far I understood, I would need to create an Observable of a Pair (or Tuple) prev => current so that I can access the previous element.
It would be much nicer if the Observerable could just bear the current value and the prev will be passed to the ObserveFunction on an update.

Macro syntax consistency with AbstractPlotting

The "helper macros" here use the & syntax to denote observables that trigger an update, e.g. @map &x + &y (see docs). At the time, this was somewhat inspired by the ampersand operator in C, even though tbh I'm not able to give a consistent argument in favor of it at the moment.

OTOH, in AbstractPlotting, which uses a very similar macro, the observables that trigger an update are denoted by $, e.g. @lift $x + $y (see docs).

From what I understand, Observables is going towards a 1.0 release, so I thought it was relevant to figure out whether we want a unified syntax and, if it is Observables that should change, make the change before tagging 1.0.

type declaration wierdness

i believe this is a bug, but declaring types like this is not something i do often.

julia> using GLMakie

julia> x = Observable(1)
Observable(1)

julia> y::Observable{Union{Int,AbstractFloat}} = lift(x) do x
           x==1 ? 2 : NaN
       end
Observable(2)
    0 => (sc::Observables.SetindexCallback)(x) @ Observables ~/.julia/packages/Observables/YdEbO/src/Observables.jl:148

julia> y[]
2

julia> x[]=3    ### y can not be set to NaN via the lift
ERROR: InexactError: Int64(NaN)
Stacktrace:
  [1] Int64
    @ ./float.jl:912 [inlined]
  [2] convert(::Type{Int64}, x::Float64)
    @ Base ./number.jl:7
  [3] setproperty!(x::Observable{Int64}, f::Symbol, v::Float64)
    @ Base ./Base.jl:40
  [4] setindex!(observable::Observable, val::Any)
    @ Observables ~/.julia/packages/Observables/YdEbO/src/Observables.jl:122
  [5] (::Observables.MapCallback)(value::Any)
    @ Observables ~/.julia/packages/Observables/YdEbO/src/Observables.jl:436
  [6] #invokelatest#2
    @ ./essentials.jl:892 [inlined]
  [7] invokelatest
    @ ./essentials.jl:889 [inlined]
  [8] notify
    @ ~/.julia/packages/Observables/YdEbO/src/Observables.jl:206 [inlined]
  [9] setindex!(observable::Observable, val::Any)
    @ Observables ~/.julia/packages/Observables/YdEbO/src/Observables.jl:123
 [10] top-level scope
    @ REPL[7]:1

julia> y[]=NaN    ### but can be set to NaN directly
NaN

the workaround, which is more verbose, is:

julia> X = Observable(1)
Observable(1)

julia> Y = Observable{Union{Int,AbstractFloat}}(2)
Observable{Union{Int64, AbstractFloat}}(2)

julia> on(X) do X
       Y[] = X==1 ? 2 : NaN
       end
ObserverFunction defined at REPL[13]:2 operating on Observable(1)

julia> Y[]
2

julia> X[]=3
3

julia> Y[]
NaN

julia> X[]=1
1

julia> Y[]
2

let me know if i should have rather filed an issue with Makie.

julia> versioninfo()
Julia Version 1.10.2
Commit bd47eca2c8a (2024-03-01 10:14 UTC)
Build Info:
  Official https://julialang.org/ release
Platform Info:
  OS: macOS (arm64-apple-darwin22.4.0)
  CPU: 12 × Apple M2 Max
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-15.0.7 (ORCJIT, apple-m1)
Threads: 1 default, 0 interactive, 1 GC (on 8 virtual cores)
Environment:
  JULIA_PROJECT = @.
  JULIA_EDITOR = vi

(jl_PdQxLH) pkg> st
Status `/private/var/folders/s5/8d629n5d7nsf37f60_91wzr40000gq/T/jl_PdQxLH/Project.toml`
  [e9467ef8] GLMakie v0.9.9

Mapping observable leads to world age error

After running:

obs = Observable(0)
t = @task for n=1:10
    obs[] = n
    sleep(1)
end

schedule(t)
sleep(1)
map(x->2x, obs)
# This alternative works:
# f(x) = 2x
# map(f, obs)
julia> obs
Observable{Int64} with 1 listeners. Value:
2

julia> t
Task (failed) @0x0000000017b7e290
MethodError: no method matching (::var"#127#128")(::Int64)
The applicable method may be too new: running in world age 27995, while current world is 27996.
Closest candidates are:
  #127(::Any) at untitled-975cdec0428a7c38d7a195fd90bb4d35:16 (method too new to be called from this world context.)
[...]

(on 0.3.2, julia 1.5.2)

EDIT: looks like it's irrelevant the the function is anonymous. The issue also occurs with a named function, provided it's a new function (no applicable method existed when the task was compiled).

Why not foldp?

In Reactive there's foldp which is useful for accumulating state over updates (https://juliagizmos.github.io/Reactive.jl/#maintaining-state). I found it pretty useful. Right now I accomplish this with:

u = Observable(0)
oldu = Ref(u[])
p = Observable(0)
map!(p, u) do newu
  s = oldu[] + newu
  oldu[] = newu
  p[] + s
end

which seems a bit cumbersome considering what it could be:

p = foldp(+, u)

Confusing behavior on setindex! of a vector Observable

I find the following behavior a bit confusing (from Makie examples):

points = Observable(Point2f0[(0, 0), (0.5, 0.5), (1.0, 0.0)]) # AbstractPlotting renames Observables to Nodes it seems

later, in a function

          points[][idx[]] = pos
          points[] = points[]

The example does not work if the last line is excluded, even though it seems to be doing nothing. Is the issue that points[][idx[]] = pos somehow does not send a change signal?

Bug in v0.5.4 but not in v0.5.2: Binding freed GLBuffer{Vec{4, Float32}}

I have performed a really complicated plot using GraphMakie.jl and GLMakie.jl, then replot it when I change the underlying graph. It seems the following error is introduced by Observables.jl v0.5.4. When I fix the version of Observables.jl at v0.5.2, the error is gone. It seems the error is related to adjustlimits! of an existing axis.

julia> Error in callback:
Binding freed GLBuffer{Vec{4, Float32}}
Stacktrace:
   [1] error(s::String)
     @ Base ./error.jl:35
   [2] bind(buffer::GLMakie.GLAbstraction.GLBuffer{Vec{4, Float32}})
     @ GLMakie.GLAbstraction ~/.julia/packages/GLMakie/x68HK/src/GLAbstraction/GLBuffer.jl:24
   [3] gpu_setindex!
     @ ~/.julia/packages/GLMakie/x68HK/src/GLAbstraction/GLBuffer.jl:141 [inlined]
   [4] setindex!(A::GLMakie.GLAbstraction.GLBuffer{Vec{4, Float32}}, value::Vector{Vec{4, Float32}}, ranges::UnitRange{Int64})
     @ GLMakie.GLAbstraction ~/.julia/packages/GLMakie/x68HK/src/GLAbstraction/AbstractGPUArray.jl:51
   [5] update!
     @ ~/.julia/packages/GLMakie/x68HK/src/GLAbstraction/AbstractGPUArray.jl:73 [inlined]
   [6] (::GLMakie.GLAbstraction.var"#10#11"{GLMakie.GLAbstraction.GLBuffer{Vec{4, Float32}}})(x::Vector{Vec{4, Float32}})
     @ GLMakie.GLAbstraction ~/.julia/packages/GLMakie/x68HK/src/GLAbstraction/AbstractGPUArray.jl:196
   [7] #invokelatest#2
     @ ./essentials.jl:729 [inlined]
   [8] invokelatest
     @ ./essentials.jl:726 [inlined]
   [9] notify
     @ ~/.julia/packages/Observables/PHGQ8/src/Observables.jl:169 [inlined]
  [10] setindex!(observable::Observable, val::Any)
     @ Observables ~/.julia/packages/Observables/PHGQ8/src/Observables.jl:86
  [11] (::Observables.MapCallback)(value::Any)
     @ Observables ~/.julia/packages/Observables/PHGQ8/src/Observables.jl:431
--- the last 5 lines are repeated 1 more time ---
  [17] #invokelatest#2
     @ ./essentials.jl:729 [inlined]
  [18] invokelatest
     @ ./essentials.jl:726 [inlined]
  [19] notify
     @ ~/.julia/packages/Observables/PHGQ8/src/Observables.jl:169 [inlined]
  [20] setindex!(observable::Observable, val::Any)
     @ Observables ~/.julia/packages/Observables/PHGQ8/src/Observables.jl:86
  [21] (::Observables.SetindexCallback)(x::Any)
     @ Observables ~/.julia/packages/Observables/PHGQ8/src/Observables.jl:111
--- the last 5 lines are repeated 1 more time ---
  [27] #invokelatest#2
     @ ./essentials.jl:729 [inlined]
  [28] invokelatest
     @ ./essentials.jl:726 [inlined]
  [29] notify
     @ ~/.julia/packages/Observables/PHGQ8/src/Observables.jl:169 [inlined]
  [30] setindex!(observable::Observable, val::Any)
     @ Observables ~/.julia/packages/Observables/PHGQ8/src/Observables.jl:86
  [31] (::Observables.MapCallback)(value::Any)
     @ Observables ~/.julia/packages/Observables/PHGQ8/src/Observables.jl:431
--- the last 5 lines are repeated 4 more times ---
  [52] #invokelatest#2
     @ ./essentials.jl:729 [inlined]
  [53] invokelatest
     @ ./essentials.jl:726 [inlined]
  [54] notify
     @ ~/.julia/packages/Observables/PHGQ8/src/Observables.jl:169 [inlined]
  [55] setindex!(observable::Observable, val::Any)
     @ Observables ~/.julia/packages/Observables/PHGQ8/src/Observables.jl:86
  [56] adjustlimits!(la::Axis)
     @ Makie ~/.julia/packages/Makie/iY5BJ/src/makielayout/blocks/axis.jl:982
  [57] (::Makie.var"#1295#1325"{Axis})(pxa::GeometryBasics.HyperRectangle{2, Int64}, lims::GeometryBasics.HyperRectangle{2, Float32})
     @ Makie ~/.julia/packages/Makie/iY5BJ/src/makielayout/blocks/axis.jl:503
  [58] invokelatest(::Any, ::Any, ::Vararg{Any}; kwargs::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
     @ Base ./essentials.jl:729
  [59] invokelatest(::Any, ::Any, ::Vararg{Any})
     @ Base ./essentials.jl:726
  [60] (::Observables.OnAny)(value::Any)
     @ Observables ~/.julia/packages/Observables/PHGQ8/src/Observables.jl:415
  [61] #invokelatest#2
     @ ./essentials.jl:729 [inlined]
  [62] invokelatest
     @ ./essentials.jl:726 [inlined]
  [63] notify
     @ ~/.julia/packages/Observables/PHGQ8/src/Observables.jl:169 [inlined]
  [64] setindex!(observable::Observable, val::Any)
     @ Observables ~/.julia/packages/Observables/PHGQ8/src/Observables.jl:86
  [65] reset_limits!(ax::Axis; xauto::Bool, yauto::Bool, zauto::Bool)
     @ Makie ~/.julia/packages/Makie/iY5BJ/src/makielayout/blocks/axis.jl:628
  [66] reset_limits!
     @ ~/.julia/packages/Makie/iY5BJ/src/makielayout/blocks/axis.jl:552 [inlined]
  [67] plot!(la::Axis, P::Type{Combined{GraphMakie.graphplot}}, attributes::Attributes, args::PolymerArchitecture.BlockCopolymerGraph; kw_attributes::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
     @ Makie ~/.julia/packages/Makie/iY5BJ/src/makielayout/blocks/axis.jl:785
  [68] plot!
     @ ~/.julia/packages/Makie/iY5BJ/src/makielayout/blocks/axis.jl:765 [inlined]

Fix resource cleanups

In the discussion for #60, some outstanding issues in resource cleanup were identified. I was hoping to get to them before releasing 0.4, but I don't think that's going to happen. Filing this as a reminder to try to address these before 1.0.

Documentation not deployed

I started to revisit the problems of deploying the documentation, when I realised, that it has been already done. Although it is still not working.
Latest build shows a Permission denied error, and suggests that is may caused by a wrong DOCUMENTER_KEY.

Another problem could be, that every job tries to deploy the documentation. If all of them succeeds, there will be four (six?) identical copies of the same version?
Though I'm not sure that it is a real problem, the following travis script avoids this:

jobs:
   allow_failures:
     - julia: nightly
   include:
     - stage: Documentation
       julia: 1.0
       os: linux
       script:
         - julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd()));
                                               Pkg.instantiate()'
         - julia --project=docs/ docs/make.jl
       after_success: skip

I can open a PR for the second, but can't fix or test the first.

`map` document may introduce misunderstanding

"""
map(f, observable::AbstractObservable, args...)
Creates a new observable ref which contains the result of f applied to
values extracted from args
. The second argument observable must be an observable ref for dispatch reasons.
....
"""

The above italic text may introduce misunderstanding. Suggested correction will be:

applied to value contained in second argument observable and values extracted from following args

Using Observables inside Oxygen.jl app and/or HypertextLiteral.jl

I created a super simple app with Oxygen that has various routes. In this, I created an HTML page using HypertextLiteral.jl (HTL) and a counter using HTL and the <script> tag inside the @htl macro.

I now want to recreate this simple functionality using Observables.jl (and maybe WebIO or whatever) inside this Oxygen app. Is there a simple way to go about this? Here is the app.jl file and if someone could direct me on how to make a route like /counter but using Observables, that would be a helpful starting place!

using Oxygen
using HTTP
using HypertextLiteral

@get "/greet" function (req::HTTP.Request)
    return "hello world!"
end

@get "/multiply/{a}/{b}" function (req, a::Float64, b::Float64)
    return a * b
end

@get "/getdata" function ()
    return Dict("message" => "hello2!")
end

@get "/htl" function ()
    string(@htl """
        <h1>Hello test</h1>
        <p>This is a simple frontend using HypertextLiteral.jl.</p>
    """)
end

@get "/counter" function ()
    string(@htl """
        <h1>Counter</h1>
        <button onclick="incrementCounter()">Increment</button>
        <p>Counter: <span id="counter">0</span></p>
        <script>
            let counter = 0;
            function incrementCounter() {
                counter += 1;
                document.getElementById("counter").innerText = counter;
            }
        </script>
    """)
end

serve()

Removing a child observable

As far as I can tell there is no automatic way to remove a child observable and detach it from all its parents. For example

O1 = Observable(1)
O2 = map(identity, O1)
O2 = nothing
GC.gc()

will leave you with a listener in O1 and O2 will remain somewhere in memory. The same happens with weak = true - the mapped observable and the listeners remain. In these cases one can explicitly remove listener with off.(O2.inputs), which then allows them to be gced.

With on it's a bit different. There are no references being kept in inputs for on, so the above doesn't work. The gc will also happily remove the observable when it goes out of scope, causing this to error:

O1 = Observable(1)
O2 = Observable(1)
on(x -> O2[] = x, O1)
O2 = nothing
O1[] = 1

So in this case one would need to keep track of observer functions themselves to correctly remove a child. It would be nice if this was easier to handle.

Support for distributed operation?

Are there plans to support distributed Observables, cross-process? E.g. creating an observable on one process and listening to it from another process by automatically introducing a channel when the observable is serialized? Or is this beyond the intended scope of Observables.jl?

Identifiers

Add a String or Int id field to Observable which gives it a name, and also store each Observable in a global id to WeakRef{Observable} dict on construction.

This is required when working with objects across address spaces (like Julia <-> Browser :)).

MapUpdater doesn't accept AbstractObservables{T} other than Observables{T}

Currently, Observables throws an error when trying to map!(any, r, o) where o is a normal Observable and r is of a different observable type R{T} <: AbstractObservable{T}. The root cause is the definition of MapUpdate:

struct MapUpdater{F, T} <: Function
    f::F
    observable::Observable{T}
end

map!(), map() and connect!() all support AbstractObservables.
Probably, the only thing that would need to be adpated is to change Observable{T} to AbstractObservable{T}.

For my use case that worked, but I may be overlooking something.

ObservableOnWrite, ObservableOnChange

At present, your Observable values report on every assignment. There are plenty of use cases where that all that activity could be an encumbrance. A second serving of observing, one that reports on changed value (an assignment of a value that is not equal to the current value held) should be considered. One might use isapprox with Floats. The third kind would report on change of value which exceeds an initialization time safe range.

Use Int for ID instead of string


Should give quite a speed up compared to a string field - also it will be much clearer that it's an ID.
I also don't like to mix names and ids like this. It should either be a name or a unique id in my book ;)
We could have something like this, to have the best of both worlds:

const name_dict = Dict{Int, String}()
Observable(x, name = nothing) = (...; name != nothing && name_dict[id] = name)
name(x::Observable) = get(name_dict, x.id) do 
    error("Observable doesn't have a name. You may call it numer: $(x.id), though")
end

Could do a PR if this is appreciated

[Feature Reqest] Static Observable graph

Just from a slack discussion with @mschauer, which hopelessly nerd-sniped me, since I've always wanted static, no overhead observable graphs as well...
A possible implementation could be this:

struct StaticObservable{T, Listeners} <: Observables.AbstractObservable
    listeners::Listeners
    value::Base.RefValue{T}
end

Base.getindex(obs::StaticObservable) = obs.value[]
Observables.listeners(obs::StaticObservable) = obs.listeners

_notify(val, funcs::Tuple{}) where F = nothing
function _notify(val, funcs::Tuple)
    f = first(funcs)
    f(val)
    _notify(val, Base.tail(funcs))
end

function Base.notify(obs::StaticObservable)
    val = observable[]
    _notify(val, obs.listeners)
    return
end

function Base.setindex!(observable::StaticObservable, val)
    observable.val[] = val
    notify(observable)
    return val
end

The question is how to construct the graph... Since the current API relies on lots of dynamic behavior, the normal API can't be used to construct the observables.

I had the idea to use static_obs = make_static(observables...), but the Observable api doesn't really allow to walk over the graph and convert the nodes to StaticObservable... So is the only way to use something like SoonToBeStaticObservable,
which works like Observable but keeps more information about the graph, and can then be converted via make_static to a static graph?

async_latest can cause world age problems

ERROR (unhandled task failure): MethodError: no method matching (::##272#273)(::Array{Widgets.Widget{:slider},1})      
The applicable method may be too new: running in world age 24284, while current world is 24288.

I got this by running async_latest in this function where sliders are rebuilt when I swap to a different model, and they are remapped for sending back to the model.
I think the key issue is that map!(x, y, async_lates(z)) ocurrs within another map!().

function make_sliders(model)
    params = flatten(Vector, model)
    rnge = metaflatten(Vector, model, range)
    labels = metaflatten(Vector, model, fieldname_meta)

    sliders = broadcast((x,l,v) -> InteractBase.slider(x[1]:(x[2]-x[1])/200:x[2], label=string(l), value=v), rnge, labels, params)
    map!(make_plot, plt, model, async_latest.(observe.(sliders))...)
    sliders
end
map!(make_sliders, sliders, model)

Swapping async_latest to throttle resolves the issue, but thought I should document it here because it took a while to track down the problem.

Feature request: add priority filter for `notify()`

I've often come across the need to selectively trigger handlers of observables, e.g. only trigger the first one.
In the past I've worked around that with some weired constructions, but with the introduction of priorities this could be implemented quite easily and in a very clean way.

function Base.notify(@nospecialize(observable::AbstractObservable); priority::Union{Int, Function})
    val = observable[]
    for (p, f) in Observables.listeners(observable)::Vector{Pair{Int, Any}}
        (priority isa Int ? p == priority : priority(p)) || continue
        result = Base.invokelatest(f, val)
        if result isa Consume && result.x
            # stop calling callbacks if event got consumed
            return true
        end
    end
    return false
end

Example

using Observables

o = Observable(1)

on(o) do o
    println("Called with standard priority: $o")
end

on(o, priority = -1) do r
    println("Called with priority -1: $r")
end

notify(o, priority = -1);
# Called with priority -1: 1

notify(o, priority = 0);
# Called with standard priority: 1

notify(o, priority = <(1));
# Called with standard priority: 1
# Called with priority -1: 1

What do you think?

Feature request: a more general validity check for updates

I saw that version 5 introduced ignore_equal_values.

In the past I already thought it would be nice to have a general acceptance test for incoming updates.
So while version 5 is not officially released I propose to change the architecture to have a field isinvalid which accepts a function as validy check. In case of ignore_equal_values isinvalid would be set to isequal

I'll submit a draft PR how that could look like.

Docs are not deploying

From the CI log:

│ This can be caused by a DOCUMENTER_KEY variable that is not correctly set up.
│ Make sure that the environment variable is properly set up as a Base64-encoded string
│ of the SSH private key. You may need to re-generate the keys with DocumenterTools.
└ @ Documenter ~/.julia/packages/Documenter/pjwqp/src/Documenter.jl:546

To do list for Widget integration

To have a better integration with the Widgets package and ecosystem:

  • Define AbstractObservable interface (setindex!, getindex, listeners, setexcludinghandlers! I think)
  • Define flatten to turn Observable{Observable{T}} into Observable{T}
  • Define @map, @map!and @on macro as syntactic sugar: @map &a+2(&b) stands for map((x,y) -> x+2y, a, b)

TagBot trigger issue

This issue is used to trigger TagBot; feel free to unsubscribe.

If you haven't already, you should update your TagBot.yml to include issue comment triggers.
Please see this post on Discourse for instructions and more details.

If you'd like for me to do this for you, comment TagBot fix on this issue.
I'll open a PR within a few hours, please be patient!

precompilation of callback functions

I have difficulties in understanding what is compiled upon the first call of a callback function.
It seems that even if the callback function has been used before something is compiled upon the first notification.
MWE: with the definitions

using Observables

o = Observable("Hello")

function f(s::String)
    println(s)
end

I end up with the following timings

julia> @time f("Hello")
Hello
  0.000326 seconds (8 allocations: 160 bytes)

julia> on(f, o)
ObserverFunction `f` operating on Observable("Hello")

julia> @time notify(o);
Hello
  0.002647 seconds (22 allocations: 976 bytes, 88.23% compilation time)

julia> @time notify(o);
Hello
  0.000283 seconds (9 allocations: 208 bytes)

After redefinition of f, I have the same scenario. Calling f directly doesn't need any extra compilation (f is probably compiled directly after definition). But calling f on the observable needs extra compilation.

julia> function f(s::String)
           s = s * s
           println(s, " world!")
       end
f (generic function with 1 method)

julia> @time f("Hello")
HelloHello world!
  0.000377 seconds (10 allocations: 224 bytes)

julia> @time notify(o);
HelloHello world!
  0.003482 seconds (29 allocations: 1.312 KiB, 91.26% compilation time)

julia> @time notify(o);
HelloHello world!
  0.000332 seconds (12 allocations: 576 bytes)

Where does this compilation come from and could it be avoided? (Obviously calling notify() would be one way of forcing precompilation.)

Question: Would it make sense to make `val` a `Ref{T}`?

I'm looking at

mutable struct Observable{T} <: AbstractObservable{T}
    listeners::Vector{Any}
    val::T
    ...
end

and wondering if it would make sense to make that

struct Observable{T} <: AbstractObservable{T}
    listeners::Vector{Any}
    val::Ref{T}
    ...
end

so that Observable could be immutable and then

function Base.setindex!(observable::Observable, val)
    observable.val = val
    ...
end

becomes

function Base.setindex!(observable::Observable, val)
    observable.val[] = val
    ...
end

and

Base.getindex(observable::Observable) = observable.val

becomes

Base.getindex(observable::Observable) = observable.val[]

etc.

My brain hurts

I read some more and tried some stuff, but my brain still hurts 🤕... I don't understand the difference between Observables and Signals.

Could you guys improve/elaborate on the explanation for how Observables are different from Signals?

Thanks!

Suggestion: shorter name for `setexcludinghandlers!`

setexcludinghandlers! is a bit long. Something like the following might be better:

setsilent!
setquiet!
setmuted!

And just a note here that if/when this is chosen, the examples in the GtkObservables.jl docs need to be updated. Currently uses .value, which doesn't work because that's not the current field name.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.