2

我把 Monad、ReaderT 弄得一团糟……来执行“简单?” 行为。

我想将测试功能散布到Maybe转换(Maybe或另一个个性化的单子)中。

确切地说,我想避免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执行一些喜欢

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 帖子

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]

可能的(重要)问题是:

  1. 是可用的单子吗?
  2. do符号在哪里?
  3. 仅适用于将测试功能定义为唯一类型(新测试功能需要新类型)

但我可以将测试函数包装成伪单子......(它的东西)

4

2 回答 2

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)因为我们只是使用完全一般的“单子”(真的,Kleisli)组合,没有特殊效果。

二、

给定一个谓词p :: a -> Bool,我们可以开始失败。第一种方法是使用Maybe'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-like 和Maybe-like 效果的混合,但它没有与Monad它们或它们的堆栈完全相同的 ic 语义。我们可以用其他方式捕捉它吗?

三、

好吧,让我们试着把它们包起来。

newtype OurMonad a = OM { getOM :: MaybeT (Reader (a -> Bool)) a }

现在OurMonad是一个newtype围绕 monad 变压器堆栈,包括ReaderMaybe. 我们将能够利用这一点来编写一个高度通用的“运行”函数,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,即 type a -> a。这是我们的单个谓词就足够的一种方式!我们可以利用类型同质性将Endos 的列表写成[a -> a]。因此,让我们更直接地定义我们需要的内容作为fold该列表的内容:

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 技术上不正确,但理论上和实践上,如果我们不能编写Functor实例,那么我们也会在编写实例时遇到困难Monad

** 从技术上讲,这仍然是一个 categorical Functor,它只是逆变的,而Functor假设Monad函子是协变的。我们可以将所有这些概括为具有同构上的仿函数,Edward Kmett 将这些称为ExFunctors 我认为您可以改为定义xmap :: (a -> b) -> (b -> a) -> f a -> f b,这很好。

于 2013-02-22T01:56:48.043 回答
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

并一起跳过 do 符号。

于 2013-02-22T00:36:06.203 回答