-
Notifications
You must be signed in to change notification settings - Fork 27
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
Signal.map evaluations #16
Comments
So, there are two things here - First, I think the evaluation every time you fetch the value after a mapping can be cleaned up a bit. I'll try to update that. However, setting I'm going to try to make a change that will give it the following behavior: open Gjallarhorn
let x = Mutable.create 0
let f i = printf "processing"; i*i
let y = Signal.map f x // "processing"
printfn "x = %A" x.Value // nothing
printfn "y = %A" y.Value // nothing
printfn "y = %A" y.Value // nothing
x.Value <- 1 // nothing - by design
printfn "y = %A" y.Value // "processing" - gets value at first access, since there's no active subscription
printfn "y = %A" y.Value // nothing Note that having a subscription active would change the behavior - open Gjallarhorn
let x = Mutable.create 0
let f i = printf "processing"; i*i
let y = Signal.map f x // "processing"
use disp = Signal.Subscription.create ignore y // Add an active subscription - no-op in this case
printfn "x = %A" x.Value // nothing
printfn "y = %A" y.Value // nothing
printfn "y = %A" y.Value // nothing
x.Value <- 1 // "processing" - Subscription needs to update immediately
printfn "y = %A" y.Value // nothing
printfn "y = %A" y.Value // nothing This is "by design" because I want to allow Signal "data flow" to be setup safely without causing tons of evaluations. The evaluation is only needed when you actually try to read the value unless a subscription is in place (effectively triggering a side effect outside of the "system"). Let me know if this seems acceptable, and I'll try to push out a version with this change ASAP. |
Great. Thanks for the explanations and help with this. |
@ReedCopsey Not sure I understand why in this line: x.Value <- 1
printfn "y = %A" y.Value // "processingprocessing" (unexpected) such result. |
@charlesroddie and @FoggyFinder I think I have this working now - I have two new tests (to guarantee this doesn't "slip" in the future): [<Test>]
let ``Issue #16 - Signal.map evaluations - Without Subscription`` () =
use sw = new System.IO.StringWriter()
let x = Mutable.create 0
let f i =
sw.Write("*")
printfn "processing"
i*i
Assert.AreEqual("", sw.ToString())
let y = Signal.map f x // "processing"
Assert.AreEqual("*", sw.ToString())
printfn "x = %A" x.Value // nothing
printfn "y = %A" y.Value // nothing
printfn "y = %A" y.Value // nothing
printfn "Setting value"
x.Value <- 1 // nothing - by design
Assert.AreEqual("*", sw.ToString())
printfn "Before processing"
printfn "y = %A" y.Value // "processing" - gets value at first access, since there's no active subscription
Assert.AreEqual("**", sw.ToString())
printfn "y = %A" y.Value // nothing
Assert.AreEqual("**", sw.ToString())
[<Test>]
let ``Issue #16 - Signal.map evaluations - With Subscription`` () =
use sw = new System.IO.StringWriter()
let x = Mutable.create 0
let f i =
sw.Write("*")
printfn "processing"
i*i
Assert.AreEqual("", sw.ToString())
let y = Signal.map f x // "processing"
Assert.AreEqual("*", sw.ToString())
use disp = Signal.Subscription.create ignore y // Add an active subscription - no-op in this case
printfn "x = %A" x.Value // nothing
printfn "y = %A" y.Value // nothing
printfn "y = %A" y.Value // nothing
Assert.AreEqual("*", sw.ToString())
printfn "Setting value"
x.Value <- 1 // "processing" - Subscription needs to update immediately
Assert.AreEqual("**", sw.ToString())
printfn "After processing"
printfn "y = %A" y.Value // nothing
printfn "y = %A" y.Value // nothing
Assert.AreEqual("**", sw.ToString()) Both of these are now passing. As I mentioned, this is effectively exhibiting the behavior I thought would be possible here at the beginning. This still maintains "laziness" but also prevents extra evaluations from occurring, so it should be the best of both scenarios. Note that some operations (such as Signal.cache and Signal.merge) are not lazy - they really need to evaluate immediately, so they always update and evaluate fully. |
This changes it so dependencies are set immediately (instead of waiting for a subscription to happen), but changes aren't evaluated until fetched. A dirty flag is tracked in most operations so the Value can update when needed, but not more often than needed.
y = Signal.map f x, f is evaluated each time y's value is asked for.
When x is changed, y is not updated immediately.
The text was updated successfully, but these errors were encountered: