Search code examples
haskellfunctional-programmingmonadsside-effects

Haskells bind operator and the >> operator and their relation


I have recently posted a question about the >> operator, because even though I have read the LYAH walk the linee section I still have some gaps in my understanding. Below is some code/MVE that I have stumbled upon since which sparkled the following reflections. How come I can get the output that follows the code? will it not be the case that no arguments are provided to the bind operator, and hence that no str argument can be concatenated as they otherwise would have been using bind as seen in the >>= definition, and shouldn't the result therefor not be similar to expected result mentioned below:

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

Output:

ghci> runComputation (apply "print" [(IntVal 1), (IntVal 2)]) [("x", (IntVal 4)), ("y", (IntVal 3))]
(Right NoneVal,["1 2"])

Expected output

(Right NoneVal, [])

Furthermore why will extending the bind operator definition with an extra str' concatenation to this:

        (Left e, str) -> (Left e, str)
        (Right a, str) -> let (a', str') = runComputation (f a) env in (a', str++str'++str'))

and the apply with another >> like this: apply "print" values = output (unwords [showValue x| x<-values]) >> output (unwords [showValue x| x<-values]) >> return NoneVal , not result in the following:

ghci> runComputation (apply "print" [(IntVal 1), (IntVal 2)]) [("x", (IntVal 4)), ("y", (IntVal 3))]
(Right NoneVal,["1 2","1 2","1 2", "1 2"])

rather than the actual:

ghci> runComputation (apply "print" [(IntVal 1), (IntVal 2)]) [("x", (IntVal 4)), ("y", (IntVal 3))]
(Right NoneVal,["1 2","1 2","1 2"])

with only 3 inputted elements.


Solution

  • will it not be the case that no arguments are provided to the bind operator, and hence that no str argument can be concatenated as they otherwise would have been using bind as seen in the >>= definition

    Not really. m >> n, by definition, is m >>= \_ -> n, so you can see what it does by replacing f in your definition with the constant function \_ -> 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'))
    

    So the only thing being ignored is the intermediate result a. The generation of the str output happens as usual. (Using a bit of jargon, we might say it is part of the effect of the monadic computation, in contrast with any results of type a obtained from it.)

    On your output duplicating bind example, you get three rather than four strings because that bind only duplicates the output from the second computation (str' but not str).

    (By the way, even though you have only meant this for the sake of illustration, it's worth noting the duplicating bind isn't lawful: return a >>= f = f a won't hold, as return a >>= f will have duplicated output while f a won't, and the asymmetry of the duplication will also make the associativity law fail.)