2014-11-05 4 views
2

Интересно, существуют ли функции bind2, bind3 и т. Д., Определенные где-то в стандартных пакетах?Есть ли bindN в библиотеках по умолчанию?

bind2 :: (Monad m, Applicative m) => (a -> b -> m c) -> m a -> m b -> m c 
bind2 f a b = join (liftA2 a b) 

Почему я хочу этого? Потому что я хочу уменьшить привязки до минимума. Например, если мы используем бесплатную монады подход для создания автоматически параллельных вычислений асинхронное, или выбирает, как и в Haxl:

fetchForSuggestions :: Artist -> [Like] -> Async [Suggestions] 
fetchForSuggestions = error "implement me" 

-- Two binds! 
action :: ArtistId -> UserId -> Async [Suggestions] 
action artistId userId = do 
    artist <- fetchArtist artistId 
    likes <- fetchUserLikes userId 
    fetchForSuggestions artist likes 

-- Single bind 
-- here artist and user likes could be fetched concurrently 
action :: ArtistId -> UserId -> Async [Suggestions] 
action artistId userId = bind2 fetchForSuggestions (fetchArtist artistId) (fetchUserLikes userId) 

Am I представляя своего рода анти-шаблон здесь? Если я пытаюсь сделать:

complexAction :: ParamA -> ParamB -> ParamC -> Async Result 
complexAction a b c = do 
    (x, y, z) <- (,,) <$> subActionX a b <*> subActionY b c <*> subActionZ c a 
    (i, j, k) <- (,,) <$> subActionI a x <*> subActionJ b y <*> subActionK c z 
    finalAction i j k x y z 

subAction, где каждая функция связывания свободной? То есть удалить action принимая ArtistId и UserId, и оставить только fetchForSuggestions?

+0

Похоже, вы могли бы воспользоваться функцией '{- # LANGUAGE ApplicativeDo # -}'. :) https://ghc.haskell.org/trac/ghc/wiki/ApplicativeDo –

ответ

2

С AMP вы должны быть в состоянии просто написать f <$> a <*> b, где f :: (Applicative m) => a -> b -> c и a :: (Applicative m) => m a и b :: (Applicative m) m b. Поскольку AMP прошел (по состоянию на сентябрь 2014 года) в GHC Head, вы можете это сделать прямо сейчас! Ваш код будет выглядеть так:

action artistId userId = 
    join (fetchForSuggestions <$> fetchArtist artistId <*> fetchUserLikes userId) 

Это будет работать для любого Монады в GHC Head прямо сейчас, я думаю, что это будет также работать для 7.8.3, и это будет наиболее уверенно работать в 7.10.

Утонченная вещь в том, что это вообще. Для любого п-арной действия k :: a_1 -> ... -> a_n -> m c, вы можете написать

join (k <$> (x_1 :: a_1) <*> ... <*> (x_n :: a_n)) :: m c 

В целом, однако, эта запись не очень лучше, чем просто с использованием нескольких do привязок. Я думаю, вам нужно будет тщательно рассмотреть, какой из них будет более читабельным. Использование нескольких связок не является антипаттерном и происходит довольно часто. A bind2 и т. Д. В значительной степени делается ненужным синтаксисом do, и я не думаю, что он существует.

+0

В 'f <$> x <*> y', 'f' имеет тип' a -> b -> c' обычно. Вам не хватает 'join', в котором я нуждаюсь. – phadej

+0

Вы, конечно, правы. Это то, что я получаю за то, что просто быстро выбрасывал ответ на работу: -P –

+0

'do {a <- ma; b <- mb; f a b} 'имеет два' >> = 'при снятии. Для меня важно иметь как можно меньше связок. Я переписал бы его на 'do {(a, b) <- (,) <$> ma <*> mb; f a b} 'или' join (f <$> ma <*> mb) ', как вы прокомментировали, но' bind2 f ma mb' гораздо читабельнее IMHO. ApplicativeDo решит эту проблему, но ее пока нет. – phadej