Ответ 1
Определение, которое вы ищете, читает что-то вроде
Concurrent h >>= k = Concurrent (\f -> h (\x -> runConcurrent (k x) f))
Как мы туда попали? Как всегда, мы позволяем типам работать.:)
Введем сначала вспомогательную функцию:
runConcurrent :: Concurrent b -> (b -> Action) -> Action
runConcurrent (Concurrent h) = h
Если вы начинаете с левой стороны своего определения
Concurrent h >>= k = ...
с h :: (a -> Action) -> Action
и k :: a -> Concurrent b
, тогда ваша цель - заменить ...
выражением типа Concurrent b
, не так ли?
Как мы можем построить значение типа Concurrent b
? Один из способов - применить нашу функцию k
, но это не сработает, потому что у нас нет подходящего значения типа a
, доступного в качестве аргумента. Таким образом, в значительной степени единственное, что мы можем сделать, это применить конструктор данных Concurrent
, который имеет тип ((b -> Action) -> Action) -> Concurrent b
.
Это дает:
Concurrent h >>= k = Concurrent ...
Теперь нам нужно найти выражение типа (b -> Action) -> Action
для представления в качестве аргумента для Concurrent
. Мы знаем, что выражения типа функции всегда можно построить с помощью лямбда-абстракции:
Concurrent h >>= k = Concurrent (\f -> ...)
Это дает нам f :: b -> Action
и обязательство заменить ...
выражением типа Action
. Разумеется, использование одного из Action
-структоров будет обманывать;). Чтобы гарантировать типичность (>>=)
(точнее, чтобы убедиться, что мы в конечном итоге подчиняемся законам монады), мы рассматриваем Action
как абстрактный тип данных. Тогда единственным способом создания значения Action
является применение функции h
:
Concurrent h >>= k = Concurrent (\f -> h ...)
Следовательно, в дальнейшем нам нужно поставить h
аргументом типа a -> Action
. Это снова тип функции, поэтому мы выбрасываем другую лямбда:
Concurrent h >>= k = Concurrent (\f -> h (\x -> ...))
Следовательно, имеем x :: a
и нужно построить тело типа Action
. Что мы можем сделать со значением типа a
? Мы можем предоставить его функции k
. Это дает нам значение типа Concurrent b
, которое мы можем затем передать нашей вспомогательной функции runConcurrent
:
Concurrent h >>= k = Concurrent (\f -> h (\x -> runConcurrent (k x) ...))
Это дает нам функцию типа (b -> Action) -> Action
и поставку f
в качестве аргумента делает трюк:
Concurrent h >>= k = Concurrent (\f -> h (\x -> runConcurrent (k x) f))