代码之家  ›  专栏  ›  技术社区  ›  Piskator

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

  •  0
  • Piskator  · 技术社区  · 2 年前

    我最近发布了一个关于 >> 操作员,因为即使我读过LYAH步行路线部分,我的理解仍有一些差距。以下是我偶然发现的一些代码/MVE,它引发了以下思考。为什么我可以得到代码后面的输出?难道没有向绑定运算符提供任何参数,因此没有 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' 连接到:

            (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个输入元素。

    0 回复  |  直到 2 年前
        1
  •  4
  •   duplode    2 年前

    难道没有向绑定运算符提供任何参数,因此没有 str 参数可以连接起来,否则它们将使用bind,如 >>= 释义

    不是。 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 与任何类型的结果相反 从中获得。)

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

    (顺便说一句,尽管你的意思只是为了说明,但值得注意的是,重复绑定是不合法的: return a >>= f = f a 无法保持,因为 return a >>= f 将有重复的输出,而 f a 不会,重复的不对称性也会使结合律失效。)

    推荐文章