The library for general signal manipulation. Includes mapping, merging, filters, past-dependence, and helpers for handling inputs from the UI.
Some useful functions for working with time (e.g. setting FPS) and combining
signals and time (e.g. delaying updates, getting timestamps) can be found in
the Time library.
Merge many signals into one. This is useful when you are merging more than
two signals. When multiple updates come in at the same time, the left-most
update wins, just like with merge.
type Update = MouseMove (Int,Int) | TimeDelta Float | Click
updates : Signal Update
updates =
mergeMany
[ map MouseMove Mouse.position
, map TimeDelta (fps 40)
, map (always Click) Mouse.clicks
]Apply a function to the current value of a signal.
mouseIsUp : Signal Bool
mouseIsUp =
map not Mouse.isDown
main : Signal Element
main =
map toElement Mouse.positionApply a function to the current value of two signals. In the following
example, we figure out the aspectRatio of the window by combining the
current width and height.
ratio : Int -> Int -> Float
ratio width height =
toFloat width / toFloat height
aspectRatio : Signal Float
aspectRatio =
map2 ratio Window.width Window.heightAn alias for map. A prettier way to apply a function to the current value
of a signal.
Intended to be paired with the (<~) operator, this makes it possible for
many signals to flow into a function. Think of it as a fancy alias for mapN.
For example, the following declarations are equivalent:
main : Signal Element
main =
scene <~ Window.dimensions ~ Mouse.position
main : Signal ELement
main =
map2 scene Window.dimensions Mouse.positionYou can use this pattern for as many signals as you want by using (~) a bunch
of times, so you can go higher than map5 if you need to.
Create a past-dependent signal. Each update from the incoming signals will be used to step the stat forward. The outgoing signal represents the current state.
clickCount : Signal Int
clickCount =
foldp (\click total -> total + 1) 0 Mouse.clicks
timeSoFar : Signal Time
timeSoFar =
foldp (+) 0 (fps 40)So clickCount updates on each mouse click, incrementing by one. timeSoFar
is the time the program has been running, updated 40 times a second.
Filter out some updates. The given function decides whether we should keep an update. If no updates ever flow through, we use the default value provided. The following example only keeps even numbers and has an initial value of zero.
numbers : Signal Int
isEven : Int -> Bool
evens : Signal Int
evens =
keepIf isEven 0 numbersFilter out some updates. The given function decides whether we should drop an update. If we drop all updates, we use the default value provided. The following example drops all even numbers and has an initial value of one.
numbers : Signal Int
isEven : Int -> Bool
odds : Signal Int
odds =
dropIf isEven 1 numbersKeep updates when the first signal is true. You provide a default value just in case that signal is never true and no updates make it through. For example, here is how you would capture mouse drags.
dragPosition : Signal (Int,Int)
dragPosition =
keepWhen Mouse.isDown (0,0) Mouse.positionDrop events when the first signal is true. You provide a default value just in case that signal is always true and we drop all updates.
Drop updates that repeat the current value of the signal.
numbers : Signal Int
noDups : Signal Int
noDups =
dropRepeats numbers
-- numbers => 0 0 3 3 5 5 5 4 ...
-- noDups => 0 3 5 4 ...Sample from the second input every time an event occurs on the first input.
For example, (sampleOn clicks (every second)) will give the approximate time
of the latest click.
Create a signal channel that you can send messages to. To receive these
messages, subscribe to the channel and turn it into a normal signal. The
primary use case is receiving updates from UI elements such as buttons and
text fields. The argument is a default value for the custom signal.
Note: This is an inherently impure function, so (channel ())
and (channel ()) produce two different signals.
Create a Message that can be sent to an Channel with a handler like
Html.onclick or Html.onblur.
import Html
type Update = NoOp | Add Int | Remove Int
updates : Channel Update
updates = channel NoOp
addButton : Html.Html
addButton =
Html.button
[ onclick (send updates (Add 1)) ]
[ Html.text "Add 1" ]Receive all the messages sent to an Channel as a Signal. The following
example shows how you would set up a system that uses an Channel.
-- initialState : Model
-- type Update = NoOp | ...
-- step : Update -> Model -> Model
-- view : Channel Update -> Model -> Element
updates : Channel Update
updates = channel NoOp
main : Signal Element
main =
map
(view updates)
(foldp step initialState (subscribe updates))The updates channel appears twice in main because it serves as a bridge
between your view and your signals. In the view you send to it, and in signal
world you subscribe to it.
Create a constant signal that never changes.
Merge two signals into one. This function is extremely useful for bringing together lots of different signals to feed into a
foldp.If an update comes from either of the incoming signals, it updates the outgoing signal. If an update comes on both signals at the same time, the left update wins.