У меня есть тип с отслеживанием состояния с >>
и >> =
, что является почти монадой. Предполагаемое использование - создание кода для другого языка, и наличие доступной нотации будет очень уместным.
Однако нет четко определенной функции возврата, так как значения должны производиться только вместе с побочными эффектами . Итак, если я подделываю функцию возврата, функция возврата должна возвращать только ошибку и нарушает законы монад. (То есть никогда нельзя использовать функцию возврата.)
Я заметил, что в нотации do используются только два оператора: >>
и >> =
. .
Есть ли способ сохранить что-то вроде нотации do только для этих двух операторов или что-то столь же чистое, но без создания монады?
ПРИМЕЧАНИЕ: Ниже строки приведены детали, которые не являются необходимыми для вопрос в заголовке. Это контекст того, почему я задал этот вопрос, ответы на которые меня интересуют. Другие, просматривающие эту тему, вероятно, предпочтут проигнорировать ее.
ИСПРАВЛЕНИЕ: Я не генерирую код на императивном языке, хотя и не должен не имеет значения. Я генерирую код в Javascript.
УТОЧНЕНИЕ (в ответ на bdonlan): Мой тип - (ma), который передает состояние (то есть код, который должен быть сгенерирован при различных параметрах, аналогично монаде состояний) и будет вернуть значение (имена переменных в сгенерированном коде с прикрепленным типом haskell). Таким образом, не должно быть способа вернуть значения, связанные с именами переменных, без генерации кода, определяющего имена переменных. * Это исключительно из-за моего дизайна, который может быть хуже других, о которых я не думал.
В ответ на ответы: Кажется, есть два типа ответов на это. Во-первых, это действительно возможно и, возможно, лучший способ использовать нотацию do. Второй вопрос заключается в том, лучше ли использовать монаду и определять возврат (или имеет смысл вообще этого не делать - возможно, позже я обнаружу, что требуется возврат).
Пример того, что я буду делать:
data JsState = JsState { code :: String , vidCount :: Int }
-- vidCount is for generating unique variable names
newtype JsStmt = JsStmt ( JsState -> JsState )
newtype JsMonad a = JsMonad ( JsState -> ( a , JsState ) )
def :: (JsValue a) => a -> JsMonad a
-- Outputs "var " ++ toUniqueVarName vidCount ++ " = " toCode a ++ ";"
-- Returns JsMonad with the variable name as the value,
-- with a type attached, and the JsState's vidCount is incremented by 1.
alertJsInt :: JsIntE -> JsMonad ()
-- Outputs something like "alert(" ++ toCode JsIntE ++ ");"
do
x <- def $ JsIntL 2
y <- def $ JsIntL 4
alertJsInt (x + y)
Ну, структура, с которой вы работаете, уже существует; Соответствующий класс типов можно найти в Hackage , на самом деле. Тем не менее, я не рекомендую пытаться навязать это в Monad
по причинам, указанным bdonlan. Существование return
довольно фундаментально, и вы, вероятно, везде будете сталкиваться с проблемами, пытаясь использовать стандартные функции для Monad
с.
.... однако! Тем не менее, если вы действительно хотите do
нотацию, рассмотрите эту цитату из руководства пользователя GHC :
Нотация «Do» переводится с использованием любых функции (>> =), (>>) и сбой находятся в области видимости (не версии Prelude).
... другими словами, вы правы в том, что использование нотации do
имеет смысл, потому что нигде не используется return
при десагарации. Этот раздел о расширении RebindableSyntax
, которое делает именно то, что заявляет. Если вы не возражаете отказаться от нотации do
для Monad
, вы можете использовать это расширение, определить свои собственные функции и использовать нотацию do
все, что вам нравится.
Похоже, у вас есть стрелка . В монаде вы должны быть в состоянии написать:
do
x <- return $ f y
if x then something else notSomething
Условие «если» оценивается как часть оценки «делать», и в результате получается либо «что-то», либо «не так» , но не оба сразу. Однако для генерации кода вы, вероятно, захотите, чтобы «если» было преобразовано в ваш сгенерированный код с оценкой обеих ветвей, чтобы создать код, который может сделать выбор во время выполнения.
Эквивалентный код для стрелки приводит к использованию класса ArrowChoice, в котором у вас есть доступ как к условию, так и к обеим ветвям, что вам и нужно.