Есть ли интересные монады одиночного выстрела, кроме "Может быть" и "Любого"?
В языке программирования Монте, как и в его предшественнике E, существует синтаксис для продолжений, разделенных одиночным выстрелом, которые называются "выталкиватели" и являются продолжениями, которые эффективно используются только один раз в пределах синтаксической границы. Например, вот эжектор, который не называется:
escape ej { 42 }
И эжектор, который называется:
escape ej { ej(42); "After 10,000 years, I'm free!" }
Оба оценивают 42
, Мы также можем прикрепить действие к случаю, когда эжектор называется:
escape ej { ej(2) } catch result { result + 4 }
Это обо всем этом синтаксисе. Нетрудно представить, как это подражает Maybe
или же Either
, Я переписываю пример из Haskell Wiki на Maybe в идиоматический Монте:
def f(x, ej):
return if (x == 0) { ej() } else { x }
def g(x, ej):
return if (x == 100) { ej() } else { x }
def h(x, ej):
return g(f(x, ej), ej)
# Monte permits Unicode identifiers but not for free.
def ::"h´"(x, ej):
def n := f(x, ej)
return g(n, ej)
(Обратите внимание, как мы должны нести бремя прохождения ej
, Монте не хватает "программируемой точки с запятой" в нотации.)
Я не буду делать Either
, но это в значительной степени то же самое; возможность добавить catch
пункт обеспечивает необходимую дискриминацию типов. Известно, что продолжения с разделителями хорошо известны, поэтому можно создавать сложные инструменты:
def trying(f, g, ej):
return escape innerEj { f(innerEj) } catch _ { g(ej) }
Такие гаджеты используются, например, в Монте для создания рукописных комбинаторов синтаксического анализа. Итак, в "Матери всех монад" Дэн Пипони объясняет, что Cont
в определенном смысле очень Monad
на котором многие другие Monad
с можно построить. Мы можем попытаться сделать это и в Монте. Давайте использовать стиль Moggi для кодирования монад в объектно-ориентированных языках:
object identity:
to unit(x):
return x
# "bind" is a Monte keyword; we may still use it, but not for free.
# NB this is `x >>= f` in Haskell.
to "bind"(x, f):
return f(x)
И давайте закодировать обязательный помощник i
, чтобы понять, как это выглядит:
def i(m, x, ej):
return m."bind"(x, ej)
... Это не полезно. Это не похоже на хороший синтаксис.
# Haskell: runIdentity $ do { x <- return 32; return $ x // 4 }
escape ej { i(identity, i(identity, identity.unit(32), fn x { identity.unit(x // 4) }), ej) }
В будущем должны были быть классные роботы, а не это. Есть и другая проблема; давайте закодируем другой традиционный Monad
, List
:
object list:
to unit(x) { return [x] }
to "bind"(action, f):
var rv := []
for x in (action) { rv += f(x) }
return rv
И давайте сделаем традиционное декартово произведение. Во-первых, давайте сделаем прямые вычисления; вместо передачи продолжения мы просто свяжем напрямую, используя монаду списка:
▲> i(list, [1, 2, 3], fn x { i(list, [4, 5, 6], fn y { [x * y] }) })
Result: [4, 5, 6, 8, 10, 12, 12, 15, 18]
А теперь с эжектором:
▲> escape ej { i(list, i(list, [1, 2, 3], fn x { i(list, [4, 5, 6], fn y { [x * y] }) }), ej) }
Result: 4
Так! Это довольно интересно. Выполняется вычисление монады полного списка, но эжектор видит только первый элемент в списке. Я подозреваю, что, поскольку эжекторы являются составными, существует способ создать более сложную логическую монаду, которая лучше не вычисляет так много промежуточных результатов.
Мой вопрос заключается в следующем: когда мы преобразуем Maybe
а также Either
в идиоматическом Монте мы можем ясно видеть, что синтаксис эжектора применяется элегантно. Какие еще есть монады, у которых было бы интересное одиночное поведение, подобное этому? Не чувствуй себя ограниченным Хаскеллом; Монте нетипизирован, поэтому никто не будет избегать вас за монады, которые трудно набрать!