Понимание стрелок в Haskell

Я пытался сжать стрелки, так как они - основание большинства реализаций FRP. Я думаю, что понимаю основную идею - они связаны с монадами, но хранят статическую информацию в каждом, связывают оператор, таким образом, можно идти через цепочку стрелок и посмотреть на статическую информацию, не имея необходимость оценивать целую стрелку.

Но я заблудился в точке, где мы начинаем обсуждать первый, второй, и подкачка. Что 2 кортежа имеют отношение к стрелкам? Учебные руководства представляют материал кортежа, как будто это был очевидный следующий шаг, но я действительно не вижу соединение.

В этом отношении, что синтаксис стрелки означает интуитивно?

54
задан nbro 7 April 2017 в 03:42
поделиться

1 ответ

Пожалуйста, загляните в http://www.cs.yale.edu/homes/hudak/ CS429F04 / AFPLectureNotes.pdf , в котором объясняется, как стрелки работают в FRP.

Кортежи из 2-х элементов используются при определении стрелок, поскольку они необходимы для представления функции со стрелками, принимающей 2 аргумента.

В FRP константы и переменные часто представлены в виде стрелок, которые игнорируют их «ввод», например

twelve, eleven :: Arrow f => f p Int
twelve = arr (const 12)
eleven = arr (const 11)

Приложения функций затем превращаются в композиции ( >>> ):

# (6-) 12

arr (6-) <<< twelve

Как теперь превратить функцию с двумя аргументами в стрелку? Например,

(+) :: Num a => a -> a -> a

из-за каррирования мы можем рассматривать это как функцию, возвращающую функцию. Итак,

arr (+) :: (Arrow f, Num a) => f a (a -> a)

теперь давайте применим его к константе

arr (+)             -- # f     a (a -> a)
  <<< twelve        -- # f b Int
                      :: f b     (Int -> Int)

+----------+      +-----+      +--------------+
| const 12 |----> | (+) |  ==  | const (+ 12) |
+----------+      +-----+      +--------------+

Эй, подождите, это не сработает. Результатом остается стрелка, возвращающая функцию, но мы ожидаем чего-то вроде f Int Int . Мы замечаем, что каррирование в Arrow не работает, потому что разрешена только композиция. Следовательно, мы должны распаковать сначала функцию

uncurry :: (a -> b -> c) -> ((a, b) -> c)

uncurry (+) :: Num a => (a, a) -> a

Затем у нас есть стрелка

(arr.uncurry) (+) :: (Num a, Arrow f) => f (a, a) a

Из-за этого возникает 2-кортеж. Затем для работы с этими кортежами из двух элементов необходимы функции группы, такие как &&& .

(&&&) :: f a b -> f a d -> f a (b, d)

, тогда добавление может быть выполнено правильно.

(arr.uncurry) (+)        -- # f   (a,    a) a
  <<<     twelve         -- # f b  Int
      &&& eleven         -- # f b      Int
                           :: f b           a

+--------+
|const 12|-----.
+--------+     |       +-----+      +----------+
              &&&====> | (+) |  ==  | const 23 |
+--------+     |       +-----+      +----------+
|const 11|-----'
+--------+

(Теперь, почему нам не нужны такие вещи, как &&&& для 3-кортежей для функций, имеющих 3 аргумента? Потому что a ((a, b), c) может быть используется вместо этого.)


Редактировать: Из оригинальной статьи Джона Хьюза Обобщение монад на стрелки , в нем указана причина как

4.1 Стрелки и пары

Однако, даже если в случае монад операторы return и >> = - все, что нам нужно, чтобы начать писать полезный код, для стрелок используются аналогичные операторы arr и >>> не достаточно. Даже простая монадическая функция сложения, которую мы видели ранее

  add :: Monad m => m Int -> m Int -> m Int
добавить x y = x >> = \ u -> (y >> = \ v -> return (u + v))

еще не может быть выражен в форме стрелки. Делая зависимость от ввода явной, мы видим, что аналогичное определение должно иметь форму

  add :: Arrow a => a b Int -> a b Int -> a b Int
добавить f g = ...

, где мы должны последовательно объединить f и g . Единственный доступный оператор последовательности - >>> , но f и g не имеют правильных типов для компоновки. Действительно, функция add должна сохранять ввод типа b при вычислении f , чтобы иметь возможность предоставлять тот же вход для g . Аналогичным образом результат f должен быть сохранен при вычислении g , чтобы два результата в конечном итоге можно было сложить вместе и вернуть.Комбинаторы стрелок, представленные до сих пор, не дают нам возможности сохранить значение в другом вычислении, поэтому у нас нет альтернативы, кроме как ввести другой комбинатор.

48
ответ дан 7 November 2019 в 08:10
поделиться
Другие вопросы по тегам:

Похожие вопросы: