提问人:Piskator 提问时间:7/16/2023 最后编辑:Piskator 更新时间:11/11/2023 访问量:143
Haskells 绑定算子和>>算子及其关系
Haskells bind operator and the >> operator and their relation
问:
我最近发布了一个关于操作员的问题,因为即使我已经阅读了 LYAH walk the linee 部分,我的理解仍然存在一些差距。以下是我偶然发现的一些代码/MVE,从那时起,它引发了以下思考。为什么我可以得到代码后面的输出?难道不是没有向 Bind 运算符提供任何参数,因此没有参数可以连接,否则它们将使用 bind,如定义所示,并且其结果不应与下面提到的预期结果相似:>>
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 个输入元素。
答:
难道不是没有向 Bind 运算符提供任何参数的情况,因此没有参数可以连接起来,否则它们会使用 bind,如定义所示
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
a
在输出复制绑定示例中,您得到三个而不是四个字符串,因为该绑定仅复制第二个计算的输出(但不是 )。str'
str
(顺便说一句,即使你只是为了说明而故意这样做,但值得注意的是,复制绑定是不合法的:不会成立,因为会有重复的输出,而不会,重复的不对称性也会使关联定律失效。return a >>= f = f a
return a >>= f
f a
评论
Comp
Monad
Comp a = ReaderT [(String, Value)] (ExceptT RError (WriterT [String] a))
如果我没记错的话。