В последнее время я пытаюсь использовать Haskell в некоторых моих реальных производственных системах. Система типов Haskell действительно предлагает мне большую помощь. Например, когда я понял, что мне нужна какая-то функция типа
f :: (Foldable t, Monad m) => ( a-> b -> m b) -> b -> t a -> m b
Есть на самом деле такие функции , как foldM
, foldlM
и foldrM
.
Однако, что действительно шокировало меня, так это определение этих функций, таких как:
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldr f' return xs z0
where f' x k z = f z x >>= k
поэтому функция f'
должна иметь тип:
f' :: a -> b -> b
как того требует foldr
, то b
должно быть добрым *-> m *
, чтобы все определение foldlM
могло иметь смысл.
Другой пример включает в себя определения liftA2
и<*>
(<*>) :: f (a -> b) -> f a -> f b
(<*>) = liftA2 id
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
liftA2 f x = (<*>) (fmap f x)
Я попробовал некоторые из моих собственных решений, прежде чем заглянуть в исходный код. Но разрыв настолько велик, что я не думаю, что смогу когда-нибудь придумать это решение, независимо от того, сколько строк кода я напишу в будущем.
Поэтому мой вопрос в том, какие знания или какая конкретная математическая отрасль необходима кому-либо для рассуждений на столь отвлеченном уровне.
Я знаю, что теория категорий может предложить некоторую помощь, и я давно слежу за этой замечательной лекцией и все еще работаю над ней.
источник
Ответы:
В общем, логика и т. Д. Я бы вообразил. Но вы также можете научиться этому, делая это. :) Со временем вы замечаете некоторые закономерности, подбираете некоторые хитрости.
Вот так
foldr
с дополнительным аргументом. Некоторые видят, что это складывается в функции, чтобы их можно было объединить с помощью.
иid
(что иногда действительно<=<
иreturn
),Некоторым легче понять это в более простых, синтаксических терминах, так как
поэтому, когда не
g
является строгим во втором аргументе,s
может служить состоянием, передаваемым слева, даже если мы сворачиваем справа, как один пример.источник
Так что лучший способ понять это - это сделать. Ниже приведена реализация
foldlM
использованияfoldl
вместоfoldr
. Это хорошее упражнение, попробуйте его и попробуйте позже найти решение, которое я бы предложил. В этом примере объясняются все причины, по которым я это сделал, которые могут отличаться от ваших и могут быть предвзятыми, поскольку я уже знал об использовании аккумулятора функций.Шаг 1 : Давайте попробуем написать
foldlM
с точки зренияfoldl
Здесь вы понимаете, что
f'
это чисто, и вам нужно извлечь результатf
для соответствия типа. Единственный способ «извлечь» монадическое значение - использовать>>=
оператор, но такой оператор должен быть перенесен сразу после его использования.Итак, в заключение: каждый раз, когда вы в конечном итоге, я хотел бы полностью развернуть эту монаду , просто сдаваться. Это не правильный путь
Шаг 2 : Давайте попробуем писать
foldlM
в терминах,foldl
но сначала используем[]
как складываемые, так как сопоставление с образцом легко (т.е. нам на самом деле не нужно использоватьfold
)Хорошо, это было легко. Давайте сравним определение с обычным
foldl
определением списковКруто!! они почти одинаковые. Тривиальный случай примерно такой же. Рекурсивный случай немного по- другому, вы хотели бы написать что - то подобное:
foldlM' f (f z0 x) xs
. Но это не компилируется, как в шаге 1, так что вы можете подумать, ОК, я не хочу применятьf
, просто чтобы провести такое вычисление и составить его>>=
. Я хотел бы написать что-то более похожее,foldlM' f (f z0 x >>=) xs
если бы это имело смысл ...Шаг 3 Поймите, что то, что вы хотите накапливать, является композицией функций, а не результатом. ( здесь я, вероятно, предвзятый факт, что я уже знал это, потому что Вы отправили это ).
По типу
initFunc
и использованию наших знаний из шага 2 (рекурсивное определение) мы можем вывести этоinitFunc = return
. Определениеf'
может быть завершено, зная, чтоf'
следует использоватьf
и>>=
.Как видите, это не так уж сложно сделать. Это требует практики, но я не профессиональный разработчик Haskell, и я мог бы сделать это сам, это вопрос практики
источник
Monad
случаев.Вам не нужно никаких специальных знаний по математике, чтобы написать такую функцию, как
foldM
. Я использую Haskell в производстве уже 4 года, и я также пытаюсь понять это определениеfoldM
. Но это в основном потому, что оно плохо написано. Пожалуйста, не воспринимайте это как личную ошибку, если вы не можете понять какой-то непонятный код. Вот более читаемая версияfoldlM
Эта функция все еще не самая простая. Главным образом потому, что он имеет нетипичное использование,
foldr
где промежуточный аккумулятор является функцией. Но вы можете увидеть несколько парней, которые делают такое определение более читабельным:where
(чтобы вы знали форму аргументов).Увидев такую функцию, вы можете выполнить технику эквационального рассуждения, чтобы пошагово расширить определение и посмотреть, как оно работает. Умение придумывать такие функции приходит с опытом. У меня нет сильных математических навыков, и эта функция не является типичной функцией Хаскеля. Но чем больше у вас практики, тем лучше становится :)
источник