将 monadic 验证转变为适用性?

Ulr*_*ter 1 validation monads haskell applicative

我需要验证数字输入是否在某个范围内。为此,我正在使用

ensure :: (a -> Bool) -> a -> Maybe a
ensure p v | p v       = Just v
           | otherwise = Nothing
Run Code Online (Sandbox Code Playgroud)

检查某个值的上限和下限的一种方法x :: Int是通过一元链:

let validated = pure x >>= ensure (>0) >>= ensure (<100)
Run Code Online (Sandbox Code Playgroud)

据我了解,两次验证的顺序无关紧要;因此,应该可以以应用形式重写上述表达式。如何?我没能做到,但我希望一旦我做到了,我就能对应用程序有更深入的了解:-)。

Fyo*_*kin 6

这里要注意的非常微妙的事情是“直观地”返回值ensure并不重要。仅重要的是它是Just还是Nothing。为了表达这一点,你可以给它一个更诚实的类型签名:

ensure :: (a -> Bool) -> a -> Maybe ()
ensure p v | p v       = Just ()
           | otherwise = Nothing
Run Code Online (Sandbox Code Playgroud)

现在,由于返回值无关紧要,您应该可以安全地忽略它。所以逻辑变成了这样:调用ensure (>0)and ensure (<100),然后将它们组合在一起,忽略它们的返回值,但保留它们的Maybe形状。

为此,您确实可以使用 applicative。您将要应用的函数将执行我上面所说的:忽略两个返回值。“保留Maybe形状”部分将由Applicative实例透明地处理。所以:

let validated = (\_ _ -> x) <$> ensure (>0) x <*> ensure (<100) x
Run Code Online (Sandbox Code Playgroud)

看看我的应用函数如何忽略两个参数并返回x自身?


但是,当然,ensure正如您的问题中所写的那样, 的类型签名表明它可能不仅仅做验证。当然,当它完全通用时它真的不能,但如果它更具体一点就可以:

ensure :: (Int -> Bool) -> Int -> Maybe Int
ensure p v | p v       = Just (v + 100)
           | otherwise = Nothing
Run Code Online (Sandbox Code Playgroud)

现在订单突然很重要:

pure (-5) >>= ensure (>0) >>= ensure (<100) == Nothing
pure (-5) >>= ensure (<100) >>= ensure (>0) == Just 95
Run Code Online (Sandbox Code Playgroud)

所以这里的底线是:目的ensure有点不明确。真的回来了Maybe a吗?那么顺序可能很重要。是真正的返回类型Maybe ()吗?然后你可以使用Applicative和忽略这些单位。


che*_*ner 6

您可以使用liftA2组合两个谓词:

> import Control.Applicative
> :t liftA2 (&&) (> 0) (< 100)
liftA2 (&&) (> 0) (< 100) :: (Ord a, Num a) => a -> Bool
Run Code Online (Sandbox Code Playgroud)

此函数具有与 一起使用的正确类型ensure

validate :: Num a => a -> Bool
validate f g = ensure (liftA2 (&&) f g)
Run Code Online (Sandbox Code Playgroud)

然后

> validate (> 0) (< 100) 50
Just 50
> validate (> 0) (< 100) 1000
Nothing
Run Code Online (Sandbox Code Playgroud)

  • 我-1ed是因为缺乏对“(a-&gt;)”应用的解释。虽然这无疑是一个合理的用例,但它可能会让初学者感到非常困惑,特别是如果问题是在完全不同的 Maybe monad 的上下文中提出的。 (3认同)
  • 对于谓词列表:“validate ps = Ensure (all .sequenceA ps)” (2认同)