Вяз - Как изменить параметризацию одного сигнала на основе другого сигнала

Как я могу параметризовать один сигнал на основе другого сигнала? Например, предположим, что я хотел изменить fps в зависимости от положения мыши по оси x. Типы:

Mouse.x : Signal Int
fps     : number -> Signal Time

Как я мог заставить Вяза понять что-то вроде этого псевдокода:

fps (Mouse.x) : Signal Time

Очевидно, что lift не работает в этом случае. Я думаю, что результат будет Signal (Signal Time) (но я все еще новичок в Вязов).

Спасибо!

2 ответа

Решение

преамбула

fps Mouse.x

В результате возникает ошибка типа, для которой fps требуется Intне Signal Int,

lift fps Mouse.x : Signal (Signal Int)

Вы правы там. Как упоминает ответ CheatX, вы не можете использовать эти "вложенные сигналы" в Elm.

Ответ на ваш вопрос

Похоже, вы просите что-то, что еще не существует в стандартных библиотеках. Если я правильно понимаю ваш вопрос, вы бы хотели сигнал времени (или fps), время которого можно динамически изменять. Что-то вроде:

dynamicFps : Signal Int -> Signal Time

Использование встроенных функций, таких как lift не дает вам возможности построить такую ​​функцию самостоятельно из функции типа Int -> Signal Time,

Я думаю, что у вас есть три варианта здесь:

  1. Попросите добавить эту функцию в библиотеку времени в списке рассылки. (Инструкции запроса функции немного раздуты для запроса такой функции, поэтому вы можете пропустить то, что не применимо)
  2. Обходите проблему, используя Elm или JavaScript, используя порты для подключения к Elm.
  3. Найдите способ не нуждаться в динамически изменяющемся Time сигнал.

Я советую вариант 1. Вариант 3 печален, вы должны быть в состоянии к тому, что вы спросили в Elm. Вариант 2, возможно, не очень хорошая идея, если вы новичок в Elm. Вариант 1 - не большая работа, и люди в списке рассылки не кусаются;)

Чтобы уточнить вариант 2, если вы хотите пойти на это:

  1. Если вы указываете исходящий порт для Signal Int и входящий порт для Signal Time Вы можете написать свою собственную динамическую функцию времени в JavaScript. Смотрите http://elm-lang.org/learn/Ports.elm
  2. Если вы хотите сделать это из Elm, вам понадобится более уродливый хак:

    dynamicFps frames = 
      let start = (0,0)
          time  = every millisecond -- this strains your program enormously
          input = (,) <~ frames ~ time
          step (frameTime,now) (oldDelta,old) = 
            let delta = now - old
            in if (oldDelta,old) == (0,0)
                 then (frameTime,now) -- this is to skip the (0,0) start
                 else if delta * frameTime >= second
                        then (delta,now)
                        else (0,old)
      in dropIf ((==) 0) 0 <| fst <~ foldp step start input
    

    По сути, вы помните абсолютную временную метку, спрашиваете новое время так быстро, как только можете, и проверяете, достаточно ли времени между запомненным временем и сейчас достаточно большим, чтобы оно соответствовало желаемому таймфрейму. Если это так, вы отправляете эту дельту времени (fps дает дельты времени) и запоминаете сейчас как новую метку времени. Поскольку foldp отправляет все, что нужно запомнить, вы получаете и новую дельту, и новое время. Итак, используя fst <~ Вы держите только дельту. Но время ввода (вероятно) намного быстрее, чем тот период времени, который вы хотите, так что вы также получите много (0,old) от foldp, Вот почему существует dropIf ((==) 0),

Вложенные сигналы явно запрещены системой типов Элма [часть 3.2 этой статьи].

Насколько я понимаю FRP, вложенные сигналы полезны только тогда, когда предусмотрена какая-то польщенность (например, монадическая функция 'join'). И эту операцию трудно реализовать без сохранения всей истории сигналов.

Другие вопросы по тегам