Там я был, записывая функцию, которая принимает значение как вход, вызывает функцию на этом входе, и если результатом этого является 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, который принимает два (карриных) аргумента, превращается в функцию, которая принимает один аргумент?
Может кто-нибудь помочь мне подключить точки?