簡體   English   中英

沒有單子的符號:可能嗎?

[英]Do notation without monads: possible?

我有一個有狀態類型的>>>>=運算符,幾乎是一個monad。 預期用途是為另一種語言生成代碼,並且具有可用的標記將是非常合適的。

但是,沒有明確定義的返回函數,因為值只應與副作用一起產生。 因此,如果我偽造一個返回函數,return函數應該只返回一個錯誤,並且它違反了monad定律。 (即使用返回函數永遠無效。)

我觀察到的是,do-notation只會使兩個運算符, >>>>=運算符。

有沒有辦法只為那些2個操作符保留像記號這樣的東西,或者像它一樣干凈的東西,但沒有制作monad?

注意:行下方是標題中不需要的詳細信息。 這就是為什么我問這個問題的背景,回答我感興趣的問題。瀏覽這個主題的其他人可能更願意忽略它。


更正:我沒有將代碼生成為命令式語言,盡管它無關緊要。 我正在為Javascript生成代碼。

澄清(響應bdonlan):我的類型是(ma),它帶有一個狀態(這是給定各種參數的代碼,類似於狀態monad)並將返回一個值(生成的代碼中的變量名稱) ,附有haskell類型)。 因此,不應該有一種方法來返回與變量名相關聯的值,而不會生成定義變量名的代碼。 *這完全是由於我的設計,可能不如我未想到的其他設計。

回應答復:似乎有兩種回應。 首先,是否確實是可能的,也許是使用符號的最佳方式。 第二個是關於使用monad是否更好,並定義一個返回(或者是否有意義不這樣做 - 也許在以后的某個時間點,我會發現需要返回)。

我要做的事情的例子:

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記號所有你喜歡。

慣用的解決方案是使用兩個monad Writer和State-Writer來累積代碼“String”和State來跟蹤變量計數器。

如果您不想使用monad變換器庫,可以將兩個monad組合成一個合並的monad:

-- There are better types to use rather than String...
type Output = String
type St  = Int

newtype JsMonad a = JSMonad { getJSMonad :: St -> (a, St, Output) }

[當你使用兩個標准monad的標准組合時,返回和綁定的明顯定義不會違反monad法則。]

這是構建使用該注釋的嵌入式DSL的一種常見模式,例如,請參閱Hackage上的Andy Gill的Dot庫。 Oleg Kiselyov也有這種風格的XML庫(CSXML - 不是Hackage)。

由於您不關心最終答案,因此通常會編寫一個忽略它的“運行”函數:

runJS :: JSmonad a -> Output
runJS ma = post $ getJSMonad ma 0
  where
    post (_,_,w) = w

需要注意的一點是,輸出代碼只是一個“日志” - 你在構建它時無法檢查它,所以這確實限制了你可以做的一些事情。

如果沒有return就會很難使用do notation。 考慮以下常見模式:

foo = do
    x <- bar
    y <- quux
    return $ x + y

如果沒有回報,就無法定義。 回想一下>>= has type (>>=) :: Monad m => ma -> (a -> mb) - 它必須在monad中返回一個值。 因此,即使我們在沒有注釋的情況下寫出來,我們也會遇到同樣的問題:

foo = bar >>= \x -> quux >>= \y -> (???????) (x + y)

因此,為此使用do-notation並不是一個好主意。 特別是,沒有什么可以阻止引入編譯器實現return用符合法律的單子(即假定存在轉換小號return )。

而且,即使定義>> =也很難。 您將如何將其翻譯成您的命令式語言? 請記住,>> =在其右邊的參數上采用任意函數; 您無法檢查此函數以告知它可能執行的操作,因此無法將此函數的主體轉換為命令式語言。 我想你可以將參數的類型約束為某種跟蹤數據類型並嘗試找出函數的效果 ,但現在這意味着函數無法檢查它的參數。 簡而言之,它不會很好地運作。

您可以考慮的另一種方法是創建一個Monad,它代表構建命令功能的過程。 例如,它可能看起來像這樣:

emitAddAndPrint varA varB = do
    varTmp <- allocateTempVariable
    emitOp (Add varTmp varA varB)
-- if you want to be fancy, emitOp (varTmp :=: varA :+: varB) or something
    emitCall "print" [varTmp]

聽起來你有一支箭 在monad你需要能夠寫:

do
   x <- return $ f y
   if x then something else notSomething

“if”條件作為“do”的評估的一部分進行評估,結果是“某事”或“不是某事”,但不能同時進行。 但是,對於代碼生成,您可能希望將“if”轉換為生成的代碼,並對兩個分支進行求值,以生成可在運行時進行選擇的代碼。

箭頭的等效代碼可以使用ArrowChoice類,您可以在其中訪問條件和兩個分支,這就是您需要的。

如果你可以讓你的類型的實例Monad ,即使缺少return那么do記號的作品。 我已經完成了它(例如BASIC ),這是獲得DSL命令式表示法的絕佳方式。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM