Haskell中>>运算符与>>=绑定算子的关联及输出疑问
关于Haskell中>>运算符的两个疑问
疑问一:>>连接output与return时为何输出列表非空?
代码示例
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 Computation 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, [])
解答
Haskell里>>是>>=的语法糖,定义为m >> n = m >>= \_ -> n。你的Computation Monad的>>=逻辑是:执行m得到结果(Right a, str)后,执行f a得到(a', str'),最终返回的字符串列表是str ++ str'。
在output s >> return NoneVal中:
m是output s,执行后得到(Right (), ["1 2"])n是return NoneVal,执行后得到(Right NoneVal, [])- 按照
>>=的逻辑,最终字符串列表是["1 2"] ++ [] = ["1 2"]
>>只是忽略m的返回值,但不会丢弃m产生的副作用(这里就是字符串列表的累积),所以输出列表非空。
疑问二:修改>>=定义后为何输出列表长度不符预期?
修改后的>>=定义
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'++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"])
解答
Haskell中>>是左结合运算符,a >> b >> c等价于(a >> b) >> c,对应两次>>=绑定,我们一步步拆解:
先计算
a >> b(a和b都是output s):- 执行
a得到(Right (), ["1 2"]) - 执行
b得到(Right (), ["1 2"]) - 按照修改后的
>>=逻辑,字符串列表为str ++ str' ++ str→["1 2"] ++ ["1 2"] ++ ["1 2"] = ["1 2", "1 2", "1 2"]
- 执行
再计算
(a>>b) >> c(c是return NoneVal):- 执行
(a>>b)得到(Right (), ["1 2", "1 2", "1 2"]) - 执行
c得到(Right NoneVal, []) - 若严格按照你修改后的
>>=逻辑,这里会拼接出["1 2","1 2","1 2"] ++ [] ++ ["1 2","1 2","1 2"],即6个元素,但你实际得到3个,说明你可能在修改>>=时出现笔误,或是误解了绑定的执行顺序。
- 执行
核心问题是你错误假设每个output都会被独立拼接两次,但实际上绑定的嵌套逻辑会将前一次绑定的结果作为整体参与下一次拼接,最终导致元素数量和预期不符。
内容的提问来源于stack exchange,提问作者Piskator
相关产品推荐
相关产品推荐

