0

I am still a bit new to Monads and have just realized that something I thought I understood can't be the case. In the MVE that follows I have a hard time understanding why next <- scanChar can return just a single Char and bind it to the next. Wouldn't it rather be [(Char, Int, d)] that would be bound to next since this is what is unwrapped when using do notation given that all of the value within the Monad will be unpacked, or is there something in the constructors or instances that will make it otherwhise?

import qualified Data.Set as S

import Control.Monad

type CharSet = S.Set Char

data RE =
    RClass Bool CharSet

newtype RegModule d a =
  RegModule {runRegModule :: String -> Int -> d -> [(a, Int, d)]}

instance Monad (RegModule d) where
  return a = RegModule (\_s _i d -> return (a, 0, d))
  m >>= f =
    RegModule (\s i d -> do (a, j, d') <- runRegModule m s i d
                            (b, j', d'') <- runRegModule (f a) s (i + j) d'
                            return (b, j + j', d''))

instance Functor (RegModule d) where fmap = liftM
instance Applicative (RegModule d) where pure = return; (<*>) = ap

scanChar :: RegModule d Char
scanChar = RegModule (\s i d ->
  case drop i s of
    (c:cs) -> return (c, 1, d)
    [] -> []
  )

regfail :: RegModule d a
regfail = RegModule (\_s _i d -> []
                )
regEX :: RE -> RegModule [String] ()
regEX (RClass b cs) = do
  next <- scanChar  
  if (S.member next cs)
    then return ()
    else regfail
 
runRegModuleThrice :: RegModule d a -> String -> Int -> d -> [(a, Int, d)]
runRegModuleThrice matcher input startPos state =
  let (result1, pos1, newState1) = head $ runRegModule matcher input startPos state
      (result2, pos2, newState2) = head $ runRegModule matcher input pos1 newState1
      (result3, pos3, newState3) = head $ runRegModule matcher input pos2 newState2
  in [(result1, pos1, newState1), (result2, pos2, newState2), (result3, pos3, newState3)]
Piskator
  • 605
  • 1
  • 9
  • 2
    `scanChar` has type `RegModule d Char`. The monad is `RegModule d` so the only thing `scanChar` can possibly bind is `Char`. `return (c, 1, d)` happens in an entirely different monad (`[]`) in a different context. – n. m. could be an AI Jul 08 '23 at 08:44
  • `<-` do not unwrap. I'd recomend to start with something like `Maybe` monad and do not use `do`-notation as it is only sintactic sugar for `>>=`. Try writing something in the `Maybe` monad using `>>=` and then, the same code using `do-notation`. You realize nothings gets "unwraps", it just looks like – lsmor Jul 08 '23 at 08:45
  • @n.m.willseey'allonReddit why does it follow that `Char`is the only thing that can possibly be bind due to that the monad is `RegModule d`? `return (c, 1, d)` uses the list Monad I am aware, but I am not entirely sure of what this has to do with what is done (what it's impact is) wiht `next <- scanChar` considering that this line will have to return something whether that be based on the list Monad or the `RegModule d`-monad, right? – Piskator Jul 08 '23 at 08:53
  • @lsmor , I have done the Maybe-binding that you suggest at some point already. I think I could be more precise in my formulation, that the values gets unwrapped and wrapped again, or that the transformation is applied directly to the value that is within the Monad. – Piskator Jul 08 '23 at 08:55
  • 2
    @Piskator it's all about the types, but *nothing whatsoever* to do with what the Monad is. Literally take any monad `m` and any concrete type `a`, and any value `m'` of type `m a`, and then when you "bind" `m'`, whether via `m' >>= \a' -> ...` or as `a' <- m'` in `do` notation (which is simply syntactic sugar for the former expression), `a'` will have type `a`. The type of `>>=` doesn't allow for anything else. – Robin Zigmond Jul 08 '23 at 09:16
  • 3
    Because that's how types work in this language. Look at the types. The type of `x` is `M T`, the monad is `M`, therefore when you do `y <- x` the type of `y` is `T`. It follows from the definition of `<-`. `y <- x` is a syntactic sugar for `x >>= \y -> ...` so you need to look at how `>>=` is done in your monad and what kind of function it expects as its second argument. Note it has nothing to do with `return`. – n. m. could be an AI Jul 08 '23 at 09:17
  • 2
    Looking at types is the right thing to do. Even if it's more complex, you can also look at the instance definition: since `y <- x` is sugar for `x >>= \y -> ...`, you can check the definition in the instance of `x >>= f` and see what is passed to `f` as an argument, since that will be the value of `y`. In your instance, you have `runRegModule (f a) s (i + j) d'` which involves `f a`, so `y` is `a`. – chi Jul 08 '23 at 09:36
  • @RobinZigmond and @n. m. will see y'all on Reddit , I think I understand monads and types the way both of you depict heree. I think however that @chi 's comment clarified my confusion the most as to *why* `y` is `a` and not `(c, Int, d)` ... in this code it is extra complex because a list Monad comes into play. – Piskator Jul 08 '23 at 09:51
  • Would there be a way - without creating new functions - to get the whole tupple of the monad, such as with `get` (I don't know if that works) or something else? – Piskator Jul 08 '23 at 09:52

0 Answers0