我正在搞乱Monad,ReaderT,......表演“简单?”行为。
我想将测试函数散布到Maybe
转换(Maybe
或其他个性化monad)中。
确切地说,我想避免t
来电,在
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
例如
test :: Int -> Bool
test n = not (n `elem` [3, 7, 9])
*Main> doCalculus test 2
Nothing
*Main> doCalculus test 3
Just 15
*Main>
我正在尝试像ReaderT
那样执行一些monad来执行类似
runMaybeTest doCalculus test
用作
doCalculus :: Int -> Maybe' Int
doCalculus a = do
b <- a + 1
c <- 2 * b
d <- a + b + c
return d
perform = runMaybe' doCalculus test
但我不能。
(当然,Int
类型在monad中是通用的)
感谢您的任何提示!
===更新1 ===
我能做到! :) ...但不实用(我认为):(
我改编了一个很棒的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]
可能(重要)的问题是:
但是我可以将测试函数包装成伪monad ...(这是一些东西)
答案 0 :(得分:6)
看起来您希望(a)对某些转换进行排序,以及(b)在各个阶段发生短路谓词故障。整个过程是参数化的“包含”类型(这里是Int)和谓词。
让我们潜入。
我们在这里控制的主要影响是失败,所以Maybe
是一个很好的起点。它的Monad
实例允许我们编写各种Maybe
- 生成计算。
-- some pure computations
f, g, h :: a -> a
-- ... lifted and sequenced!
may :: a -> Maybe a
may = (return . f) >=> (return . g) >=> (return . h)
这是一种非常礼仪的写作方式(h . g . f)
,因为我们只使用完全一般的“monadic”(真的,Kleisli
)构图而没有特效。
给定谓词p :: a -> Bool
,我们可能会开始失败。第一种方法是使用Maybe
的{{1}}实例和MonadPlus
。
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
类似和Reader
类似效果的强烈混合,就像您想的那样,但它与其中任何一个或它们的堆栈没有完全相同的Maybe
ic语义。我们可以用其他方式捕捉它吗?
好吧,让我们试着把它们包起来。
Monad
现在newtype OurMonad a = OM { getOM :: MaybeT (Reader (a -> Bool)) a }
是monad变换器堆栈周围的OurMonad
,包括newtype
和Reader
。我们将能够利用这一点来编写一个高度通用的“运行”函数Maybe
。
或者,相反,它有点像我们可以,对吗?我想说,实际上我们不能。原因是为了编写runOurMonad :: (a -> Bool) -> OurMonad a -> Maybe a
实例,我们必须有一个Monad
实例*,这意味着给定任何函数映射Functor
我们需要能够映射a -> b
}。问题是我们通常不知道如何推广我们的谓词!除非我们还有函数OM a -> OM b
**,否则我知道无法编写函数(a -> b) -> (a -> Bool) -> b -> Bool
。
所以我们一直试图将其概括为b -> a
。我不认为这是一个。
但是你的例子实际上并不需要Monad的全部通用性 - 我们知道我们所有的纯变换都是Monad
,即类型为Endo
。这是我们的单一谓词就足够了的一种方式!我们可以利用类型同质性将a -> a
的列表写为Endo
。因此,让我们直接在该列表中定义我们需要的[a -> a]
:
fold
* n.b.技术上不正确,但从理论上和实际上如果我们不能编写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
实例,那么我们也会陷入编写Functor
实例的困境。
**从技术上讲,这仍然是一个分类Monad
,它只是逆变而Functor
和Functor
假设仿函数是协变的。我们可以将所有这些概括为同构函数,Edward Kmett称之为Monad
,我认为你可以定义ExFunctor
,这很好。
答案 1 :(得分:0)
我认为你需要做的是在你的do块中使用“let”。
doCalculus :: Int -> Maybe Int
doCalculus a = do
let b = a + 1
let c = 2 * b
let d = a + b + c
return d
或
doCalculus :: Int -> Maybe Int
doCalculus a = Just d where
b = a + 1
c = 2 * b
d = a + b + c
并一起跳过记号。