我有f
类型的函数Integral => a -> a -> Maybe a
。这意味着
f 1 2
是有效的代码,但是像
这样的自然组合f (f 3 4) 5
f 6 (f 7 8)
f (f 9 10) (f 11 12)
无效。
有很多方法可以让它发挥作用。例如,
f 6 =<< f 7 8
对于案例2,是我所知道的最好的方式。对于案例3,
join $ f <$> f 9 10 <*> f 11 12
是我能想到的最好的,但额外的join
像拇指一样伸出来。对于案例1,我没有一个好主意。除此之外,困扰我的是我提出的语法不是“一致” - 我实现了基本相同的东西,但语法非常不同。
是否有标准/惯用的结构允许我将f
转换为Maybe a -> a -> Maybe a
,a -> Maybe a -> Maybe a
和Maybe a -> Maybe a -> Maybe a
,并且语法一致且一致?
答案 0 :(得分:7)
让我们为第一种情况创建一个实用函数:
lift :: (a -> b -> Maybe c) -> Maybe a -> b -> Maybe c
lift _ Nothing _ = Nothing
lift f (Just a) b = f a b
案例1:
lift f (f 3 4) 5
案例2:
f 6 =<< f 7 8
案例3:
lift f (f 9 10) =<< f 11 12
虽然它看起来更加一致,但在我看来它仍然看起来很难看。也许其他人可以提供更好的解决方案。
编辑:在考虑了我的解决方案后,我意识到它可以推广:
(<&>) :: (Applicative m, Monad m) => m (a -> m b) -> m a -> m b
f <&> a = join $ f <*> a
infixl 4 <&>
案例1:
f <$> f 3 4 <&> pure 5
案例2:
f <$> pure 6 <&> f 7 8
案例3:
f <$> f 9 10 <&> f 11 12
这适用于任意arity的功能:
f <$> a <&> b -- f :: a -> a -> Maybe a
f <$> a <*> b <&> c -- f :: a -> a -> a -> Maybe a
f <$> a <*> b <*> c <&> d -- f :: a -> a -> a -> a -> Maybe a
依旧......
编辑:我与@n.m一起同意。最佳解决方案是将函数的类型签名更改为Maybe a -> Maybe a -> Maybe a
,并在必要时使用Just
。
案例1:
f (f (Just 3) (Just 4)) (Just 5)
案例2:
f (Just 6) (f (Just 7) (Just 8))
案例3:
f (f (Just 9) (Just 10)) (f (Just 11) (Just 12))
最简单的解决方案始终是最好的。
编辑:实际上,回想起来,我以前的解决方案要简单得多。
答案 1 :(得分:2)
这不是标准或惯用的,但它可以解决您使用已关闭的问题
打字的家庭。
你想要的是一个超级return
,如果值已经被包装,它就不会包装一个值,以便return' 4 = Just 4
和return' (Just 4) = Just 4
。
这样做的一种方法是
{-# LANGUAGE TypeFamilies, FlexibleInstances, OverlappingInstances #-}
import Control.Applicative
type family Maybed a where
Maybed (Maybe a) = Maybe a
Maybed a = Maybe a
class Returnable a where
return' :: a -> Maybed a
这里需要封闭式familie 因为Maybe a
符合机器人规则。封闭式familes允许您定义重叠的规则并使用第一个匹配的规则。
Maybe
实例很简单
instance Returnable (Maybe a) where
return' = id
普通实例有点棘手instance Returnable a
与前一个实例重叠。为此,我们需要一种方式来说a is not a Maybe
。幸运的是,如果a
与Maybe
相同,我们可以表达Maybe a
不是Maybed a
这一事实。
instance (Maybe a ~ Maybed a) => Returnable a where
return' a = return a
瞧瞧!
您现在可以写一个lift'
函数
lift' f a b = do
a' <- return' a
b' <- return' b
f a' b'
对a
和Maybe a
的每个组合使用提升函数。
f a 0 = Nothing
f a b = Just (a `div` b)
f' :: ( Returnable a
, Returnable b
, Maybed a ~ Maybe a'
, Maybed b ~ Maybe a'
, Integral a')
=> a -> b -> Maybed a
f' = lift' f
唯一的问题是,因为类型系列无法推断类型向后,您必须为每个参数指定类型,因此我将使用{{ 1}}和x
示例。
y