我最近发布了一个关于>>
运算符的问题,因为即使我已经阅读了LYAH walk the linee部分,我仍然有一些理解上的差距。下面是我偶然发现的一些代码/MVE,它们引发了以下思考。为什么我可以得到代码后面的输出?如果bind操作符没有提供参数,那么str
参数就不能连接起来,否则它们将使用bind,如>>=
定义中所示,结果不应该与下面提到的预期结果相似:
import Control.Monad
import Data.List
data Value =
NoneVal
| IntVal Int
| ListVal [Value]
deriving (Eq, Show, Read)
type FName = String
data RError = EBadVar String | EBadFun String | EBadArg String
deriving (Eq, Show)
newtype Comp a = Comp {runComp :: [(String, Value)] -> (Either RError a, [String]) }
instance Monad Comp where
return a = Comp( \_ -> (Right a, []))
m >>= f = Comp(\env -> case runComp m env of
(Left e, str) -> (Left e, str)
(Right a, str) -> let (a', str') = runComp (f a) env in (a', str++str'))
-- You shouldn't need to modify these
instance Functor Comp where
fmap = liftM
instance Applicative Comp where
pure = return; (<*>) = ap
showValue :: Value -> String
showValue (IntVal int) = show int
showValue (ListVal values) = "[" ++ intercalate ", " [showValue x| x<-values] ++ "]"
output :: String -> Comp ()
output s = Comp (\_ -> (Right (), [s]))
-- Helper functions for interpreter
apply :: FName -> [Value] -> Comp Value
apply "print" values = output (unwords [showValue x| x<-values]) >> return NoneVal
字符串
输出量:
ghci> runComp (apply "print" [(IntVal 1), (IntVal 2)]) [("x", (IntVal 4)), ("y", (IntVal 3))]
(Right NoneVal,["1 2"])
型
预期产量
(Right NoneVal, [])
型
此外,为什么要用一个额外的str'
串联来扩展bind运算符定义:
(Left e, str) -> (Left e, str)
(Right a, str) -> let (a', str') = runComp (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> runComp (apply "print" [(IntVal 1), (IntVal 2)]) [("x", (IntVal 4)), ("y", (IntVal 3))]
(Right NoneVal,["1 2","1 2","1 2", "1 2"])
型
而不是实际的:
ghci> runComp (apply "print" [(IntVal 1), (IntVal 2)]) [("x", (IntVal 4)), ("y", (IntVal 3))]
(Right NoneVal,["1 2","1 2","1 2"])
型
只有3个输入元素。
1条答案
按热度按时间pvabu6sv1#
如果没有为bind操作符提供任何参数,那么
str
参数就不能连接起来,否则它们将使用bind,如>>=
定义中所示根据定义,
m >> n
是m >>= \_ -> n
,所以你可以通过将定义中的f
替换为常量函数\_ -> n
来了解它的作用:字符串
因此,唯一被忽略的是中间结果
a
。str
输出的生成与往常一样。(使用一些术语,我们可以说它是一元计算的effect的一部分,与从它获得的任何a
类型的结果相反。在输出复制绑定的示例中,您将得到三个而不是四个字符串,因为该绑定仅复制第二次计算的输出(
str'
,而不是str
)。(By顺便说一句,即使你只是为了说明而这样做,但值得注意的是,复制绑定是不合法的:
return a >>= f = f a
不会成立,因为return a >>= f
会有重复的输出,而f a
不会,而且重复的不对称性也会使结合律失效。