Use of map

I am using Makie with text sliders, and I would like to understand a strange behavior of map. Consider the code:

using Makie
sradius, s_marker_radius = textslider((0.5f0:0.25f0:10f0), "Radius", start=2.f0)
map(s -> sin(s), s_marker_radius)
s(s_marker_radius)

s_marker_radius is of type Observable{Any}. Obviously, sin(s_marker_radius) gives an error:

ERROR: MethodError: no method matching sin(::Observable{Any})
Closest candidates are:
  sin(::Float16) at math.jl:1104
  sin(::Complex{Float16}) at math.jl:1105
  sin(::Missing) at math.jl:1157
  ...
Stacktrace:
 [1] top-level scope at none:0

However, the map statement produces no error. Why is that? Won’t the sin function be applied to an argument of type Observable, which should generate an error? And yet it does not.

I find that there is a method

map(f, observable::Observables.AbstractObservable, os...; init) 

so clearly, this explains why map does not crash. But it does not explain why the sin() function does not crash when applied to the Observable. It is as if to_value is applied surreptitiously in the background. That type of hidden behavior should be documented.
Thank you for any insight.

Hi,

From the Observables package documentation, you can read

map!(f, o::Observable, args...)

Updates `o` with the result of calling `f` with values extracted from args.
`args` may contain any number of `Observable` ojects.
`f` will be passed the values contained in the refs as the respective argument.
All other ojects in `args` are passed as-is.

And the code of the map function for observables:

function Base.map!(f, o::Observable, os...)
    onany(os...) do val...
        o[] = f(val...)
    end
    o
end

The map function then returns an observable with the result of the mapping function as its value.

To use the sin function, you can apply it on the value of the observable:

sin(o[])

but then this returns a number. So to get the result of the sin function as an observable, you can either create a new observable variable:
another_var = Observable(sin(o[]))

or update the value of your original observable:

o[] = sin(o[])

As a last tip, if you want, you can define the sin function for Observable types:

Base.sin(o::Observable) = sin(o[])

Hope I helped you!

1 Like

This helps very much. This should be enough to complete my current task.