Извлечь данные из сигнала
У меня есть такой сигнал: signal1 = Signal.constant {a=4, b=3, l = []}
Как извлечь данные из сигнала?
я пытался Signal.map (\x -> x) signal1
но Signal.map
возвращает другой сигнал.
2 ответа
Если во время программирования в Elm у вас когда-нибудь возникнут вопросы
- "Как извлечь значение из сигнала?"
- "Как мне изменить эту переменную на другое значение?"
- "Куда я положу
foldp
и какие аргументы я должен передать в это?
Вы должны прочитать учебник The Elm Architecture и попытаться реализовать его код. Серьезно, любой начинающий программист вяза должен старательно пройти этот урок от начала и до конца, так как он многое избавит от путаницы.
Тем не менее, я приведу упрощенное резюме - имейте в виду, что я опускаю много деталей и сложностей. Если вы читали учебник, вы, вероятно, понимаете, как устроена типичная программа Elm. В большинстве программ Elm есть 3 основные части: модель, обновление и просмотр.
Модель содержит начальные значения для всех данных вашей программы и определения их типов. Обновление принимает событие и модель и возвращает измененную модель на основе события. View берет модель (на каком бы этапе программа в данный момент не находилась) и рисует ее на экране, другими словами, возвращает Element
,
В простой программе вы можете сразу визуализировать то, что излучают ваши сигналы, без сохранения промежуточного состояния, просто применяя Signal.map
к функции рендеринга и сигнала. Ниже, функция show
играет роль вашего примитивного взгляда. Вы не используете никаких моделей или обновлений, потому что вы немедленно визуализируете всякий раз, когда сигнал мыши генерирует новое событие.
import Graphics.Element exposing (Element, show)
import Mouse
import Signal exposing (Signal, map)
main : Signal Element
main = map show Mouse.isDown
Но если вы хотите поддерживать состояние между событиями, вы должны использовать foldp
либо напрямую, либо через некоторую абстракцию более высокого уровня. Под состоянием я подразумеваю вашу модель, измененную последовательными приложениями вашей функции обновления. В любой момент времени ваша модель находится в определенном состоянии. Ваш main
будет выглядеть примерно так:
main = map view (foldp update model signal)
main
функция всегда имеет тип Signal Element
- есть исключения, но за кадром они преобразуются в Signal Element
тем не мение. Что бы вы ни делали со своими данными, кодом и функциями, в какой-то момент вы должны объединить все это во что-то типа Signal Element
, который будет телом вашего main
функция.
Модель, как правило, представляет собой запись со многими полями, любое из которых также может быть записью. Обновление обычно имеет тип Event -> Model -> Model
, где Model
это тип вашей модели и Event
что бы ни испускал ваш последний сигнал. Вид обычно имеет тип Model -> Element
, (Имена типов не должны быть Event
а также Model
Я использую их в качестве заполнителей в этом примере).
Вы не можете извлечь значения из Сигнала, это намеренно невозможно в Elm. Вместо этого вы поднимаете функцию в Signal
контекст, используя Signal.map
, Все манипуляции со значениями, испускаемыми сигналом, выполняются в течение Signal
контекст с помощью функций, которые подняты в него. Предположим, у вас есть signal
типа Signal Event
, затем foldp update model signal
будет иметь тип Signal Model
, так как foldp
имеет тип:
foldp : (a -> state -> state) -> state -> Signal a -> Signal state
Если ваша функция просмотра имеет тип Model -> Element
, затем map view (foldp update model signal)
будет иметь тип Signal Element
,
main
уполномочен иметь тип Signal Element
, следовательно map view (foldp update model signal)
может быть телом 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)
Выше очень простая программа, которая накапливает щелчки мыши. У нас есть фиктивное событие, а наша модель - просто целое число. Как Signal.map
функция работы? Имеет тип:
map : (a -> b) -> Signal a -> Signal b
Для получения сигнала второго значения требуется обычная функция, которая преобразует значение в другое значение и принимает сигнал первого значения. Предположим, у вас много разных сигналов, они излучают значения, соответствующие щелчкам мыши, нажатию клавиш, синхронизированные события, поля ввода HTML, все виды вещей. Вы управляете этими сигналами так, как вам нравится, но в какой-то момент вы merge
их в один окончательный сигнал, с типом Signal Something
(где Something соответствует сложному типу данных, содержащему все входные данные, необходимые для программы).
Потому что вы должны иметь main
функция, в какой-то момент вам придется преобразовать ваш окончательный сигнал в Signal Element
, так что в какой-то момент вам придется сопоставить (поднять) функцию типа Something -> Element
над Signal Something
получить Signal Element
, Почему это называется лифтинг? Из-за частичного применения эти 2 определения типа Signal.map
эквивалентны:
map : (a -> b) -> Signal a -> Signal b
map : (a -> b) -> (Signal a -> Signal b)
Вы поднимаете обычную повседневную функцию типа a -> b
в Signal
контекст, так что он может работать на сигналах значений, а не только значения. Вот более сложный пример, который считает и секунды, и щелчки мыши:
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)
Теперь, я надеюсь, у вас есть общее представление о том, как структурированы программы Elm и как обрабатывать события, излучаемые сигналами, и изменять ваши данные от события к событию. Теперь вы можете расширить вышеуказанную программу, добавив больше сигналов, сделав вашу модель более сложной и сделав ваш взгляд более привлекательным.
Это намеренно почти невозможно, потому что вам это не нужно.
Зачем? Ну, это может помочь посмотреть на одну из возможных сигнатур main в приложении Elm:
main : Signal Element
Здесь мы заявляем, что тип нашей программы является Сигналом Элемента; это означает, что наша программа является элементом, который изменяется со временем. Среда исполнения Elm будет определять для нас бит "изменения во времени", при условии, что мы дадим ему знать, какие сигналы нам нужны (путем ссылки на них) и как их соединить (используя map, foldp и т. Д.),
Если вы пытаетесь получить доступ к внутреннему значению, чтобы отобразить его как часть вашего приложения - правильный путь - использовать эту основную подпись и позволить Elm развернуть Сигнал.
Если вы просто хотите посмотреть значение во время выполнения (например, в журнале консоли), взгляните на:
http://package.elm-lang.org/packages/elm-lang/core/2.1.0/Debug