Haskell 中不同类型的嵌套应用函子
Nested applicative functors of different types in Haskell
我想制作不同类型的嵌套应用函子。例如,不同类型的嵌套简单函子(在 ghci 中)工作正常:
Prelude> ((+2) <$>) <$> (Just [1..4])
Just [3,4,5,6]
但是对于不同类型的应用函子:
Prelude> ((*) <$>) <$> (Just [1,2,3]) <*> (Just [4,5,6,7])
<interactive>:56:1: error:
* Couldn't match type `[Integer -> Integer]' with `[Integer] -> b'
不工作!我想获得这样的东西:
Just [4,5,6,7,8,10,12,14,12,15,18,21]
我知道应用仿函数介于仿函数和单子之间。我可以将此练习视为有关 monad 转换器主题之前的初步练习。
在这种情况下,您需要:
liftA2 (*) <$> Just [1, 2, 3] <*> Just [4, 5, 6, 7]
或:
liftA2 (liftA2 (*)) (Just [1, 2, 3]) (Just [4, 5, 6, 7])
外层… <$> … <*> …
或liftA2
作用于Maybe
,而内层作用于[]
。如果你不知道这一点,你可以通过询问 GHCi 你应该放在那里的类型来弄清楚,例如带有类型的孔:
:t _ <$> (Just [1 :: Int, 2, 3]) <*> (Just [4 :: Int, 5, 6, 7]) :: Maybe [Int]
它回馈:
_ :: [Int] -> [Int] -> [Int]
您想要的组合列表的行为是 \ xs ys -> (*) <$> xs <*> ys
,可以缩写为 liftA2 (*)
。 ((*) <$>)
或 fmap (*)
不起作用,因为这只是你需要的一半:它在单个列表上运行(使用 Functor
),而你想组合两个(使用 Applicative
).
当然,liftA2 (liftA2 (*))
适用于任何两个嵌套的应用函子,其元素是数字:
(Applicative f, Applicative g, Num a)
=> f (g a) -> f (g a) -> f (g a)
例如,嵌套列表:
liftA2 (liftA2 (*)) [[1], [2], [3]] [[4, 5, 6]]
== [[4,5,6],[8,10,12],[12,15,18]]
-- (Transposing the inputs transposes the output.)
liftA2 (liftA2 (*)) [[1, 2, 3]] [[4], [5], [6]]
== [[4,8,12],[5,10,15],[6,12,18]]
或 Maybe
列表:
liftA2 (liftA2 (*)) [Just 1, Nothing, Just 3] [Just 4, Nothing, Just 6]
== [Just 4, Nothing, Just 6,
Nothing, Nothing, Nothing,
Just 12, Nothing, Just 18]
或者更奇特的东西,比如函数列表:
($ (3, 5)) <$> (liftA2 (+) <$> [fst, snd] <*> [snd, fst])
== [fst (3, 5) + snd (3, 5),
fst (3, 5) + fst (3, 5),
snd (3, 5) + snd (3, 5),
snd (3, 5) + fst (3, 5)]
== [3+5, 3+3, 5+5, 5+3]
== [8,6,10,8]
除了嵌套 lifts 和 fmaps,另一种组合应用仿函数的选择是 Data.Functor.Compose
新类型:
newtype Compose f g a = Compose { getCompose :: f (g a) }
例如:
ghci> let Compose result = (*) <$> Compose (Just [1,2,3]) <*> Compose (Just [4,5,6,7])
ghci> result
Just [4,5,6,7,8,10,12,14,12,15,18,21]
Applicative
表现得非常好,以至于单个新类型足以组成任何两个实例类型。而且除了嵌套还有其他的组合方式,比如Product
and Day
convolution:
data Product f g a = Pair (f a) (g a)
data Day f g a = forall b c. Day (f b) (g c) (b -> c -> a)
Monad
s 并没有很好地组合,所以我们需要一个 不同的 每个 monad 的新类型,以便用第一个 monad 的能力增强其他一些 monad。我们称这些新型 monad 转换器。
我们也可以用前奏函数来做这件事。不过你的第一部分很好。
((*) <$>) <$> (Just [1,2,3])
类型 Num a => Maybe [a -> a]
我们只需要将 Maybe monad 中的应用列表 fmap 到 Maybe monad 中的列表即可。因此,一种方法可能是将第一部分绑定到 (<$> Just [4, 5, 6, 7]) . (<*>) :: Num a => [a -> b] -> Maybe [b]
((*) <$>) <$> (Just [1,2,3]) >>= (<$> Just [4,5,6,7]) . (<*>)
屈服于
Just [(1*),(2*),(3*)] >>= (<$> Just [4,5,6,7]) . (<*>)
屈服于
([(1*),(2*),(3*)] <*>) <$> Just [4,5,6,7]
屈服于
Just [4,5,6,7,8,10,12,14,12,15,18,21]
我想制作不同类型的嵌套应用函子。例如,不同类型的嵌套简单函子(在 ghci 中)工作正常:
Prelude> ((+2) <$>) <$> (Just [1..4])
Just [3,4,5,6]
但是对于不同类型的应用函子:
Prelude> ((*) <$>) <$> (Just [1,2,3]) <*> (Just [4,5,6,7])
<interactive>:56:1: error:
* Couldn't match type `[Integer -> Integer]' with `[Integer] -> b'
不工作!我想获得这样的东西:
Just [4,5,6,7,8,10,12,14,12,15,18,21]
我知道应用仿函数介于仿函数和单子之间。我可以将此练习视为有关 monad 转换器主题之前的初步练习。
在这种情况下,您需要:
liftA2 (*) <$> Just [1, 2, 3] <*> Just [4, 5, 6, 7]
或:
liftA2 (liftA2 (*)) (Just [1, 2, 3]) (Just [4, 5, 6, 7])
外层… <$> … <*> …
或liftA2
作用于Maybe
,而内层作用于[]
。如果你不知道这一点,你可以通过询问 GHCi 你应该放在那里的类型来弄清楚,例如带有类型的孔:
:t _ <$> (Just [1 :: Int, 2, 3]) <*> (Just [4 :: Int, 5, 6, 7]) :: Maybe [Int]
它回馈:
_ :: [Int] -> [Int] -> [Int]
您想要的组合列表的行为是 \ xs ys -> (*) <$> xs <*> ys
,可以缩写为 liftA2 (*)
。 ((*) <$>)
或 fmap (*)
不起作用,因为这只是你需要的一半:它在单个列表上运行(使用 Functor
),而你想组合两个(使用 Applicative
).
当然,liftA2 (liftA2 (*))
适用于任何两个嵌套的应用函子,其元素是数字:
(Applicative f, Applicative g, Num a)
=> f (g a) -> f (g a) -> f (g a)
例如,嵌套列表:
liftA2 (liftA2 (*)) [[1], [2], [3]] [[4, 5, 6]]
== [[4,5,6],[8,10,12],[12,15,18]]
-- (Transposing the inputs transposes the output.)
liftA2 (liftA2 (*)) [[1, 2, 3]] [[4], [5], [6]]
== [[4,8,12],[5,10,15],[6,12,18]]
或 Maybe
列表:
liftA2 (liftA2 (*)) [Just 1, Nothing, Just 3] [Just 4, Nothing, Just 6]
== [Just 4, Nothing, Just 6,
Nothing, Nothing, Nothing,
Just 12, Nothing, Just 18]
或者更奇特的东西,比如函数列表:
($ (3, 5)) <$> (liftA2 (+) <$> [fst, snd] <*> [snd, fst])
== [fst (3, 5) + snd (3, 5),
fst (3, 5) + fst (3, 5),
snd (3, 5) + snd (3, 5),
snd (3, 5) + fst (3, 5)]
== [3+5, 3+3, 5+5, 5+3]
== [8,6,10,8]
除了嵌套 lifts 和 fmaps,另一种组合应用仿函数的选择是 Data.Functor.Compose
新类型:
newtype Compose f g a = Compose { getCompose :: f (g a) }
例如:
ghci> let Compose result = (*) <$> Compose (Just [1,2,3]) <*> Compose (Just [4,5,6,7])
ghci> result
Just [4,5,6,7,8,10,12,14,12,15,18,21]
Applicative
表现得非常好,以至于单个新类型足以组成任何两个实例类型。而且除了嵌套还有其他的组合方式,比如Product
and Day
convolution:
data Product f g a = Pair (f a) (g a)
data Day f g a = forall b c. Day (f b) (g c) (b -> c -> a)
Monad
s 并没有很好地组合,所以我们需要一个 不同的 每个 monad 的新类型,以便用第一个 monad 的能力增强其他一些 monad。我们称这些新型 monad 转换器。
我们也可以用前奏函数来做这件事。不过你的第一部分很好。
((*) <$>) <$> (Just [1,2,3])
类型 Num a => Maybe [a -> a]
我们只需要将 Maybe monad 中的应用列表 fmap 到 Maybe monad 中的列表即可。因此,一种方法可能是将第一部分绑定到 (<$> Just [4, 5, 6, 7]) . (<*>) :: Num a => [a -> b] -> Maybe [b]
((*) <$>) <$> (Just [1,2,3]) >>= (<$> Just [4,5,6,7]) . (<*>)
屈服于
Just [(1*),(2*),(3*)] >>= (<$> Just [4,5,6,7]) . (<*>)
屈服于
([(1*),(2*),(3*)] <*>) <$> Just [4,5,6,7]
屈服于
Just [4,5,6,7,8,10,12,14,12,15,18,21]