参数化也许是monad

时间:2013-02-21 23:58:46

标签: haskell monad-transformers

我正在搞乱Monad,ReaderT,......表演“简单?”行为。

我想将测试函数散布到Maybe转换(Maybe或其他个性化monad)中。

确切地说,我想避免t来电,在

上创建某种monad(monad,我认为)
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]

可能(重要)的问题是:

  1. 是一个可用的monad?
  2. 在哪里做记谱法?
  3. 仅将测试功能定义为唯一类型(新测试功能需要新类型)
  4. 但是我可以将测试函数包装成伪monad ...(这是一些东西)

2 个答案:

答案 0 :(得分:6)

看起来您希望(a)对某些转换进行排序,以及(b)在各个阶段发生短路谓词故障。整个过程是参数化的“包含”类型(这里是Int)和谓词。

让我们潜入。

I

我们在这里控制的主要影响是失败,所以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)构图而没有特效。

II。

给定谓词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语义。我们可以用其他方式捕捉它吗?

III。

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

Monad

现在newtype OurMonad a = OM { getOM :: MaybeT (Reader (a -> Bool)) a } 是monad变换器堆栈周围的OurMonad,包括newtypeReader。我们将能够利用这一点来编写一个高度通用的“运行”函数Maybe

或者,相反,它有点像我们可以,对吗?我想说,实际上我们不能。原因是为了编写runOurMonad :: (a -> Bool) -> OurMonad a -> Maybe a实例,我们必须有一个Monad实例*,这意味着给定任何函数映射Functor我们需要能够映射a -> b }。问题是我们通常不知道如何推广我们的谓词!除非我们还有函数OM a -> OM b **,否则我知道无法编写函数(a -> b) -> (a -> Bool) -> b -> Bool

所以我们一直试图将其概括为b -> a。我不认为这是一个。

IV

但是你的例子实际上并不需要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,它只是逆变而FunctorFunctor假设仿函数是协变的。我们可以将所有这些概括为同构函数,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

并一起跳过记号。