在默认库中是否有bindN?

时间:2014-11-05 09:24:26

标签: haskell monads applicative

我想知道在标准包中的某处定义了bind2,bind3等类型的函数吗?

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

为什么我想要那个?因为我希望将绑定减少到最低限度。例如,如果我们使用免费monad方法来构建自动并发异步计算,或者像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)

我在这里介绍一些反模式吗?我应该这样做:

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 ArtistIdUserId,仅保留fetchForSuggestions

1 个答案:

答案 0 :(得分:2)

使用AMP,您应该只能写f <$> a <*> bf :: (Applicative m) => a -> b -> ca :: (Applicative m) => m ab :: (Applicative m) m b。自从AMP在GHC Head中经历(截至2014年9月),您现在可以实现这一目标!您的代码变为:

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

这对于GHC Head中的任何Monad都适用,我认为它也适用于7.8.3,并且它肯定会在7.10中工作。

整洁的是,这是一般的。对于任何n-ary动作k :: a_1 -> ... -> a_n -> m c,您可以编写

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

但总的来说,这种表示法并不比仅仅使用多个do绑定好得多。我认为你必须仔细考虑哪一个更具可读性。使用多个绑定不是反模式,事实上经常发生。 bind2语法几乎不需要do等,而且我认为它不存在。