Як ніколи, термінологія, яку люди використовують, не є цілком послідовною. Існує безліч натхненних монадів, але строго кажучи - не зовсім понять. Термін "індексована монада" - це один із ряду (включаючи "монадіш" та "параметризовану монаду" (ім'я Аткі для них)) термінів, які використовуються для характеристики одного такого поняття. (Інше таке поняття, якщо вас цікавить, - це "параметричний монада ефекту" Кацумата, індексований моноїдом, де повернення індексується нейтрально, а зв'язок накопичується в його індексі.)
Перш за все, давайте перевіримо види.
IxMonad (m :: state -> state -> * -> *)
Тобто тип "обчислення" (або "дії", якщо ви хочете, але я буду дотримуватися "обчислення"), виглядає як
m before after value
де before, after :: stateі value :: *. Ідея полягає у захопі засобів для безпечної взаємодії із зовнішньою системою, яка має певне передбачуване поняття стану. Тип обчислень повідомляє вам про те, яким він повинен бути beforeстан, який afterвін буде працювати, і як (при регулярних монах над *), який тип valueобчислень виробляє.
Звичайні шматочки і шматки - так *само, як монада і - так stateсамо, як грають у доміно.
ireturn :: a -> m i i a -- returning a pure value preserves state
ibind :: m i j a -> -- we can go from i to j and get an a, thence
(a -> m j k b) -- we can go from j to k and get a b, therefore
-> m i k b -- we can indeed go from i to k and get a b
Поняття "стрілка Клейслі" (функція, яка дає обчислення), таким чином, генерується
a -> m i j b -- values a in, b out; state transition i to j
і ми отримуємо склад
icomp :: IxMonad m => (b -> m j k c) -> (a -> m i j b) -> a -> m i k c
icomp f g = \ a -> ibind (g a) f
і, як завжди, закони саме це забезпечують ireturnі icompдають нам категорію
ireturn `icomp` g = g
f `icomp` ireturn = f
(f `icomp` g) `icomp` h = f `icomp` (g `icomp` h)
або, в комедії підробленої C / Java / що завгодно,
g(); skip = g()
skip; f() = f()
{g(); h()}; f() = h(); {g(); f()}
Навіщо турбуватися? Моделювати "правила" взаємодії. Наприклад, ви не можете вийняти DVD, якщо його немає в диску, і ви не можете вставити DVD у привід, якщо він вже є. Так
data DVDDrive :: Bool -> Bool -> * -> * where -- Bool is "drive full?"
DReturn :: a -> DVDDrive i i a
DInsert :: DVD -> -- you have a DVD
DVDDrive True k a -> -- you know how to continue full
DVDDrive False k a -- so you can insert from empty
DEject :: (DVD -> -- once you receive a DVD
DVDDrive False k a) -> -- you know how to continue empty
DVDDrive True k a -- so you can eject when full
instance IxMonad DVDDrive where -- put these methods where they need to go
ireturn = DReturn -- so this goes somewhere else
ibind (DReturn a) k = k a
ibind (DInsert dvd j) k = DInsert dvd (ibind j k)
ibind (DEject j) k = DEject j $ \ dvd -> ibind (j dvd) k
Маючи це на місці, ми можемо визначити "примітивні" команди
dInsert :: DVD -> DVDDrive False True ()
dInsert dvd = DInsert dvd $ DReturn ()
dEject :: DVDrive True False DVD
dEject = DEject $ \ dvd -> DReturn dvd
з якого збираються інші ireturnі ibind. Тепер я можу написати (запозичення do-нотація)
discSwap :: DVD -> DVDDrive True True DVD
discSwap dvd = do dvd' <- dEject; dInsert dvd ; ireturn dvd'
але не фізично неможливо
discSwap :: DVD -> DVDDrive True True DVD
discSwap dvd = do dInsert dvd; dEject -- ouch!
Крім того, можна визначити свої примітивні команди безпосередньо
data DVDCommand :: Bool -> Bool -> * -> * where
InsertC :: DVD -> DVDCommand False True ()
EjectC :: DVDCommand True False DVD
а потім створити загальний шаблон
data CommandIxMonad :: (state -> state -> * -> *) ->
state -> state -> * -> * where
CReturn :: a -> CommandIxMonad c i i a
(:?) :: c i j a -> (a -> CommandIxMonad c j k b) ->
CommandIxMonad c i k b
instance IxMonad (CommandIxMonad c) where
ireturn = CReturn
ibind (CReturn a) k = k a
ibind (c :? j) k = c :? \ a -> ibind (j a) k
Насправді ми сказали, що таке примітивні стрілки Клейслі (що таке «доміно»), а потім побудували над ними відповідне поняття «послідовності обчислень».
Зауважимо, що для кожної індексованої монади mдіагональ "без змін" m i iє монадою, але загалом m i jце не так. Більше того, значення не індексуються, а обчислення індексуються, тому індексована монада - це не просто звичайне уявлення про монаду, інстанційне для якоїсь іншої категорії.
Тепер ще раз подивіться на тип стрілки Клейслі
a -> m i j b
Ми знаємо, що ми повинні бути в стані, iщоб почати, і прогнозуємо, що будь-яке продовження розпочнеться з держави j. Ми багато знаємо про цю систему! Це не ризикована операція! Коли ми ставимо DVD у привід, він входить! DVD-накопичувач не може сказати, у якому стані знаходиться після кожної команди.
Але це взагалі не вірно при взаємодії зі світом. Іноді вам може знадобитися віддати трохи контролю і дозволити світу робити те, що йому подобається. Наприклад, якщо ви сервер, ви можете запропонувати своєму клієнту вибір, і стан вашого сеансу буде залежати від того, що він вибере. Операція "вибір вибору" сервера не визначає отриманий стан, але сервер повинен мати можливість продовжувати роботу. Це не "примітивна команда" у вищезгаданому сенсі, тому індексовані монади не є таким хорошим інструментом для моделювання непередбачуваного сценарію.
Який кращий інструмент?
type f :-> g = forall state. f state -> g state
class MonadIx (m :: (state -> *) -> (state -> *)) where
returnIx :: x :-> m x
flipBindIx :: (a :-> m b) -> (m a :-> m b) -- tidier than bindIx
Страшне печиво? Не дуже, з двох причин. По-перше, це більше схоже на те, що таке монада, тому що це монада, але над (state -> *), ніж чим *. По-друге, якщо подивитися на тип стрілки Клейслі,
a :-> m b = forall state. a state -> m b state
ви отримуєте тип обчислень з попередньою умовою a та постумовою b, як у логіці Доброго Старого Хора. Твердження в програмній логіці зайняли менше півстоліття, щоб перетнути листування Кері-Говарда і стати типом Хаскелла. Тип returnIxтвердження говорить, що "ви можете досягти будь-якої післязастереження, яка дотримується, просто не роблячи нічого", що є правилом Лоріка Лоара для "пропустити". Відповідним складом є правило логіки Хоара для ";".
Давайте закінчимо, подивившись на тип bindIx, включивши всі кількісні показники.
bindIx :: forall i. m a i -> (forall j. a j -> m b j) -> m b i
Вони forallмають протилежну полярність. Ми вибираємо початковий стан iі обчислення, яке може починатися з i, після постуслової умови a. Світ вибирає будь-який проміжний стан, який jйому сподобається, але він повинен дати нам докази того, що є посткондиціоном b, і з будь-якої такої держави ми можемо продовжувати bвлаштовувати. Отже, послідовно, ми можемо досягти умови bвід держави i. Відпустивши свою владу над станами "після", ми зможемо моделювати непередбачувані обчислення.
І те IxMonadй інше MonadIxє корисним. Обидві моделі обґрунтованості інтерактивних обчислень щодо мінливого стану, передбачувані та непередбачувані відповідно. Передбачуваність цінна, коли ти можеш її отримати, але непередбачуваність - це іноді факт життя. Сподіваємось, тоді ця відповідь дає деяку вказівку на те, що таке індексовані монади, передбачуючи, коли вони почнуть бути корисними, і коли вони зупиняться.
True/Falseзначень як аргументи типуDVDDrive? Це якесь розширення, чи булеві фактично тут типи?