有无点的方法可以将条件检查转换为 Maybe 类型的输入吗?

Is there a point-free way to convert a conditional check into a Maybe type of the input?

我只是在 haskell 中完成一些简单的练习,想知道是否有一种无需点的方法可以将 if-then-else 语句转换为 Maybe 类型:[=如果条件为假,则返回 12=],如果条件为真,则返回 Just

简而言之,给出一些:

maybeIf :: (a -> Bool) -> a -> Maybe a
maybeIf cond a = if cond a then Just a else Nothing

是否有相对于 a 无意义的实现?我也一直在寻找一个更具体的版本,a -> Maybe a,感觉 Control.Arrow 中可能有答案。但是,由于 Maybe 是一种数据类型,而 if-else 语句控制数据流,我不确定是否有一种干净的方法来做到这一点。

实现这一点的主要障碍是 if/then/else。您可以定义一个 if' 组合器,或者您可以使用我定义和经常使用的这个通用版本:

ensure p x = x <$ guard (p x)

标准工具给出连续的 point-free 个版本作为

ensure p = ap (<$) (guard . p)
ensure = ap (<$) . (guard .)

虽然我真的不认为任何一个都比有针对性的版本更好。

如果我们为布尔值选择 Church-encoding...

truth :: Bool -> a -> a -> a
truth True  t f = t
truth False t f = f

那么我们可以在Applicative-style.

中写一个point-freemaybeIf
maybeIf :: (a -> Bool) -> a -> Maybe a
maybeIf = liftA3 truth <*> pure Just <*> pure (pure Nothing)

一些直觉……

f <$> m₁ <*> … <*> mₙ = \x -> f (m₁ x) … (mₙ x)
liftAₙ f <$> m₁ <*> … <*> mₙ = \x -> f <$> m₁ x <*> … <*> mₙ x

如果您安装的字体不支持所需的 unicode 字符,这里是上述 "intuitions" 的 PNG 格式渲染。

因此:

liftA3 truth <*> pure Just <*> pure (pure Nothing)
= liftA3 truth <$> id <*> pure Just <*> pure (pure Nothing)
= \p -> truth <$> id p <*> (pure Just) p <*> (pure (pure Nothing)) p
= \p -> truth <$> p <*> Just <*> pure Nothing
= \p -> \a -> truth (p a) (Just a) ((pure Nothing) a)
= \p -> \a -> truth (p a) (Just a) Nothing

您可以从 Data.Foldable 导入 find 然后就很简单了:

import Data.Foldable(find)

maybeIf cond = find cond . Just

函数 find 并不复杂,因此您可以根据 Maybe 轻松地自己定义它,但它实际上与您自己实现的 [=] 并没有太大区别15=] 所以你可能不会收获太多,这取决于你为什么想这样做。

遵循 (并为此功能使用 Daniel Wagner 的新名称),

import Data.Bool (bool)
--         F    T    
-- bool :: a -> a -> Bool -> a

ensure :: (a -> Bool) -> a -> Maybe a
ensure p x = bool (const Nothing) Just (p x) x

ensure p   = join (bool (const Nothing) Just . p)
           = bool (const Nothing) Just =<< p  

ensure     = (bool (const Nothing) Just =<<)

join 是一个单子函数,join :: Monad m => m (m a) -> m a,但对于函数来说它只是

join   k  x = k x x
(k =<< f) x = k (f x) x  

join 被接受为 point-free 代码中 W combinator 的替代品。

你只想要它 point-free 关于 value 参数,但是很容易用 join 进一步转换等式(结果的可读性完全是另一个问题),因为

          = join ((bool (const Nothing) Just .) p)
          = (join . (bool (const Nothing) Just .)) p

的确如此,

 #> (join . (bool (const Nothing) Just .)) even 3
Nothing

 #> (bool (const Nothing) Just =<<) even 4
Just 4

但我更希望在实际代码中看到 \p x -> listToMaybe [x | p x]

或者只是 \p x -> [x | p x],使用 Monad Comprehensions。 x <$ guard (p x) 相同,只是语法不同。

此函数在 Control.Monad.Plus 中定义并称为 partial