我想知道在标准包中的某处定义了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
ArtistId
和UserId
,仅保留fetchForSuggestions
?
答案 0 :(得分:2)
使用AMP,您应该只能写f <$> a <*> b
,f :: (Applicative m) => a -> b -> c
,a :: (Applicative m) => m a
和b :: (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
等,而且我认为它不存在。