I'm making a mess with Monad, ReaderT, ... to perform a "simple?" behavior.
I want to intersperse a test function into Maybe
transformation ( Maybe
or another personalized monad).
Exactly, I want avoid t
calls, creating some kind of monad (monad, I think) on
doCalculus :: (Int -> Bool) -> Int -> Maybe Int
doCalculus f a = do
b <- t $ a + 1
c <- t $ 2 * b
d <- t $ a + b + c
return d
where t = if f n then Just n else Nothing
example
test :: Int -> Bool
test n = not (n `elem` [3, 7, 9])
*Main> doCalculus test 2
Nothing
*Main> doCalculus test 3
Just 15
*Main>
I'm trying to do some monad like ReaderT
to perform some like
runMaybeTest doCalculus test
to use as
doCalculus :: Int -> Maybe' Int
doCalculus a = do
b <- a + 1
c <- 2 * b
d <- a + b + c
return d
perform = runMaybe' doCalculus test
but I can't.
(Of course, Int
type would be generic into monad)
Thank you for any tip!
=== UPDATE 1 ===
I can do it! :) ... but isn't practical (I think) :(
I had adapted a fantastic Eric Kidd post
import Prelude hiding (Just, Nothing, return, (>>=))
class Tester a where
test :: a -> Bool
test _ = True
data MMaybe a = Nothing | Just a deriving (Eq, Show)
class Monad1 m a where
return :: a -> m a
fail :: String -> m a
class (Monad1 m a, Monad1 m b) => Monad2 m a b where
(>>=) :: m a -> (a -> m b) -> m b
instance (Tester a) => Monad1 MMaybe a where
return = Just
fail _ = Nothing
instance (Tester a, Tester b) => Monad2 MMaybe a b where
Nothing >>= _ = Nothing
(Just x) >>= f = if test x then f x else Nothing
instance Tester Int where
test n = not $ n `mod` 2 == 0 && n `mod` 3 == 0
test1 :: Int -> MMaybe Int
test1 n =
return n >>= \a ->
return (a + 3) >>= \b ->
return (a + b)
test2 = map test1 [1..20]
possible (important) problems are:
but I can wrap test function into pseudo-monad... (it's something)
It looks like you want to (a) sequence some transformations and (b) short-circuit predicate failure at various stages. This whole process is parametric over the "contained" type (which is Int here) and the predicate.
Let's dive in.
The primary effect we're controlling here is failure, so Maybe
is a great place to start. It's Monad
instance lets us compose various Maybe
-producing computations.
-- some pure computations
f, g, h :: a -> a
-- ... lifted and sequenced!
may :: a -> Maybe a
may = (return . f) >=> (return . g) >=> (return . h)
This is a very ceremonial way of writing (h . g . f)
since we're just using completely general "monadic" (really, Kleisli
) composition and no special effects.
Given a predicate p :: a -> Bool
, we can start to fail. The first way to do this would be to use Maybe
's MonadPlus
instance and guard :: MonadPlus m => Bool -> m ()
.
\a -> do x <- return (f a)
guard (p x)
y <- return (g x)
guard (p y)
z <- return (h y)
guard (p z)
return z
But we've obviously got a fairly repeated pattern going on here---at every "composition boundary" of pure functions we perform our predicate and maybe fail. This is a strong commingling of Reader
-like and Maybe
-like effects just like you thought, but it doesn't have quite the same Monad
ic semantics as either of those or their stack. Can we capture it some other way?
Well, let's try wrapping them up.
newtype OurMonad a = OM { getOM :: MaybeT (Reader (a -> Bool)) a }
Now OurMonad
is a newtype
around the monad transformer stack including Reader
and Maybe
. We'll be able to take advantage of that in order to write a highly general "run" function, runOurMonad :: (a -> Bool) -> OurMonad a -> Maybe a
.
Or, rather, it sort of feels like we could, right? I want to argue that we can't, actually. The reason is that in order to write a Monad
instance we have to have a Functor
instance* which means that given any function mapping a -> b
we need to be able to map OM a -> OM b
. The problem is that we don't generally know how to generalize our predicate! I know no way to write a function (a -> b) -> (a -> Bool) -> b -> Bool
unless we also have a function b -> a
**.
So we're stuck trying to generalize this to a Monad
. I don't think it is one.
But your example didn't actually need the full generality of Monad—we know all of our pure transformations are Endo
, ie of type a -> a
. That's one way our single predicate could be sufficient! We can take advantage of the type homogeneity to write a list of our Endo
s as [a -> a]
. So let's just define what we need a little more directly as a fold
over that list:
stepGuarded :: (a -> Bool) -> a -> [a -> a] -> Maybe a
stepGuarded pred = foldM $ \a f -> mfilter pred (return $ f a)
stepGuarded (`elem` [3, 7, 9]) 3 [ (+1), (*2) ]
-- Nothing
stepGuarded (`elem` [4, 8, 9]) 3 [ (+1), (*2) ]
-- Just 8
* nb Not technically true, but theoretically and practically if we can't write a Functor
instance then we'll get stuck writing a Monad
instance, too.
** Technically, this is still a categorical Functor
, it's just contravariant whereas Functor
and Monad
assume the functor is covariant. We can generalize all of this to have functors-on-isomorphisms, Edward Kmett calls these ExFunctor
s I think and you get to define xmap :: (a -> b) -> (b -> a) -> fa -> fb
instead, which is nice.
I think what you need to do is use "let" in your do blocks.
doCalculus :: Int -> Maybe Int
doCalculus a = do
let b = a + 1
let c = 2 * b
let d = a + b + c
return d
or
doCalculus :: Int -> Maybe Int
doCalculus a = Just d where
b = a + 1
c = 2 * b
d = a + b + c
and skip the do notation all together.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.