【问题标题】:Parameterized Maybe monad参数化的 Maybe monad
【发布时间】:2013-02-21 23:58:45
【问题描述】:

我正在和 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 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. 是可用的单子吗?
  2. do 表示法在哪里?
  3. 仅适用于将测试函数定义为唯一类型(新测试函数需要新类型)

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

【问题讨论】:

  • 我不清楚你到底想做什么(但这不一定是你的错)。如果您想编写添加了“Maybe”上下文的函数,请使用 Maybe monad 和使用 bind >>= 运算符的链函数?
  • 如果这听起来像你想要做的,我会提交一个完整的答案并尝试提供帮助。
  • 我认为您无法以任何重要的方式摆脱t。你将如何将表达式提升到 monad 中,以及如何界定应该执行测试的位置?
  • 在我看来,您对 Haskell 的看法是错误的,忽略了它的懒惰。
  • 是的@JohnL,这是个问题,如果我将“测试”函数(静态,“恒定地”,内联,...)写入Maybe monad 没有问题。如何参数化该测试功能?谢谢!

标签: haskell monad-transformers


【解决方案1】:

您似乎想要 (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 -&gt; Bool,我们可以开始失败。第一种方法是使用Maybe 的MonadPlus 实例和guard :: MonadPlus m =&gt; Bool -&gt; 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 效果的强烈混合,就像您想象的那样,但它与Monadic 语义与它们或其堆栈中的任何一个都不完全相同。我们可以通过其他方式捕获它吗?

三。

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

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

现在 OurMonad 是一个 newtype 在 monad 转换器堆栈周围,包括 Reader 和 Maybe。我们将能够利用这一点来编写一个高度通用的“运行”函数runOurMonad :: (a -&gt; Bool) -&gt; OurMonad a -&gt; Maybe a。

或者,更确切地说,感觉我们可以,对吧?我想争辩说我们不能,实际上。原因是为了编写Monad 实例,我们必须有一个Functor 实例*,这意味着给定任何函数映射a -&gt; b,我们需要能够映射OM a -&gt; OM b。问题是我们通常不知道如何泛化我们的谓词!我不知道怎么写函数(a -&gt; b) -&gt; (a -&gt; Bool) -&gt; b -&gt; Bool,除非我们也有函数b -&gt; a**。

所以我们一直试图将其概括为Monad。我不认为它是一个。

IV.

但您的示例实际上并不需要 Monad 的全部通用性——我们知道我们所有的纯转换都是 Endo,即 a -&gt; a 类型。这是我们的单个谓词就足够的一种方式!我们可以利用类型同质性将我们的Endos 列表写成[a -&gt; 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

* 注意技术上不正确,但理论上和实践上,如果我们不能编写 Functor 实例,那么我们也会在编写 Monad 实例时遇到困难。

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

【讨论】:

  • 这对于问题中给出的示例是不够的。纯粹的转换并不都是Endo,尤其是d &lt;- a + b + c 不是。如果您尝试拆分该表达式,则谓词将应用于每个部分和,这可能是错误的。在不了解更多问题的情况下,我怀疑 Maybe-monad+guard 解决方案可能是最合适的。
  • @tel,谢谢,我需要一些时间来阅读你的答案(我是一只哈斯克尔乌龟)
  • @JohnL 哦!我错过了!远藤完全不正确,你可能需要更像newtype Step a = a -&gt; (a, Step a) 的东西。我的主要目的是表明,如果你修复了谓词,那么你就不能形成一个简单的 Monad,但我的 Endo 示例显然过于简单了。
【解决方案2】:

我认为你需要做的是在你的 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 符号。

【讨论】:

  • 看来我在这里错过了这个问题。如果它完全错误,我可能会删除这个答案。
  • :) 是的@MFlamer 你的回答是完全错误的,但谢谢你,我没有得到很好的解释(可能)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-21
  • 1970-01-01
  • 2011-08-02
  • 2015-03-01
  • 1970-01-01
  • 2019-04-15
相关资源
最近更新 更多