Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
152 views
in Technique[技术] by (71.8m points)

elm - Extract data from a signal

I have a signal like this: signal1 = Signal.constant {a=4, b=3, l = []}
How do I extract a data from the signal?
I have tried Signal.map (x -> x) signal1 but Signal.map returns another signal.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

If while programming in Elm you ever have questions like:

  • “how do I extract value from a signal?”
  • “how do I change this variable to another value?”
  • “where do I put foldp and what arguments should I pass into it?”

you should read The Elm Architecture tutorial and try to implement its code along the way. Seriously, any novice Elm programmer should diligently go through this tutorial from start to finish, as it will clear much of the confusion.

Nevertheless, I'll give a simplified summary — bear in mind I omit lots of details and complexities. If you've read the tutorial, you probably understand, how a typical Elm program is structured. There are 3 main parts in most Elm programs: model, update, and view.

Model contains initial values for all your program data, and their type definitions. Update takes an event and a model, and returns a modified model based on the event. View takes a model (at whatever stage the program currently is) and draws it on screen, in other words, returns an Element.

In a simple program, you can immediately render whatever your signals emit, without storing any intermediate state, just by applying Signal.map to a rendering function and a signal. Below, function show plays the role of your primitive view. You don't use any models or updates, because you immediately render whenever the mouse signal emits new event.

import Graphics.Element exposing (Element, show)
import Mouse
import Signal exposing (Signal, map)

main : Signal Element
main = map show Mouse.isDown

But if you want to maintain state between the events, you have to use foldp, either directly or through some higher-level abstraction. By state I mean your model, modified by successive applications of your update function. At any point in time your model is in a certain state. Your main would usually look something like this:

main = map view (foldp update model signal)

main function always has the type Signal Element — there are exceptions, but behind the scenes they are converted to Signal Element anyway. Whatever you do with your data, code, and functions, at some point you have to combine it all into something of type Signal Element, which will be the body of your main function.

A model is usually a record with many fields, any of which can be records too. An update usually has type Event -> Model -> Model, where Model is a type of your model and Event is whatever your final signal emits. A view usually has the type Model -> Element. (The type names don't have to be Event and Model, I use them as placeholders in this example).

You can't extract values from a Signal, this is deliberately impossible in Elm. Instead you lift a function into the Signal context, using Signal.map. All the manipulation with values emitted by signal is done within Signal context by functions that are lifted into it. Suppose you have a signal of type Signal Event. Then foldp update model signal will have type Signal Model, because foldp has the type:

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

If your view function has the type Model -> Element, then map view (foldp update model signal) will have the type Signal Element.

main is mandated to have the type Signal Element, therefore map view (foldp update model signal) can be the body of main.

import Graphics.Element exposing (Element, show)
import Mouse
import Signal exposing (Signal, foldp, map)

type alias Model = Int
model : Model
model = 0

update : () -> Model -> Model
update event model = model + 1

view : Model -> Element
view model = show model

main : Signal Element
main = map view (foldp update model Mouse.clicks)

Above is a very simple program that accumulates mouse clicks. We have a dummy event, and our model is just an integer. How does Signal.map function work? It has the type:

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

It takes an ordinary function that converts a value into another value, and takes a signal of the first value, to produce a signal of the second value. Suppose you have lots of various signals, they emit values corresponding to mouse clicks, key presses, timed events, HTML input fields, all kinds of stuff. You manipulate these signals any way you like, but at some point you merge them into one final signal, with type Signal Something (where Something corresponds to a complicated datatype, containing all input data you need for the program).

Because you must have a main function, at some point you will have to convert your final signal to Signal Element, so at some point you will have to map (lift) a function of type Something -> Element over Signal Something to get Signal Element. Why is it called lifting? Because of partial application these 2 type definitions of Signal.map are equivalent:

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

You lift an ordinary day-to-day function of type a -> b into the Signal context, so that it can work on signals of values instead of just values. Here is a more complicated example that counts both seconds and mouse clicks:

import Graphics.Element exposing (Element, show)
import Mouse
import Signal exposing (Signal, foldp, map, merge)
import Time exposing (Time, fps, inSeconds)

type alias Model = { clicks : Int, time : Int }
model : Model
model = { clicks=0, time=0 }

type Event = Seconds Int | Mouse ()
update : Event -> Model -> Model
update event model = case event of
  Seconds time -> { model | time <- model.time + time }
  Mouse () -> { model | clicks <- model.clicks + 1 }

view : Model -> Element
view model = show model

timeSignal = Seconds << round << inSeconds <~ fps 1
mouseSignal = map Mouse Mouse.clicks

signal : Signal Event
signal = merge timeSignal mouseSignal

main : Signal Element
main = map view (foldp update model signal)

Now, I hope, you have a basic understanding how Elm programs are structured and how to handle events emitted by signals and modify your data from event to event. Now you can extend the above program by adding more signals, making your model more complex, and making your view draw fancier output.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...