提问人:coderodde 提问时间:9/29/2023 更新时间:11/7/2023 访问量:129
多个级别的函子
Functor over multiple levels
问:
我有这个蹩脚的尝试:
fmap2 :: (Functor f, Functor g) => (a -> b) -> f (g a) -> f (g b)
fmap2 f f2 = (fmap2 f . fmap f2)
它应该像这样工作:
fmap2 negate [[1,2], [3]] -- Returns [[-1,-2],[-3]]
fmap2 head [Just "abc",Nothing,Just "def"] -- Returns [Just 'a',Nothing,Just 'e']
请注意,这是一个练习。(我试了 1,5 个小时,但没有找到任何解决方案。
问:我在那里做错了什么?
答:
当你陷入这样的事情时。我建议使用类型孔,因为它们会引导您朝着正确的方向前进
fmap2 :: (Functor f, Functor g) => (a -> b) -> f (g a) -> f (g b)
fmap2 f nested_functor = fmap _something nested_functor
-- | | |- notice this underscore
-- | |- clearly we need fo fmap something over the nested_functor but we don't know what yet
-- |- Use better names usually help. Haskellers tend to use ver short names, but that's not appropiate always
如果您尝试编译此代码,会注意到 并会为您建议一个类型ghc
_something
Found hole: _something :: g a -> g b <-- This is interesting
Where: ‘a’, ‘g’, ‘b’ ... (blah blah blah, not interesting)
Or perhaps ‘_something’ is mis-spelled, or not in scope
我们知道需要有给定的类型。因此:_something
g a -> g b
Functor g
fmap2 :: (Functor f, Functor g) => (a -> b) -> f (g a) -> f (g b)
fmap2 f nested_functor = fmap something nested_functor
where
-- remember something :: Functor g => g a -> g b
something = undefined -- left as an exercise
评论
f
nested_functor
fmap
Functor g
something
f nested_functor
f fmap
nested_functor fmap
nested_functor f
fmap f
fmap nested functor
您应该使用函子映射作为内部结构的函数,因此如下所示:fmap
fmap2 :: (Functor f, Functor g) => (a -> b) -> f (g a) -> f (g b)
fmap2 f = fmap (fmap …)
我把填写部分作为练习。…
评论
当用于此时
fmap2 head [Just "abc",Nothing,Just "def"] -- Returns [Just 'a',Nothing,Just 'e']
- 什么是类型?它
head
[Char] -> Char
¹; - 什么类型?它。
[Just "abc",Nothing,Just "def"]
[Maybe [Char]]
所以你有一个函数,你想把它应用到函子里面的 s,而函子又是函子里面的。[Char] -> Char
[Char]
Maybe
[]
一个允许您通过其中一层,因此您需要 2 个才能通过 2 个。看:fmap
head $ "abc" == 'a'
fmap head $ Just "abc" == Just 'a'
fmap head $ Nothing == Nothing
fmap (fmap head) $ [Just "abc",Nothing,Just "def"] == [Just 'a',Nothing,Just 'e']
是的,是多态的,但在上述情况下,它是在 上操作的。head
[a] -> a
[Char]
import Test.QuickCheck
fmap2 :: (Functor f, Functor g) => (a -> b) -> f (g a) -> f (g b)
fmap2 = fmap . fmap
prop_negate :: Property
prop_negate = property (fmap2 negate [[1,2],[3]] == [[-1,-2],[-3]])
prop_head :: Property
prop_head = property (fmap2 head [Just "abc", Nothing, Just "def"]
== [Just 'a', Nothing, Just 'd'])
因此,让我们并排看一下这两个版本:
fmap2 :: (Functor f, Functor g) => (a -> b) -> f (g a) -> f (g b)
fmap2 f f2 = (fmap2 f . fmap f2)
和
fmap2 :: (Functor f, Functor g) => (a -> b) -> f (g a) -> f (g b)
fmap2 = fmap . fmap
-- or
fmap2 :: (Functor f, Functor g) => (a -> b) -> f (g a) -> f (g b)
fmap2 fab fga = (fmap . fmap) fab fga
在第一个版本中,您可以在其中引用,使其递归。我不认为你是故意的。嵌套函子不是递归的情况,因为第二个测试用例清楚地表明了列表函子中的函子——绝对不是递归重复。fmap2
fmap2
Maybe
下一个问题是给定给 的两个参数。在类型签名中,两个参数被正确标识为 1) 函数和 2) 嵌套函子,即类型为“f”的函子内类型为“g”的函子。这里的两个参数非常不同 -- 一个应用于嵌套函子的函数和嵌套函子本身。在函数绑定 () 中,这两个参数似乎是对称的——与第一个参数的位置相同——但正如我们刚才在类型签名中看到的那样,它们是两个不同的东西:一个函数和一个函子,所以很明显它们不能对称地使用。要么将这两个不同的东西视为两个函数或两个函子。我怀疑您正在考虑两个函子(f 型和 g 型)。似乎您正在尝试在此处分别覆盖两个嵌套函子。正如你所看到的,这两个函子应该映射在一起。fmap2
a -> b
fmap2 f f2 = (fmap2 f . fmap2 f2)
fmap
第二个版本以两种形式编写。在第一种形式中,“无点”的写作方式,被简单地描述为两次的组成。由于我们想将函数应用于两个嵌套函子中类型为“a”的值,因此两次的组合会跳过第一个函子(类型 f),然后跳过第二个函子(类型 g),以获得类型“a”的值并应用转换函数。fmap2
fmap
a -> b
fmap
在第二个版本的第二种形式中,参数是显式给出的。(它们只是在无点版本中隐含的,这应该很容易看到。这应该有助于澄清第一个版本中的错误参数。转换函数和嵌套函子扮演不同的角色。double 向下挖掘两次以获得类型“a”的值并应用函数 ()。fab
fga
fmap
fga
fab
a -> b
因此,保留了嵌套函子的结构,即 的“fg”部分,但类型“a”的值可以转换为类型“b”的值(在 的情况下不一定不同,但可能不同,就像在我们的示例中接受字符串并返回一个字符的情况一样)。这样,应用于嵌套函子的 type 函数将产生相同的嵌套函子,内部(可能)有不同的值,这是编译器对类型签名的期望。fga
negate
head
a -> b
(f (g a))
(f (g b))
评论
f2
fmap2
h :: a->b
?? :: f (g a) -> f (g b)
h
fmap h
??