elm-lang/core/1.0.0

NOTE: this package moved to elm/core

NOTE: this package is not compatible with Elm 0.19.1

Signal

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.

Merging

merge : Signal a -> Signal a -> Signal a

Merge two signals into one. This function is extremely useful for bringing together lots of different signals to feed into a foldp.

type Update = MouseMove (Int,Int) | TimeDelta Float


updates : Signal Update
updates =
    merge
        (map MouseMove Mouse.position)
        (map TimeDelta (fps 40))

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.

mergeMany : List (Signal a) -> Signal a

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
        ]

Mapping

map : (a -> result) -> Signal a -> Signal result

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.position
map2 : (a -> b -> result) -> Signal a -> Signal b -> Signal result

Apply 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.height
map3 : (a -> b -> c -> result) -> Signal a -> Signal b -> Signal c -> Signal result
(a -> b -> c -> d -> result)
-> Signal a
-> Signal b
-> Signal c
-> Signal d
-> Signal result
(a -> b -> c -> d -> e -> result)
-> Signal a
-> Signal b
-> Signal c
-> Signal d
-> Signal e
-> Signal result

Fancy Mapping

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

An alias for map. A prettier way to apply a function to the current value of a signal.

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

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.position

You 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.

Past-Dependence

foldp : (a -> state -> state) -> state -> Signal a -> Signal state

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.

Filters

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

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 numbers
dropIf : (a -> Bool) -> a -> Signal a -> Signal a

Filter 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 numbers
keepWhen : Signal Bool -> a -> Signal a -> Signal a

Keep 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.position
dropWhen : Signal Bool -> a -> Signal a -> Signal a

Drop 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.

dropRepeats : Signal a -> Signal a

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 ...
sampleOn : Signal a -> Signal b -> Signal b

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.

Channels

channel : a -> Channel a

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.

send : Channel a -> a -> Message

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" ]
subscribe : Channel a -> Signal a

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.

Constants

constant : a -> Signal a

Create a constant signal that never changes.