Here's my attempt:
A Signal is basically a sequence of values (usually triggered by external events like user input).
In fact, the core abstraction of Elm is that your app's state is updated in response to Signals and then that new state is re-rendered.
So let's say we have a Signal numbers
of type numbers : Signal Int
. For instance, let's say we have 10 buttons on the screen, each button has a number 1-10 on it, and our numbers
signal emits an integer each time a user clicks a button.
Signal.map
is a function that lets you transform a signal:
incrementedNumbers : Signal Int
incrementedNumbers = Signal.map (\n -> n + 1) numbers
view : Int -> Html
view num = div [] [ text ("Latest click + 1: " ++ (toString num)) ]
main : Signal Html
main =
Signal.map view incrementedNumbers
Our app will now render the value of the button the user clicked but incremented by 1.
But what if we wanted to instead render the sum of all numbers the user has clicked so far? We would need a way to transform a signal that maintains a variable that we can add something to and then return it. Just like how [1, 2, 3].reduce((n, sum) => n + 1, 0)
uses sum
to accumulate the sum of numbers as it iterates across the list.
This is what Signal.foldp
is for. It's just like that reduce sum snippet above except it's for Signals. It returns a Signal of the latest accumulated value.
currentSum : Signal Int
currentSum = Signal.foldp (\(currNum, sum)-> currNum + 1) 0 numbers
view : Int -> Html
view num = div [] [ text ("Sum so far: " ++ (toString num)) ]
main : Signal Html
main =
Signal.map view currentSum
foldp is basically how an Elm app works, except it's folding your update
function across a Signal of Actions. Your update function takes an action and your previous state and then returns an updated state.
type Action = Increment | Decrement
type alias Model =
{ num: Int }
initialModel : Model
initialModel = { num = 0 }
update : Action -> Model -> Model
update action model =
case action of
Increment -> { model | num = model.num + 1 }
Decrement -> { model | num = model.num - 1 }
latestAction : Signal Action
latestModel : Signal Model
latestModel = Signal.foldp update initialModel latestAction
viewModel : Model -> Html
main : Signal Html
main = Signal.map viewModel latestModel
That's pretty the core Elm abstraction