如何解决将 monadic 验证转变为适用性?
我需要验证数字输入是否在某个范围内。为此,我正在使用
datetime.datetime.strptime()
检查某个值 ensure :: (a -> Bool) -> a -> Maybe a
ensure p v | p v = Just v
| otherwise = Nothing
的上限和下限的一种方法是通过单子链:
x :: Int
据我所知,两次验证的顺序无关紧要;因此,应该可以以应用形式重写上述表达式。如何? 我没能做到,但我希望一旦我做到了,我就能更深入地了解 applicatives :-)。
解决方法
这里需要注意的非常微妙的一点是,“直观地”ensure
的返回值并不重要。仅重要的是它是 Just
还是 Nothing
。为了表达这一点,你可以给它一个更诚实的类型签名:
ensure :: (a -> Bool) -> a -> Maybe ()
ensure p v | p v = Just ()
| otherwise = Nothing
现在,由于返回值无关紧要,您应该可以安全地忽略它。所以逻辑变成了这样:调用 ensure (>0)
和 ensure (<100)
,然后将它们组合在一起,忽略它们的返回值,但保留它们的 Maybe
形状。
要做到这一点,您确实可以使用 applicative。您将要应用的函数将执行我上面所说的:忽略两个返回值。 “保留 Maybe
形状”部分将由 Applicative
实例透明处理。所以:
let validated = (\_ _ -> x) <$> ensure (>0) x <*> ensure (<100) x
看看我的应用函数如何忽略这两个参数并返回 x
本身?
但是,当然,ensure
的类型签名,如您的问题所写,表明它可能做的不仅仅是验证。当然,当它完全通用时它真的不能,但如果它更具体一点就可以:
ensure :: (Int -> Bool) -> Int -> Maybe Int
ensure p v | p v = Just (v + 100)
| otherwise = Nothing
现在顺序突然很重要:
pure (-5) >>= ensure (>0) >>= ensure (<100) == Nothing
pure (-5) >>= ensure (<100) >>= ensure (>0) == Just 95
所以这里的底线是:ensure
的目的有点不明确。它真的返回Maybe a
吗?那么顺序可能很重要。真正的返回类型是 Maybe ()
吗?然后您可以使用 Applicative
并忽略这些单位。
您可以使用 liftA2
组合两个谓词:
> import Control.Applicative
> :t liftA2 (&&) (> 0) (< 100)
liftA2 (&&) (> 0) (< 100) :: (Ord a,Num a) => a -> Bool
此函数具有与 ensure
一起使用的正确类型:
validate :: Num a => a -> Bool
validate f g = ensure (liftA2 (&&) f g)
然后
> validate (> 0) (< 100) 50
Just 50
> validate (> 0) (< 100) 1000
Nothing
,
(这是这个答案的全新版本)。
你有
ensure :: (a -> Bool) -> a -> Maybe a
ensure p v | p v = Just v
| otherwise = Nothing
实际上是
ensure :: Alternative m => (a -> Bool) -> a -> m a
ensure p v = if p v then pure v else empty
这样
validated x = pure x >>= ensure (>0) >>= ensure (<100)
= do
a <- pure x
b <- ensure (>0) a
c <- ensure (<100) b
pure c
= do
b <- ensure (>0) x
c <- ensure (<100) b
pure c
如果 ensure
是一个不透明的函数,我们将不得不停在这里。
但既然我们知道它的来源,我们可以继续,
= do
b <- if (>0) x then pure x else empty
c <- if (<100) b then pure b else empty
pure c
= do
_ <- if (>0) x then pure () else empty
_ <- if (<100) x then pure () else empty
pure x
= do
if (>0) x then pure () else empty
if (<100) x then pure () else empty
pure x
= guard ((>0) x) >> guard ((<100) x) >> pure x
= [ x | x > 0,x < 100 ] -- with MonadComprehensions
这个计算本质上没有什么是一元的,这里没有使用在前一步计算的值来计算下一个计算步骤,只是从给定值计算出一个计算步骤,x
:
= guard ((>0) x) *> guard ((<100) x) *> pure x
= liftA2 (\_ _-> x) (guard $ (>0) x) (guard $ (<100) x)
= when (x>0) (when (x<100) $ pure ()) *> pure x
= when (and [x>0,x<100]) (pure ()) *> pure x
最后一个表达式的推断类型是 Applicative f => f b,x :: b
。
没有。 Applicative 和 Monad 之间的区别,虽然两者都在应用程序之间强制执行顺序,但 Monad 可以接受来自链中其他操作的值,而 Applicative 不能。 Maybe 有一个 Applicative 实例,但它的工作方式与 Maybe Monoid 一样:返回最左边的 Just
值。换句话说,Applicative 实例使用值 Maybe a
,而不是 a -> Maybe a
。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。