Ответ 1
Может ли кто-нибудь объяснить, как я не понимаю, как работают стрелки, и как они могут быть использованы? Есть ли фундаментальная философия для стрел, которые мне не хватает?
Мне кажется, что вы относитесь к этому Arrow
, как к Monad
. Я не знаю, считается ли это "фундаментальной философией", но между ними существует значительная разница, несмотря на то, как часто они пересекаются. В известном смысле ключевым элементом, определяющим a Monad
, является функция join
; как свернуть вложенную структуру в один слой. Они полезны из-за того, что позволяет join
: вы можете создавать новые монадические слои в рекурсивной функции, изменять структуру Functor
на основе ее содержимого и т.д. Но это не о Monad
s, поэтому мы оставим это на этом.
Суть Arrow
, с другой стороны, это обобщенная версия функции. Класс типа Category
определяет обобщенные версии состава функций и функции идентификации, а класс типа Arrow
определяет, как поднять регулярную функцию до Arrow
и как работать с Arrow
, которые принимают несколько аргументов (в форма кортежей - Arrows
не обязательно должна быть сделана!).
При объединении Arrow
базовым способом, как и в вашей первой функции countExample
, все, что вы действительно делаете, это нечто вроде сложной композиции функций. Оглянитесь на свое определение (.)
- вы берете две функции с состоянием и соединяете их в одну функцию с состоянием, при этом поведение изменения состояния обрабатывается автоматически.
Итак, главная проблема с вашим countExample
заключается в том, что он даже упоминает count'
и тому подобное. Это все сделано за кулисами, так же как вам не нужно явно передавать параметр состояния при использовании обозначения do
в монаде State
.
Теперь, поскольку нотация proc
просто позволяет вам построить большой составной Arrow
s, чтобы фактически использовать вашу функцию состояния, вам нужно будет работать вне синтаксиса Arrow
, как вам нужно runState
или такой чтобы фактически выполнить вычисление в монаде State
. Ваш второй countExample
находится вдоль этих строк, но слишком специализирован. В общем случае ваша функция состояния отображает поток входов в поток выходов, делая его конечным преобразователем состояния, поэтому runStatefulFunction
будет вероятно, возьмут ленивый список входных значений и преобразуют их в ленивый список выходных значений, используя правую скрость с помощью unSF
, чтобы поочередно подавать их на преобразователь.
Если вы хотите увидеть пример, пакет Arrows
включает трансформатор Arrow
Automaton
, который определяет что-то почти идентичный вашему StatefulFunction
, за исключением произвольного Arrow
вместо простой функции, которую вы использовали.
Oh и кратко пересмотреть связь между Arrow
и Monad
s:
Plain Arrows
- это только функции "первого порядка" . Как я уже говорил, они не всегда могут быть карри, и они не всегда могут "применяться" в том же смысле, что функция ($)
применяет функции. Если вы действительно хотите более высокого порядка Arrows
, класс типа ArrowApply
определяет приложение Arrow
. Это добавляет большую силу для Arrow
и, среди прочего, позволяет использовать ту же функцию "свернуть вложенную структуру" , что Monad
, что позволяет в целом определить экземпляр Monad
для любого экземпляра ArrowApply
.
В другом направлении, поскольку Monad
позволяет комбинировать функции, которые создают новую монадическую структуру, для любого Monad
m
вы можете говорить о "стрелке Клейсли" , которая является функцией типа a -> m b
. Стрелкам Kleisli для a Monad
может быть задан экземпляр Arrow
довольно очевидным образом.
Помимо ArrowApply
и стрелок Kleisli, нет особо интересных отношений между типами классов.