Reputation: 30330
I want to create a filtered Signal
for mouse position. It should update when a mouse button is down, or the mouse transitions from "not down" to "down".
I came up with this function. It works, but using three anonymous functions doesn't seem right. Is there an idiomatic way to do this?
mouseDownPosition: Signal (Int, Int)
mouseDownPosition =
Signal.map2 (\(x, y) isDown -> (x, y, isDown)) Mouse.position Mouse.isDown
|> Signal.filter (\(x, y, isDown) -> isDown) (0, 0, False)
|> Signal.map (\(x, y, isDown) -> (x, y))
Upvotes: 1
Views: 192
Reputation: 5958
There is a utility library for signals called signal-extra (full disclosure: I'm the author of that library), which has a function for just this: keepWhen
: Signal Bool -> a -> Signal a -> Signal a
.
[...] filter that keeps events from the
Signal a
as long as theSignal Bool
is true.
If you're wondering, the implementation does almost the same as you wrote yourself, but there is a subtle difference!
keepWhen : Signal Bool -> a -> Signal a -> Signal a
keepWhen boolSig a aSig =
zip boolSig aSig
|> sampleOn aSig
|> keepIf fst (True, a)
|> map snd
The zip
is just a Signal.map2 (,)
to combine the two signals. The keepIf
is an old (IMHO clearer) name for Signal.filter
. So that's all basically the same. But after zipping the two signals together, there is a sampleOn
. That's there so the result of filtering is a signal that only updates when the original value signal aSig
updates, and doesn't update when the boolSig
updates.
There is another filter called sampleWhen
: Signal Bool -> a -> Signal a -> Signal a
, which has the description:
A combination of
Signal.sampleOn
andkeepWhen
. When the first signal becomesTrue
, the most recent value of the second signal will be propagated.
This alternative matches with the code that you wrote, but it doesn't match your description "Its value should change only when a mouse button is down".
Now it's up to you to figure out which behaviour you were really looking for :)
Upvotes: 2
Reputation: 2923
I like @joews solution better but just for the sake of an alternative, here is another version:
mouseDownPosition: Signal (Int, Int)
mouseDownPosition =
Signal.map2 (\p d -> if d then Just p else Nothing) Mouse.position Mouse.isDown
|> Signal.filterMap identity (0,0)
Upvotes: 2
Reputation: 30330
Whilst posting the question I worked out how to avoid anonymous functions. I have a feeling there is a more elegant solution, though.
mouseDownPosition: Signal (Int, Int)
mouseDownPosition =
Signal.map2 (,) Mouse.position Mouse.isDown
|> Signal.filter snd ((0, 0), False)
|> Signal.map fst
Upvotes: 1