> >= реализация для бедного человека Concurrency Монада

Привет, я пытаюсь реализовать "Бедный человек" Concurrency Monad. Вот мой код:

import Control.Monad

data Concurrent a = Concurrent ((a -> Action) -> Action)

data Action 
    = Atom (IO Action)
    | Fork Action Action
    | Stop

instance Monad Concurrent where
    (Concurrent ma) >>= f = Concurrent (\x -> ma(\y -> "Something return a Action")) 
    return x = Concurrent (\c -> c x)

Вот мой анализ: x имеет тип b, y имеет тип a, f имеет тип (a -> ((b ->Action) -> Action)). Чтобы выяснить "Что-то вернуть действие", я сначала вычисляю (f y), который возвращает тип ((b ->Action) -> Action). Затем, как можно использовать его с x для создания Action?

Ответ 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))