Apanatshka/elm-signal-extra/5.3.0

NOTE: this package is not compatible with Elm 0.19.1

Signal.Extra

Utility functions that aren't in the Signal module from elm-lang/core.

Flipped fancy map

(~>) : Signal a -> (a -> b) -> Signal b

The (<~) operator, but flipped. Doesn't play well with the other two!

Mouse.x ~> toFloat >> sqrt >> round
        >> isEven >> not
        >> asText

Zipping and unzipping

For those too lazy to write a record or union type.

zip : Signal a -> Signal b -> Signal ( a, b )

Zip two signals into a signal of pairs.

zip Mouse.x Mouse.y == Mouse.position
zip3 : Signal a -> Signal b -> Signal c -> Signal ( a, b, c )
zip4 : Signal a -> Signal b -> Signal c -> Signal d -> Signal ( a, b, c, d )
unzip : Signal ( a, b ) -> ( Signal a, Signal b )

Unzip a signal of pairs to a pair of signals.

unzip Mouse.position == (Mouse.x, Mouse.y)
unzip3 : Signal ( a, b, c ) -> ( Signal a, Signal b, Signal c )
Signal ( a, b, c, d )
-> ( Signal a, Signal b, Signal c, Signal d )

Stateful

foldp' : (a -> b -> b) -> (a -> b) -> Signal a -> Signal b

foldp' is slighty more general than foldp in that you can base the initial value of the state on the initial value of the input value.

foldp f b s == foldp' f (always b) s
foldps : (a -> s -> ( b, s )) -> ( b, s ) -> Signal a -> Signal b

Like foldp, but with a hidden state

foldp f b i ==
  let d a = (a,a) -- doubling function
  in foldps (\a b -> f a b |> d) (d b) i
foldps' : (a -> s -> ( b, s )) -> (a -> ( b, s )) -> Signal a -> Signal b

Like foldp', but with a hidden state

runBuffer : Int -> Signal a -> Signal (List a)

A running buffer of the given size (n) of the given signal. The list of at most n of the last values on the input signal. Starts with an empty list. Adds new values to the end of the list! So you get a list with time going from left to right.

((==) [1,2,3,4,5]) <~ runBuffer 5 (count (Time.every second))
runBuffer' : List a -> Int -> Signal a -> Signal (List a)

Same as runBuffer but with an initial buffer.

delayRound : b -> Signal b -> Signal b

Instead of delaying for some amount of time, delay for one round, where a round is initiated by outside event to the Elm program.
This may not be be very useful yet. Let the package author know if you find a good use!
Also known to delay in E-FRP.

Switching

switchWhen : Signal Bool -> Signal a -> Signal a -> Signal a

Switch between two signals. When the first signal is True, use the second signal, otherwise use the third.

switchSample : Signal Bool -> Signal a -> Signal a -> Signal a

Same as the previous, but samples the signal it switches to.

Filters

keepIf : (a -> Bool) -> a -> Signal a -> Signal a

The old name for Signal.filter, which doesn't confuse you with what the Bool value means.

keepWhen : Signal Bool -> a -> Signal a -> Signal a

The good old keepWhen filter that keeps events from the Signal a as long as the Signal Bool is true.

sampleWhen : Signal Bool -> a -> Signal a -> Signal a

A combination of Signal.sampleOn and keepWhen. When the first signal becomes True, the most recent value of the second signal will be propagated.
Before Elm 0.12 this was the standard behaviour of keepWhen.

keepThen : Signal Bool -> a -> Signal a -> Signal a

Like keepWhen, but when the filter signal turn False, the output changes back to the base value.

keepWhenI : Signal Bool -> Signal a -> Signal a

keepWhen but always keeps the initial value rather than trying to filter it.

filter : a -> Signal (Maybe a) -> Signal a

Filter a signal of optional values, discarding Nothings.

filterFold : (a -> b -> Maybe b) -> b -> Signal a -> Signal b

Apply a fold that may fail, ignore any non-changes.

Combining

fairMerge : (a -> a -> a) -> Signal a -> Signal a -> Signal a

A function that merges the events of two signals without bias (unlike Signal.merge). It takes a resolution function for the (usually rare) case that the signals update in the same "round".

fairMerge (\l r -> l) == merge
combine : List (Signal a) -> Signal (List a)

Combine a list of signals into a signal of lists. We have

  combine = mapMany identity

Also, whenever you are in a situation where you write something like

  Signal.map f (combine signals)

you are better off directly using mapMany f signals.

mapMany : (List a -> b) -> List (Signal a) -> Signal b

Apply a function to the current value of many signals. The function is reevaluated whenever any signal changes. A typical use case:

  mapMany (flow down) [sig_elem1, sig_elem2, sig_elem3]

Note how this is nicer (and more extendable) than the equivalent:

  Signal.map3 (\e1 e2 e3 -> flow down [e1, e2, e3]) sig_elem1 sig_elem2 sig_elem3

Also, mapMany List.maximum : List (Signal comparable) -> Signal (Maybe comparable) gives a signal that always carries Just the maximum value from all its input signals, unless the input signal list is empty, in which case the returned signal is equivalent to constant Nothing.

applyMany : Signal (List a -> b) -> List (Signal a) -> Signal b

Apply functions in a signal to the current value of many signals. The result is reevaluated whenever any signal changes.

passiveMap2 : (a -> b -> result) -> Signal a -> Signal b -> Signal result

Apply a function to the current value of two signals. The second signal is mapped passively -- that is, changes to the second signal do not force the function to re-evaluate. However, when the first signal changes, the function is re-evaluated with the current value of both signals.

This is equivalent to Signal.map2, except that Signal.map2 re-evaluatess the function when either Signal changes.

withPassive : Signal (a -> b) -> Signal a -> Signal b

Intended to be paired with Signal's (<~) operator, withPassive makes it possible for many signals to be passively mapped. For example, the following two declarations are equivalent:

main : Signal Element
main =
  scene <~ Mouse.position `withPassive` Window.dimensions


main : Signal Element
main =
  passiveMap2 scene Mouse.position Window.dimensions

You can use this pattern to passively map as many signals as you want, by using withPassive many times.

The function will only be re-evaluated when the signal mapped with (<~) changes. This is unlike the (otherwise equivalent) Signal (~) operator, since that operator re-evaluates the function whenever any of the input signals change.

If you want the function to be re-evaluated when some signals change but not others, then you can combine the Signal (~) operator and withPassive, putting (~) first. For instance:

main : Signal Element
main =
    scene <~ Mouse.position ~ Window.dimensions `withPassive` anotherSignal

In this example, the scene function will take three parameters, and will be called whenever either of the first two parameters changes. The third parameter will be the value of anotherSignal, but changes to anotherSignal will not cause the function to be re-evaluated.