没有单子符号吗:可能吗?

| 我有一个带状态值的类型,分别是
>>
>>=
运算符,几乎是单子。预期的用途是生成另一种语言的代码,并且使用do标记将是非常合适的。 但是,没有明确定义的返回函数,因为仅应将值与副作用一起产生。因此,如果我伪造一个return函数,则return函数应该只返回一个错误,这违反了monad法则。 (即,使用返回函数永远无效。) 我观察到的是,do标记仅使两个运算符sugar0ѭ和
>>=
运算符加糖。 有没有办法只为这两个运算符保留诸如do表示法之类的东西,或者像它一样干净但不构成monad? 注意:该行下方是标题中问题所不需要的详细信息。这就是为什么我问这个问题并回答我感兴趣的内容。浏览此主题的其他人可能更喜欢忽略它。 更正:我没有将代码生成为命令式语言,尽管没关系。我正在将代码生成为Javascript。 澄清(响应bdonlan):我的类型是(ma),它带有状态(该状态是要给定各种参数的代码,类似于状态monad)并返回一个值(生成的代码中的变量名称) ,并附有haskell类型)。因此,在没有生成定义变量名的代码的情况下,应该没有办法返回与变量名关联的值。 *这完全是由于我的设计,可能不如我未曾想到的其他设计。 回应:对此似乎有两种回应。首先,是否确实可行,也许是使用do表示法的最佳方法。第二个是关于使用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上找到相关的类型类。但是,由于bdonlan给出的原因,我不建议您尝试将其强制为
Monad
的实例。 ѭ6的存在是非常基本的,您可能会在尝试为
Monad
使用标准函数的任何地方遇到麻烦。 ....然而!就是说,如果您真的想要ѭ8表示法,请考虑使用GHC用户指南中的引号:   使用范围内的任何函数(>> =),(>>)和fail(不是Prelude版本)来翻译\“ Do \”表示法。 ...换句话说,您正确使用
do
表示法是正确的,因为在废止时,实际上没有地方使用
return
。该部分是关于“ 11”扩展名的,它确实符合其要求。如果您不介意为ѭ5放弃
do
表示法,则可以使用该扩展名,定义自己的功能,并随意使用
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) }
[当您使用两个标准单子的标准组合时,return和bind的明显定义不会违反单子法则。] 这是构建使用do-notation的嵌入式DSL的常见模式,例如,请参阅Andy Gill在Hackage上的Dot库。 Oleg Kiselyov还具有这种风格的XML库(CSXML-不在Hackage上)。 由于您不关心最终答案,因此通常会编写一个忽略该答案的“运行”功能:
runJS :: JSmonad a -> Output
runJS ma = post $ getJSMonad ma 0
  where
    post (_,_,w) = w
需要注意的一件事是输出代码只是一个\“ log \”-在构建它时您无法对其进行检查,因此这确实限制了您可以执行的某些操作。     
没有
return
,很难使用符号。考虑以下常见模式:
foo = do
    x <- bar
    y <- quux
    return $ x + y
没有返回就无法定义它。回想一下
>>=
的类型为
(>>=) :: Monad m => m a -> (a -> m b)
-它必须在monad内部返回一个值。因此,即使我们不加注释就写出了同样的问题,我们也会遇到:
foo = bar >>= \\x -> quux >>= \\y -> (???????) (x + y)
因此,为此使用do标记不是一个好主意。特别是,没有什么可以阻止编译器实现通过遵​​循monad定律(假定存在6)的转换引入6的方法。 而且,即使定义>> =也对您来说很困难。您将如何将其翻译成命令式语言?请记住>> =在其正确的参数上采用任意函数;您无法检查此函数以告知其可能执行的操作,因此无法将该函数的主体翻译成命令式语言。我想您可以将参数的类型限制为某种跟踪的数据类型,并尝试找出该函数的效果,但是现在这意味着该函数无法检查其参数。简而言之,它只是不能很好地工作。 您可能考虑的另一种方法是创建一个Monad,该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 \”评估的一部分进行评估,结果是\“ something \”或\“ notSomething \”,但不是一次执行。但是,对于代码生成,您可能希望将\“ if \”转换为生成的代码,同时评估两个分支以生成可以在运行时进行选择的代码。 箭头的等效代码可以减少使用ArrowChoice类的使用,在该类中您可以访问条件和两个分支,这是您需要的。     
        如果您可以使类型成为
Monad
的实例,即使缺少
return
,那么
do
表示法也会起作用。我已经完成了(例如BASIC),这是获得DSL命令式表示法的绝佳方法。     

要回复问题请先登录注册