0

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)

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

Output:

ghci> runComp (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') = runComp (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> runComp (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> runComp (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.

Piskator
  • 605
  • 1
  • 9
  • 4
    You know - these questions of yours are rather unattractive to read/answer, since there's quite a lot of code to go through which doesn't really seem to have anything with the monad operators that the question is ostensibly about. If you reduce your question to the actual minimum ([MCVE] as StackOverflow calls it) then it would be easier to answer, and perhaps you wouldn't even need to ask because it would already make it clearer to yourself. – leftaroundabout Jul 16 '23 at 11:54
  • @leftaroundabout , I am sorry to hear that you find it unattractive to read/answer. The MRE you mention is exactly what I have tried to create. I just called it MVE (minimal viable example), but I have tried to cut away all the code that I didn't think was completely necessary, and provided some output examples to make it clearer what the expected contra actual behaviour was. Such that it would be easier for someone else to read/answer. As stated before I am sorry to hear you find that I haven't succeeded in this, and this is quite contrary to my intentions. – Piskator Jul 16 '23 at 12:06
  • 2
    It's not just about cutting code that isn't used, but also about keeping data structures as simple as possible. `Comp` looks equivalent to a stacking of 3 monad transformers. Written that way, you can **a)** omit the hand-written `Monad` instance so there's no need to search for bugs there **b)** see which parts of the transformer stack are actually causing the behaviour you find unintuitive, or **c)** if you actually get different behaviour, then that focuses the question on why there are differences. You can ask about the differences, or you can ask about how to rewrtite is as transformers. – leftaroundabout Jul 16 '23 at 12:31
  • `Comp a = ReaderT [(String, Value)] (ExceptT RError (WriterT [String] a))` if I'm not mistaken. – leftaroundabout Jul 16 '23 at 12:33
  • 2
    Try evaluating your programs by hand, then you will either figure out what's going on on your own, or you will have something more concrete to point to in your question. – Li-yao Xia Jul 16 '23 at 12:41

1 Answers1

4

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.)

duplode
  • 33,731
  • 7
  • 79
  • 150