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

问题描述 投票:0回答:1

我最近发布了一个关于

>>
运算符的问题,因为尽管我已经阅读了 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

输出:

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

预期产量

(Right NoneVal, [])

此外,为什么要使用额外的

str'
连接来扩展绑定运算符定义:

        (Left e, str) -> (Left e, str)
        (Right a, str) -> let (a', str') = runComputation (f a) env in (a', str++str'++str'))

以及

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"])

而不是实际的:

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

仅输入 3 个元素。

haskell functional-programming monads side-effects
1个回答
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'))

所以唯一被忽略的是中间结果

a
str
输出的生成如常发生。 (用一点行话来说,我们可以说它是一元计算的effect的一部分,与从中获得的
a
类型的任何结果形成对比。)

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

str'
,但不是
str
)。

(顺便说一句,即使您只是为了说明目的而这样做,但值得注意的是,重复绑定是不合法的:

return a >>= f = f a
不会成立,因为
return a >>= f
将具有重复的输出,而
f a
不会,而且重复的不对称性也会使结合律失效。)

© www.soinside.com 2019 - 2024. All rights reserved.