Извлечение данных из сигнала

У меня есть такой сигнал: signal1 = Signal.constant {a=4, b=3, l = []}
Как извлечь данные из сигнала?
Я пробовал Signal.map (\x -> x) signal1, но Signal.map возвращает другой сигнал.


person Darek Nędza    schedule 13.07.2015    source источник


Ответы (2)


Если при программировании в Elm у вас возникнут такие вопросы, как:

  • «Как извлечь значение из сигнала?»
  • "как изменить эту переменную на другое значение?"
  • "куда мне поместить foldp и какие аргументы я должен передать ему?"

вам следует прочитать руководство по The Elm Architecture и попытаться реализовать его код по пути. Серьезно, любой начинающий программист Elm должен старательно пройти это руководство от начала до конца, так как это прояснит большую часть путаницы.

Тем не менее, я дам упрощенное резюме — имейте в виду, что я опускаю множество деталей и сложностей. Если вы читали руководство, вы, вероятно, понимаете, как устроена типичная программа Elm. В большинстве программ Elm есть 3 основные части: модель, обновление и просмотр.

Модель содержит начальные значения для всех данных вашей программы и определения их типов. Update принимает событие и модель и возвращает измененную модель на основе события. 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. Почему это называется подъем? Из-за частичного применения эти два определения типа 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 и как обрабатывать события, генерируемые сигналами, и изменять данные от события к событию. Теперь вы можете расширить приведенную выше программу, добавив больше сигналов, усложнив модель и сделав вывод более привлекательным.

person Mirzhan Irkegulov    schedule 11.08.2015

Это преднамеренно почти невозможно, потому что вам это не нужно.

Почему? Ну, это может помочь взглянуть на одну возможную сигнатуру для main в приложении Elm:

main : Signal Element

Здесь мы объявляем, что тип нашей программы — Сигнал Элемента; это означает, что наша программа является Элементом, который изменяется во времени. Среда выполнения Elm разберется с битом «изменение со временем» для нас, если мы сообщим ему, какие сигналы нам важны (путем ссылки на них) и как соединить их вместе (используя карту, foldp и т. д.). .

Если вы пытаетесь получить доступ к внутреннему значению, чтобы отобразить его как часть вашего приложения, правильный путь — использовать эту основную подпись и позволить Elm выполнить развертывание сигнала.

Если вы просто хотите посмотреть значение во время выполнения (например, в журнале консоли), взгляните на:

http://package.elm-lang.org/packages/elm-lang/core/2.1.0/Debug

person grumpyjames    schedule 13.07.2015
comment
Но я использую модуль StartApp и не могу использовать сигналы внутри модели, не так ли? - person Darek Nędza; 13.07.2015
comment
Внутри вашей модели не должно быть Signal. Можете ли вы предоставить код, показывающий, что вы пытаетесь сделать? - person robertjlooby; 14.07.2015