变换a - > a - >也许是采取任何组合和可能的组合

时间:2014-10-23 02:16:25

标签: haskell

我有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 aa -> Maybe a -> Maybe aMaybe a -> Maybe a -> Maybe a,并且语法一致且一致?

2 个答案:

答案 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 4return' (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。幸运的是,如果aMaybe相同,我们可以表达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'

aMaybe 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