2016-08-16 81 views
9

假设我有部分功能从ab,我所代表的功能从aMaybe b列表fns,我有a类型的对象x。假设现在我要定义另一个函数从aMaybe b这需要第一f x的值不是Nothing,如果存在fns这样的f,或价值Nothing如果没有这样的f存在。所以基本上它输出f x为第一个f有效,或Nothing如果没有f工作。如何应用Haskell中的第一个部分函数?

这不难想出一些代码来完成这项工作。例如,可以创建一个列表[f x| f <- fns]从中删除所有Nothing,然后将结果列表的头部,或Nothing,如果该列表为空。但是,这感觉很笨拙,这看起来像是一种使用Haskell中的某些内置函数的更加时尚的实现。如果是这样,那么我有兴趣知道它是什么。

回答

12

这是Alternative的用途。它代表选择,在这里你正在选择Maybe值。你可以这样做

foldr (<|>) empty [f x | f <- fns] 

Data.Foldable你也有asum :: (Foldable t, Alternative f) => t (f a) -> f a这你想要做什么,甚至更直接:

asum [f x | f <- fns] 

作为一个方面的话,我要指出,MonadPlus还做什么你想要它的Maybe实例。如上,你可以有

foldr mplus mempty [f x | f <- fns] 

而且

msum [f x | f <- fns] 

但IMO你应该在这里使用Alternative,因为更准确地传达你的“选择”的意思。

+0

'asum'在'Data.Foldable'的基础上。 – danidiaz

+0

使用通用工具包中的['Star'](https://hackage.haskell.org/package/profunctors-5.2/docs/Data-Profunctor.html#t:Star),您还可以编写'runStar。 asum。 fmap Star',感谢'Alternative f => Alternative(Star f a)'实例。 – danidiaz

+0

@danidiaz是的,我意识到几分钟前 - 我因为在Control.Applicative中寻找'asum'而被抛弃,其中定义了'Alternative'。这是一个巧妙的'星'的技巧!我一直在寻找像'星'这样的东西...... – Alec

9

Data.MonoidMaybe一个newtype副本,叫做First,有 “拿第一Just” 的行为。

如果您正在寻找与您所描述的行为类型

[a -> First b] -> a -> First b 

的功能,它仅仅是

fold 
Data.Foldable

,因为a ->的独异的行为做了逐点提升需要:Monoida -> First b正在挑选第一个应用程序的结果。可悲的是(我的眼泪已经很多了),得到Maybe而不是First需要更多的工作。

注意,逐点吊装,唬弄a ->出通过[],只是那种工作的sequenceA的,所以

(asum .) . sequenceA 

将做的工作。

从这种类型获得所需的幺半群结构是很好的:在这种情况下,需要使用asum访问Alternative行为。

+1

你知不知道是否有计划将'Semigroup'变成'base',这样我们就可以放松Monoid(也许是a)的超类约束,并且有一个不会减少长大的人流泪的例子? –

+0

@BenjaminHodgson你可能会喜欢[this](https://prime.haskell.org/wiki/Libraries/Proposals/SemigroupMonoid)。 – Alec

+3

@BenjaminHodgson,它在'base'中,并且将会是'Monoid'的超类,但这并不是什么会干扰pigworker的。他希望“可折叠也许”的“第一”语义,并且无法获得它们。 – dfeuer

3

警告:这是一个非常非标准的解决方案。但我个人非常喜欢它的优雅 - 以及潜在的思维弯曲。

https://wiki.haskell.org/Pointfree上,您可以找到一个名为swing的函数。它的实现和类型混淆起初:

swing :: (((a -> b) -> b) -> c -> d) -> c -> a -> d 
swing = flip . (. flip id) 

你可以得到的,当你看到它的完全应用形式,它做什么第一想法:

swing f c a = f ($ a) c 

与其他高阶的功能结合使用,做几乎看起来像魔术的事情。链接示例:

swing map  :: [a -> b] -> a -> [b] 
swing any  :: [a -> Bool] -> a -> Bool 
swing foldr  :: b -> a -> [a -> b -> b] -> b 
swing zipWith :: [a -> b -> c] -> a -> [b] -> [c] 
swing find  :: [a -> Bool] -> a -> Maybe (a -> Bool) 
swing partition :: [a -> Bool] -> a -> ([a -> Bool], [a -> Bool]) 

所有这些函数都完全按照您从类型中假定的方式进行。 (但要注意的是,维基是一个有点过时了。现在,大部分的这些功能会自动为任何Foldable类型的工作。)

您搜索可以从

swing mapMaybe :: [a -> Maybe b] -> a -> [b] 

,然后申请启动该功能listToMaybe。 (这两个函数都是从Data.Maybe

更一般的形式将(使用ClassyPrelude在一些约束噪声的成本充分的通用性,是

swing mapM :: (Monad m, Traversable t) => t (a -> m b) -> a -> m (t b) 

所以例如用headMay作为一个更一般的形式listToMaybe):

f :: (Traversable t, MonoFoldable (t b), Element (t b) ~ b) 
    => t (a -> Maybe b) -> a -> Maybe b 
f functions x = join $ headMay <$> swing mapM functions x 

是的,它可能把你的头变成糊状 - 但它像弯曲你的头看到一个笑脸,只与你的整个心灵。

相关问题