Подтвердить что ты не робот

Действительно ли операционная изоморфна свободной монаде?

Доказательства

В этом сообщении в блоге Tekmo заявляет, что мы можем доказать, что ExitSuccess завершает работу, потому что (я полагаю) он похож на функтор Const для этого конструктора (он не несет x, поэтому fmap ведет себя как Const).

С операционным пакетом Tekmo TeletypeF может быть переведено примерно так:

data TeletypeI a where
    PutStrLn :: String -> TeletypeI ()
    GetLine :: TeletypeI String
    ExitSuccess :: TeletypeI ()

Я читал, что операция изоморфна свободной монаде, но можем ли мы доказать, что ExitSuccess выходит? Мне кажется, что он испытывает ту же проблему, что и exitSuccess :: IO (), и, в частности, если мы должны были написать для него интерпретатор, нам нужно было бы написать его так, как будто он не вышел:

eval (ExitSuccess :>>= _) = exitSuccess

Сравните со свободной версией монады, которая не содержит шаблона шаблона:

run (Free ExitSuccess) = exitSuccess

Лень

В учебном пособии Практическое пособие по Монаде apfelmus упоминает недостаток:

Государственная монада, представленная в виде s → (a, s), может справиться с некоторой бесконечной такие программы, как

evalState (sequence . repeat . state $ \s -> (s,s+1)) 0

тогда как список инструкций не имеет никакой надежды когда-либо обрабатывать что, поскольку только последняя команда Return может возвращать значения.

Это верно и для свободных монадов?

4b9b3361

Ответ 1

(Позвольте мне выиграть главный приз, смело комбинируя предыдущие ответы. -))

Главное наблюдение заключается в следующем: докажите, что именно? Формулировка в терминах Free TeletypeF позволяет доказать следующее:

Каждый интерпретатор для программ типа Free TeletypeF a должен выйти, когда он встречает инструкцию ExitSuccess. Другими словами, мы автоматически получаем алгебраический закон

 interpret (exitSuccess >>= k) = interpret exitSuccess

Таким образом, монада Free фактически позволяет вам испечь определенные алгебраические законы в тип.

Напротив, операционный подход не ограничивает семантику ExitSuccess, не существует соответствующего алгебраического закона, который относится к каждому интерпретатору. Можно писать интерпретаторы, которые выходят, когда сталкиваются с этой инструкцией, но также можно писать интерпретаторы, которые этого не делают.

Конечно, вы можете доказать, что любой конкретный интерпретатор удовлетворяет закону путем проверки, например, потому что он использует шаблон шаблона подстановки. Sjoerd Visscher замечает, что вы также можете применить это в системе типов, изменив тип возврата ExitSuccess на Void. Однако это не работает для других законов, которые могут быть испечены во свободные монады, например, закон распределения для инструкции mplus.

Таким образом, в запутанном повороте событий рабочий подход более свободен, чем свободная монада, если вы интерпретируете "свободный" как "наименьшее количество алгебраических законов".

Это также означает, что эти типы данных не являются изоморфными. Однако они эквивалентны: каждый интерпретатор, написанный с помощью Free, может быть преобразован в интерпретатор, написанный с помощью Program и наоборот.

Лично я хотел бы привести все мои законы в переводчика, потому что существует множество законов, которые нельзя испечь в свободной монаде, и мне нравится иметь их всех в одном месте.

Ответ 2

Ответ да, но только если вы используете другой перевод TeletypeF:

data TeletypeI a where
    PutStrLn :: String -> TeletypeI ()
    GetLine :: TeletypeI String
    ExitSuccess :: TeletypeI Void

Аргумент TeletypeI - это то, что операция будет/должна предоставлять остальной программе. Это тип аргумента продолжения k в

eval (ExitSuccess :>>= k) = ...

Так как нет значений типа Void, мы можем быть уверены, что k никогда не будет вызван. (Как всегда мы должны здесь игнорировать undefined.)

Эквивалентный тип:

data TeletypeI a where
    PutStrLn :: String -> TeletypeI ()
    GetLine :: TeletypeI String
    ExitSuccess :: TeletypeI a

Теперь нам нужно предоставить значение k, которое соответствует любому типу, и мы тоже не можем этого сделать. Это может быть более практичным, так как singleton ExitSuccess теперь имеет гибкий тип Program TeletypeI a.

Аналогично, exitSuccess можно было бы установить, указав ему тип IO Void или IO a.

Ответ 3

Ответ: нет, вы не можете доказать, что операционный игнорирует остальную часть программы на exitSuccess. Контраст TeletypeI с TeletypeF, чтобы понять, почему. Я буду переписывать TeletypeF в нотации GADT для более простого сравнения

data TeletypeF x where                     | data TeletypeI x where
  PutStrLn :: String -> x  -> TeletypeF x  |   PutStrLn :: String -> TeletypeI ()
  GetLine :: (String -> x) -> TeletypeF x  |   GetLine :: TeletypeI String
  ExitSuccess ::              TeletypeF x  |   ExitSuccess :: TeletypeI ()

Используя TeletypeF, мы можем сразу создавать актуальные программы:

GetLine (\str -> PutStrLn (map toUpper str) ExitSuccess)

TeletypeI не имеет способа ссылаться на "остальную часть программы" таким же образом, что и TeletypeF.

-- TeletypeF:
GetLine (\str -> "rest of program" goes here)
-- or
PutStrLn someString ("rest of program" goes here)
-- or
ExitSuccess -- there is no "rest of program" slot provided

Так как TeletypeI не хватает этой информации "остальной части программы", вы больше не сможете получать какие-либо знания, когда сталкиваетесь с exitSuccess.

-- TeletypeI
PutStrLn someString -- no information about "rest of program"
-- or
GetLine -- no information about "rest of program"
-- or
ExitSuccess -- no information about "rest of program"

Что допустимо, поскольку "остальная часть программы" полностью соответствует типу Program, который ничего не знает о наборе команд, к которому он применяется. Он просто позволяет вам связывать инструкции вместе и завершать через Return.