I have module:
module Writer where
import Prelude hiding (Monad, (>>=), return, (=<<))
main = putStrLn "hello"
class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
(=<<) :: (a -> m b) -> m a -> m b
(=<<) = flip (>>=)
data Writer w a = Writer { runWriter :: (a, w) } deriving Show
instance (Monoid w) => Monad (Writer w) where
return x = Writer $ (x, mempty)
m >>= k = let
(b, w1) = runWriter m
Writer (a, w2) = k b
in Writer (a, (w1 `mappend` w2))
writer :: (a, w) -> Writer w a
writer = Writer
instance (Semigroup a, Num a) => Monoid (Foo a) where
mempty = Foo 0
mappend (Foo v1) (Foo v2) = Foo (v1 + v2)
instance Semigroup a => Semigroup (Foo a) where
(Foo v1) <> (Foo v2) = Foo (v1 <> v2)
instance Semigroup Integer where
a1 <> a2 = a1 + a2
tell :: Monoid w => w -> Writer w ()
tell w = writer ((), w)
data Foo a = Foo { getFoo :: a } deriving Show
type LoggerFooInt = Writer (Foo Integer) ()
logLine :: String -> Integer -> LoggerFooInt
logLine _ = tell . Foo
batchLog :: Writer (Foo Integer) ()
batchLog = do
logLine "line1" 19450
logLine "line2" 760
logLine "line3" 218
I tried to write batchLog
function but compiler said:
Writer.hs:46:3: error:
• No instance for (GHC.Base.Monad (Writer (Foo Integer)))
arising from a do statement
• In a stmt of a 'do' block: logLine "line1" 19450
In the expression:
do logLine "line1" 19450
logLine "line2" 760
logLine "line3" 218
In an equation for ‘batchLog’:
batchLog
= do logLine "line1" 19450
logLine "line2" 760
logLine "line3" 218
|
46 | logLine "line1" 19450
| ^^^^^^^^^^^^^^^^^^^^^^^
Failed, no modules loaded.
So, why I need to define any Monads else. I have already instance (Monoid w) => Monad (Writer w)
and instance (Semigroup a, Num a) => Monoid (Foo a)
and instance Semigroup Integer
. Why it's not enough? Without batchLog
function module compiles.
GHCi, version 8.6.5: http://www.haskell.org/ghc/
UPDATE: I tried to rewrite without do notation, and after a while I can do that and it compiles, but still can't understand, why another code, compiles with my own Monad and with do notation:
module MaybeMonad
import Prelude hiding (Monad, (>>=), return, (=<<))
import Control.Monad (ap, liftM)
import Data.Char
main = putStrLn "hello"
class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
(=<<) :: (a -> m b) -> m a -> m b
instance Monad Maybe where
return = Just
(>>=) Nothing _ = Nothing
(>>=) (Just x) k = k x
(=<<) = flip (>>=)
data Token = Number Int | Plus | Minus | LeftBrace | RightBrace
deriving (Eq, Show)
asToken :: String -> Maybe Token
asToken "+" = Just(Plus)
asToken "-" = Just(Minus)
asToken "(" = Just(LeftBrace)
asToken ")" = Just(RightBrace)
asToken str | all isDigit str = Just $ Number $ read str
asToken _ = Nothing
tokenize :: String -> Maybe [Token]
tokenize x = foldr (\word maybeTokens -> do
token <- asToken word
tokens <- maybeTokens
return $ token : tokens) (return []) $ words x
Correct example:
{-# LANGUAGE RebindableSyntax #-}
module Writer where
import Prelude hiding (Monad, (>>=), return, (=<<), (>>))
class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
(=<<) :: (a -> m b) -> m a -> m b
(=<<) = flip (>>=)
(>>) :: m a -> m b -> m b
data Writer w a = Writer { runWriter :: (a, w) } deriving Show
instance (Monoid w) => Monad (Writer w) where
return x = Writer $ (x, mempty)
m >>= k = let
(b, w1) = runWriter m
Writer (a, w2) = k b
in Writer (a, (w1 `mappend` w2))
ma >> mb = let
(_, w1) = runWriter ma
(vb, w2) = runWriter mb
in Writer(vb, (w1 `mappend` w2))
writer :: (a, w) -> Writer w a
writer = Writer
instance (Semigroup a, Num a) => Monoid (Foo a) where
mempty = Foo 0
mappend (Foo v1) (Foo v2) = Foo (v1 + v2)
instance Semigroup a => Semigroup (Foo a) where
(Foo v1) <> (Foo v2) = Foo (v1 <> v2)
instance Semigroup Integer where
a1 <> a2 = a1 + a2
tell :: Monoid w => w -> Writer w ()
tell w = writer ((), w)
data Foo a = Foo { getFoo :: a } deriving Show
logLine :: String -> Integer -> Writer (Foo Integer) ()
logLine _ = tell . Foo
batchLog :: Writer (Foo Integer) ()
batchLog = do
logLine "line1" 19450
logLine "line2" 760
logLine "line3" 218
after adding RebindableSyntax and hiding (>>) operator and adding my own realization it compiles and works properly.
As per @freestyle's comment, do-notation by design always uses the Monad
class from Prelude
, even if you have your own Monad
class defined. The RebindableSyntax
extension can be enabled to make do-notation use whatever Monad
class (or more specifically, whatever >>=
, >>
, and fail
functions) are currently in scope.
This also affects a bunch of other rebindable syntax. See the link above for a list, and make sure you aren't overriding additional syntax you don't intend to.
Also, RebindableSyntax
implies NoImplicitPrelude
, but that should be fine since you already have an import Prelude
statement.
UPDATE: However, it's important to make sure you've hidden ALL the applicable Prelude
syntax, or you may find yourself unintentionally using the Monad
class from Prelude
, even if you didn't want to. In your definition of:
batchLog :: Writer (Foo Integer) ()
batchLog = do
logLine "line1" 19450
logLine "line2" 760
logLine "line3" 218
the do-block is desugared into:
batchLog = logLine "line1" 19450 >> logLine "line2" 760 >> ...
And, where is (>>)
defined? In Prelude
, of course. You will need to hide that as well, and provide your own definition, either in your custom Monad
class or as a standalone function. After making the following modifications to your first code block, the do-block type checks correctly, and runWriter batchLog
appears to work fine:
import Prelude hiding (..., (>>), ...)
(>>) :: (Monad m) => m a -> m b -> m b
act1 >> act2 = act1 >>= \_ -> act2