elm-lang/core/3.0.0

NOTE: this package moved to elm/core

NOTE: this package is not compatible with Elm 0.19.1

Signal

A signal is a value that changes over time. For example, we can think of the mouse position as a pair of numbers that is changing over time, whenever the user moves the mouse.

Mouse.position : Signal (Int,Int)

Another signal is the Element or Html we want to show on screen.

main : Signal Html

As the Html changes, the user sees different things on screen automatically.

Some useful functions for working with time (e.g. setting FPS) and combining signals and time (e.g. timestamps) can be found in the Time library.

Signals

type Signal a

A value that changes over time. So a (Signal Int)) is an integer that is varying as time passes, perhaps representing the current window width of the browser. Every signal is updated at discrete moments in response to events in the world.

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 update provided by the left input signal wins (i.e., the update from the second signal is discarded).

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 a signal.

mouseIsUp : Signal Bool
mouseIsUp =
    map not Mouse.isDown


main : Signal Element
main =
    map Graphics.Element.show Mouse.position
map2 : (a -> b -> result) -> Signal a -> Signal b -> Signal result

Apply a function to the current value of two signals. The function is reevaluated whenever either signal changes. 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

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 state 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

filter : (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 =
    filter isEven 0 numbers
filterMap : (a -> Maybe b) -> b -> Signal a -> Signal b

Filter out some updates. When the filter function gives back Just a value, we send that value along. When it returns Nothing we drop it. If the initial value of the incoming signal turns into Nothing, we use the default value provided. The following example keeps only strings that can be read as integers.

userInput : Signal String


toInt : String -> Maybe Int


numbers : Signal Int
numbers =
    filterMap toInt 0 userInput
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 ...

The signal should not be a signal of functions, or a record that contains a function (you'll get a runtime error since functions cannot be equated).

sampleOn : Signal a -> Signal b -> Signal b

Sample from the second input every time an event occurs on the first input. For example, (sampleOn Mouse.clicks (Time.every Time.second)) will give the approximate time of the latest click.

Mailboxes

type alias Mailbox a =
{ address : Address a
, signal : Signal a
}

A Mailbox is a communication hub. It is made up of

* an Address that you can send messages to * a Signal of messages sent to the mailbox

type Address a

An Address points to a specific signal. It allows you to feed values into signals, so you can provide your own signal updates.

The primary use case is when a Task or UI element needs to talk back to the main part of your application.

mailbox : a -> Mailbox a

Create a mailbox you can send messages to. The primary use case is receiving updates from tasks and UI elements. The argument is a default value for the custom signal.

Note: Creating new signals is inherently impure, so (mailbox ()) and (mailbox ()) produce two different mailboxes.

type Message

A Message is like an envelope that you have not yet put in a mailbox. The address is filled out and the envelope is filled, but it will be sent at some point in the future.

message : Address a -> a -> Message

Create a message that may be sent to a Mailbox at a later time.

Most importantly, this lets us create APIs that can send values to ports without allowing people to run arbitrary tasks.

forwardTo : Address b -> (a -> b) -> Address a

Create a new address. This address will tag each message it receives and then forward it along to some other address.

type Action = Undo | Remove Int | NoOp


actions : Mailbox Action
actions = mailbox NoOp


removeAddress : Address Int
removeAddress =
    forwardTo actions.address Remove

In this case we have a general address that many people may send messages to. The new removeAddress tags all messages with the Remove tag before forwarding them along to the more general address. This means some parts of our application can know only about removeAddress and not care what other kinds of Actions are possible.

send : Address a -> a -> Task x ()

Send a message to an Address.

type Action = Undo | Remove Int


address : Address Action


requestUndo : Task x ()
requestUndo =
    send address Undo

The Signal associated with address will receive the Undo message and push it through the Elm program.

Constants

constant : a -> Signal a

Create a signal that never changes. This can be useful if you need to pass a combination of signals and normal values to a function:

map3 view Window.dimensions Mouse.position (constant initialModel)