至少两个Maybes

时间:2014-09-10 15:51:25

标签: haskell maybe

我希望获得最少两个可能的值,或者如果一个没有得到非任何一个,或者如果两个输入都没有则不返回任何值。我可以编写一个简单的函数来执行此操作,但我怀疑有一种方法可以在不编写自定义函数的情况下执行此操作。对不起,如果这是一个小问题,但有比使用这个自定义函数更简单的方法吗?

minMaybe :: Ord a => Maybe a -> Maybe a -> Maybe a
minMaybe Nothing b = b
minMaybe a Nothing = a
minMaybe (Just a) (Just b) = Just $ min a b

5 个答案:

答案 0 :(得分:17)

可以使用Control.Applicative中的运算符来满足规范。

myMin :: Ord x => Maybe x -> Maybe x -> Maybe x
myMin a b = min <$> a <*> b <|> a <|> b

<|> Maybe实现“偏好”

Nothing <|> b  = b
a       <|> _  = a

事情是

min <$> Just a <*> Just b = Just (min a b)

min <$> Just a <*> Nothing = Nothing

导致了这个问题的一些错误答案。使用<|>可以让您在可用时更喜欢计算出的min值,但只有一个值Just时才能恢复。

但是你应该问一下以这种方式使用Maybe是否合适。由于Monoid实例的不光彩的例外,Maybe被设置为模拟容易出错的计算。你在这里拥有的是具有“top”元素的现有Ord的扩展名。

data Topped x = Val x | Top deriving (Show, Eq, Ord)

您会发现min的{​​{1}}正是您所需要的。将类型视为数据的表示,而不是结构数据的设备,这是很好的。 Topped x通常代表某种失败,因此为您的目的使用不同类型可能更好。

答案 1 :(得分:3)

您无法使用ApplicativeMonad实例,因为这些上下文中的任何Nothing都会使您的总结果为Nothing。话虽如此,“simpler”一词是高度自以为是,你的功能很好。

答案 2 :(得分:3)

您可以使用Alternative的{​​{1}}实例编写它:

Maybe

或者,您可以使用minMaybe a b = liftA2 min a b <|> a <|> b 作为默认值,因此它始终会选择另一个:

maxBound

但我不建议这样做。

答案 3 :(得分:2)

问题是关于提升功能min :: Ord a ⇒ a → a → aMaybe的上下文一起使用。它具有关联性,因此Semigroup实例可以完全满足您的要求:

min' :: forall a. Ord a => Maybe a -> Maybe a -> Maybe a
min' = coerce ((<>) @(Maybe (Min a)))

这需要ScopedTypeVariablesTypeApplicationscoerce来自Data.Coerce。下面提供了更多的老式解决方案。但是,以上版本应具有更高的性能:coerce在运行时不存在。尽管GHC可以消除fmap,但不能保证:

min'' :: Ord a => Maybe a -> Maybe a -> Maybe a
min'' x y = fmap getMin (fmap Min x <> fmap Min y)

P.S。我会说你的解决方案很好。

答案 4 :(得分:0)

我认为radomaj有一个good idea

import Data.Ord (Down (..))
import Data.Function (on)

minMaybes mx my =
  getDown <$> (max `on` fmap Down) mx my
getDown (Down x) = x

我们使用maxJust更喜欢Nothing,然后使用Down,因此如果两者均为Just,我们实际上得到的最小值。


这是另一种类似的方法,看起来似乎更干净。可以使用Maybe将额外的最小值Nothing附加到任意Ord类型的方法。我们可以编写自己的类型以增加最大值:

data AddMax a = TheMax | Plain a deriving Eq
instance Ord a => Ord (AddMax a) where
  TheMax <= Plain _ = False
  _ <= TheMax = True
  Plain a <= Plain b = a <= b

maybeToAddMax :: Maybe a -> AddMax a
maybeToAddMax = maybe TheMax Plain

addMaxToMaybe :: AddMax a -> Maybe a
addMaxToMaybe TheMax = Nothing
addMaxToMaybe (Plain a) = Just a

现在您可以写

minMaybes mx my = addMaxToMaybe $
  (min `on` maybeToAddMax) mx my

您还可以拉一下手脚:

{-# LANGUAGE ScopedTypeVariables, TypeApplications #-}
import Data.Ord
import Data.Function
import Data.Coerce

newtype AddMax a = AddMax {unAddMax :: Down (Maybe (Down a))}
  deriving (Eq, Ord)

现在

minMaybes :: forall a. Ord a => Maybe a -> Maybe a -> Maybe a
minMaybes = coerce (min @(AddMax a))