Там я был, записывая функцию, которая принимает значение как вход, вызывает функцию на этом входе, и если результатом этого является Just x
, он должен возвращать x
; в противном случае он должен вернуть исходный ввод.
Другими словами, эта функция (которую я не знал, что вызывать):
foo :: (a -> Maybe a) -> a -> a
foo f x = fromMaybe x (f x)
Поскольку это похоже на функцию общего назначения, я задавался вопросом, не было ли это еще не определено, поэтому я спросил в Twitter и Крис Аллен ответил, что он ap fromMaybe
.
Это звучало многообещающе, поэтому я активировал GHCI и начал экспериментировать:
Prelude Control.Monad Data.Maybe> :type ap
ap :: Monad m => m (a -> b) -> m a -> m b
Prelude Control.Monad Data.Maybe> :type fromMaybe
fromMaybe :: a -> Maybe a -> a
Prelude Control.Monad Data.Maybe> :type ap fromMaybe
ap fromMaybe :: (b -> Maybe b) -> b -> b
Тип ap fromMaybe
, безусловно, выглядит правильно, и пара экспериментов, похоже, указывает на то, что он также имеет желаемое поведение.
Но как это работает?
Функция fromMaybe
кажется мне понятной, и, в отдельности, я думаю, что понимаю, что делает ap
- по крайней мере, в контексте Maybe
. Когда m
Maybe
, он имеет тип Maybe (a -> b) -> Maybe a -> Maybe b
.
Я не понимаю, как ap fromMaybe
даже компилируется. Для меня это выражение выглядит как частичное приложение, но, возможно, я ошибаюсь. Однако, если это так, я не понимаю, как совпадают типы.
Первый аргумент ap
- m (a -> b)
, но fromMaybe
имеет тип a -> Maybe a -> a
. Как это соотносится? Какой экземпляр Monad
компилятор заключает, что m
есть? Как fromMaybe
, который принимает два (карриных) аргумента, превращается в функцию, которая принимает один аргумент?
Может кто-нибудь помочь мне подключить точки?