From 8d283d8bc2b79a2156de2eb00e5959624a06981e Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Sat, 2 Jan 2021 10:27:44 -0600 Subject: [PATCH] Let `map` store ObserverFuncs & add precompile This addresses a comment made in https://github.com/JuliaGizmos/Observables.jl/pull/48#issuecomment-699608226 by implementing storage for the ObserverFunctions added by `map`. This also adds optional precompilation support for observables, precompiling the methods based on the `T` in `Observable{T}`. --- src/Observables.jl | 27 +++++++++++++++++++++++++-- test/runtests.jl | 3 +++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/Observables.jl b/src/Observables.jl index 7957ec0..fa0cd6a 100644 --- a/src/Observables.jl +++ b/src/Observables.jl @@ -22,6 +22,8 @@ Like a `Ref` but updates can be watched by adding a handler using `on`. mutable struct Observable{T} <: AbstractObservable{T} listeners::Vector{Any} val::T + inputs::Vector{Any} # for map!ed Observables + Observable{T}() where {T} = new{T}([]) Observable{T}(val) where {T} = new{T}([], val) # Construct an Observable{Any} without runtime dispatch @@ -58,6 +60,8 @@ function Base.getproperty(obs::Observable, field::Symbol) return getfield(obs, field) elseif field === :listeners return getfield(obs, field) + elseif field === :inputs + return getfield(obs, field) elseif field === :id return obsid(obs) else @@ -131,6 +135,20 @@ mutable struct ObserverFunction <: Function end end +Base.precompile(obsf::ObserverFunction) = precompile(obsf.f, (eltype(obsf.observable),)) +function Base.precompile(observable::Observable) + tf = true + T = eltype(observable) + for f in observable.listeners + precompile(f, (T,)) + end + if isdefined(observable, :inputs) + for obsf in observable.inputs + tf &= precompile(obsf) + end + end + return tf +end """ on(f, observable::AbstractObservable; weak = false) @@ -335,8 +353,13 @@ Updates `observable` with the result of calling `f` with values extracted from a `f` will be passed the values contained in the refs as the respective argument. All other objects in `args` are passed as-is. """ -function Base.map!(f, observable::AbstractObservable, os...) - onany(MapUpdater(f, observable), os...) +function Base.map!(f::F, observable::AbstractObservable, os...) where F + obsfuncs = onany(MapUpdater(f, observable), os...) + if !isdefined(observable, :inputs) + observable.inputs = obsfuncs + else + append!(observable.inputs, obsfuncs) + end return observable end diff --git a/test/runtests.jl b/test/runtests.jl index 4a62208..4f5dcae 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -51,6 +51,9 @@ end @test r2[] == 3 r1[] = 3 @test r2[] == 4 + + # Make sure `precompile` doesn't error + precompile(r1) end @testset "disconnect observerfuncs" begin