我最近发布了一个关于
>>
运算符的问题,因为尽管我已经阅读了 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 个元素。
不会出现没有向绑定运算符提供参数的情况,因此无法连接任何
参数,否则它们会使用绑定,如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
不会,而且重复的不对称性也会使结合律失效。)