Haskells 绑定运算符和 >> 运算符及其关系

Pis*_*tor 0 monads haskell functional-programming side-effects

我最近发布了一个关于>>操作员的问题,因为尽管我已经阅读了 LYAH walk the linee 部分,但我的理解仍然存在一些差距。下面是我偶然发现的一些代码/MVE,因为它们引发了以下思考。我怎么能得到代码后面的输出?是否不会出现没有向绑定运算符提供参数的情况,因此无法str连接任何参数,否则它们会使用>>=定义中所示的绑定,并且结果不应该与预期的不相似结果如下:

import Control.Monad
import Data.List

data Value =
    NoneVal
  | IntVal Int
  | ListVal [Value]
  deriving (Eq, Show, Read)

data RErr = EBVar String  | EBInt Int
  deriving (Eq, Show)

newtype Compuptaton a = Computation {runComputation :: [(String, Value)] -> (Either RErr a, [String]) }

instance Monad Computation where
  return a = Computation( \_ -> (Right a, []))
  m >>= f = Computation(\env -> case runComputation m env of
        (Left e, str) -> (Left e, str)
        (Right a, str) -> let (a', str') = runComputation (f a) env in (a', str++str'))

showValue :: Value -> String
showValue (IntVal int) = show int
showValue (ListVal values) = "[" ++ intercalate ", " [showValue x| x<-values] ++ "]"

output :: String -> Computation ()
output s = Computation (\_ -> (Right (), [s]))

apply :: String-> [Value] -> Computation Value
apply "print" values = output (unwords [showValue x| x<-values]) >> return NoneVal
Run Code Online (Sandbox Code Playgroud)

输出:

ghci> runComputation (apply "print" [(IntVal 1), (IntVal 2)]) [("x", (IntVal 4)), ("y", (IntVal 3))]
(Right NoneVal,["1 2"])
Run Code Online (Sandbox Code Playgroud)

预期产出

(Right NoneVal, [])
Run Code Online (Sandbox Code Playgroud)

此外,为什么要使用额外的串联来扩展绑定运算符定义str'

import Control.Monad
import Data.List

data Value =
    NoneVal
  | IntVal Int
  | ListVal [Value]
  deriving (Eq, Show, Read)

data RErr = EBVar String  | EBInt Int
  deriving (Eq, Show)

newtype Compuptaton a = Computation {runComputation :: [(String, Value)] -> (Either RErr a, [String]) }

instance Monad Computation where
  return a = Computation( \_ -> (Right a, []))
  m >>= f = Computation(\env -> case runComputation m env of
        (Left e, str) -> (Left e, str)
        (Right a, str) -> let (a', str') = runComputation (f a) env in (a', str++str'))

showValue :: Value -> String
showValue (IntVal int) = show int
showValue (ListVal values) = "[" ++ intercalate ", " [showValue x| x<-values] ++ "]"

output :: String -> Computation ()
output s = Computation (\_ -> (Right (), [s]))

apply :: String-> [Value] -> Computation Value
apply "print" values = output (unwords [showValue x| x<-values]) >> return NoneVal
Run Code Online (Sandbox Code Playgroud)

apply另一个>>这样的: apply "print" values = output (unwords [showValue x| x<-values]) >> output (unwords [showValue x| x<-values]) >> return NoneVal,不会导致以下结果:

ghci> runComputation (apply "print" [(IntVal 1), (IntVal 2)]) [("x", (IntVal 4)), ("y", (IntVal 3))]
(Right NoneVal,["1 2","1 2","1 2", "1 2"])
Run Code Online (Sandbox Code Playgroud)

而不是实际的:

ghci> runComputation (apply "print" [(IntVal 1), (IntVal 2)]) [("x", (IntVal 4)), ("y", (IntVal 3))]
(Right NoneVal,["1 2","1 2","1 2"])
Run Code Online (Sandbox Code Playgroud)

只有 3 个输入元素。

dup*_*ode 5

是否不会出现没有向绑定运算符提供任何参数的情况,因此str无法连接任何参数,否则它们将使用>>=定义中所示的绑定

并不真地。m >> n,根据定义,是,因此您可以通过在定义中m >>= \_ -> n替换为常量函数 来了解它的作用:f\_ -> n

m >> n = Comp(\env -> case runComp m env of
      (Left e, str) -> (Left e, str)
      (Right a, str) -> let (a', str') = runComp ((\_ -> n) a) env in (a', str++str'))

-- Applying (\_ -> n)
m >> n = Comp(\env -> case runComp m env of
      (Left e, str) -> (Left e, str)
      (Right _, str) -> let (a', str') = runComp n env in (a', str++str'))
Run Code Online (Sandbox Code Playgroud)

所以唯一被忽略的是中间结果a。输出的生成str如常进行。(用一点行话来说,我们可以说它是一元计算效果a的一部分,与从中获得的任何类型结果相反。)

在输出复制绑定示例中,您会得到三个而不是四个字符串,因为该绑定仅复制第二次计算的输出(str'但不复制str)。

(顺便说一句,即使您只是为了说明而这么说,但值得注意的是,重复绑定是不合法的:return a >>= f = f a不会成立,因为return a >>= f将有重复的输出,而f a不会,并且重复的不对称性也会使结合律失效。)