2016-05-08 83 views
4

我試圖編寫一個類似於Data.Map.unionWith的函數,但可能會失敗。原來的那個使用了Maybe,它確實是一個Monad,所以這個Monadic對我來說工作得很好。但我想知道它是否可以用Applicative重寫,因爲我用純的方法來映射它,只是爲了滿足unionWith的類型需求。或者與Data.Map中的其他函數而不是unionWith?我可以用Applicative而不是Monad來重寫這個unionWith-like函數嗎?

{-# LANGUAGE RankNTypes #-} 
import Control.Monad 
import Data.Map 
unionWithM :: (Monad m, Traversable t) 
      => (forall a. (a -> a -> a) 
       -> t a 
       -> t a 
       -> t a 
      ) 
      -> (v -> v -> m v) 
      -> t v 
      -> t v 
      -> m (t v) 
unionWithM u f a b = sequenceA (u f' (pure <$> a) (pure <$> b)) 
    where f' x y = join $ f <$> x <*> y 

unionWithOriginal :: Ord k => (a -> a -> Maybe a) -> Map k a -> Map k a -> Maybe (Map k a) 
unionWithOriginal f a b = sequenceA (unionWith f' (Just <$> a) (Just <$> b)) 
    where f' x y = join $ f <$> x <*> y 
+0

AFAICS,'unionWithM'是不可能的:'unionWith'可以養活'v'結果'v - > v - > mv'到另一個'v - > v - > mv'調用。這接近Kelisli組合,它需要monads。 – chi

回答

7

是的,你可以,但你需要一箇中間數據結構。 問題是,在應用函數之前,您將地圖的值包裹起來,這就是爲什麼您的f'類型爲m a -> m a -> m a。要將f轉換爲f',您需要join,即Monad。訣竅是在聯合後應用函數。 因爲你可以使用(Maybe a, Maybe a)這有點麻煩,所以你可以使用These數據類型。如果我們將其推廣手動你

data These a b = That a | This b | These a b 

unionWith' f a b = let theses = unionWith These (That <$> a) (This <$> b) 
        in sequenceA (f' <$> theses) 
    where f' (That a) = pure a 
      f' (This b) = pure b 
      f' (These a b) = f a b 

如果使用these包,您可以在簡化到

unionWith'' f a b = sequenceA $ alignWith (these pure pure f) a b 
+0

我看到'let'沒有'in'。 – dfeuer

+0

我不得不說,我不喜歡這個解決方案,雖然我upvoted它,好吧,它無論如何工作,謝謝。 –

+0

@dfeur確實。固定 – mb14

相關問題