"concatMap" from mono-traversable 如何能够 "pull out" common argument?
How does "concatMap" from mono-traversable is able to "pull out" common argument?
我正在学习 Haskell 并且正在为 Yesod 做一个简单的 DB-seed 程序,这时我偶然发现了这个我觉得很难理解的行为:
testFn :: Int -> Bool -> [Int]
testFn a b = if b then replicate 10 a else []
Yesod GHCI 会话:
$ :t concatMap testFn [3]
concatMap testFn [3] :: Bool -> [Int]
$ (concatMap testFn [1,2,3]) True
[1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3]
它能够以某种方式 "pull" 从每个映射中提取第二个 "Bool" 到单个 curried 参数。
标准基础 Prelude GHCI 会话甚至拒绝编译此表达式:
$ :t concatMap testFn [3]
error:
• Couldn't match type 'Bool -> [Int]' with '[b]'
Expected type: Int -> [b]
Actual type: Int -> Bool -> [Int]
• Probable cause: 'testFn' is applied to too few arguments
In the first argument of 'concatMap', namely 'testFn'
In the expression: concatMap testFn [3]
原来 Yesod 使用 mono-traversable 库,它有自己的 concatMap
:
$ :t concatMap
concatMap
:: (MonoFoldable mono, Monoid m) =>
(Element mono -> m) -> mono -> m
以我目前的 Haskell 理解水平,我无法弄清楚类型在这里是如何分布的。有人可以向我解释(尽可能面向初学者)这个技巧是如何完成的吗?上面testFn
的哪一部分符合Element mono
类型?
我们首先列出一些我们知道的类型。 (为简单起见,我们假装数字是 Int
——这实际上并不相关。)
testFn :: Int -> Bool -> [Int]
[1,2,3] :: [Int]
True :: Bool
(concatMap testFn [1,2,3]) True
与 concatMap testFn [1,2,3] True
相同,因此 concatMap
必须具有匹配所有这些参数的类型:
concatMap :: (Int -> Bool -> [Int]) -> [Int] -> Bool -> ???
其中 ???
是结果类型。请注意,由于关联性规则,->
关联到右侧,因此上面的键入与:
相同
concatMap :: (Int -> (Bool -> [Int])) -> [Int] -> (Bool -> ???)
让我们把通用类型写在那个上面。我添加了一些空格来标记相似性。
concatMap :: (MonoFoldable mono, Monoid m) =>
(Element mono -> m ) -> mono -> m
concatMap :: (Int -> (Bool -> [Int])) -> [Int] -> (Bool -> ???)
啊哈!如果我们选择 m
作为 Bool -> [Int]
,并且选择 mono
作为 [Int]
,我们就有了匹配。如果我们这样做,我们就满足了约束 MonoFoldable mono, Monoid m
(见下文),并且我们也有 Element mono ~ Int
,所以一切类型检查。
我们从m
的定义推导出???
是[Int]
。
关于限制:MonoFoldable [Int]
,没什么可说的。 [Int]
显然是一个具有 Int
元素类型的类列表类型,这足以使其成为 MonaFoldable
并以 Int
作为其 Element
。
对于Monoid (Bool -> [Int])
,它有点复杂。我们有任何函数类型 A -> B
是一个幺半群,如果 B
是一个幺半群。随后以逐点方式执行操作。在我们的具体情况下,我们依赖 [Int]
是一个幺半群,我们得到:
mempty :: Bool -> [Int]
mempty = \_ -> []
(<>) :: (Bool -> [Int]) -> (Bool -> [Int]) -> (Bool -> [Int])
f <> g = \b -> f b ++ g b
我正在学习 Haskell 并且正在为 Yesod 做一个简单的 DB-seed 程序,这时我偶然发现了这个我觉得很难理解的行为:
testFn :: Int -> Bool -> [Int]
testFn a b = if b then replicate 10 a else []
Yesod GHCI 会话:
$ :t concatMap testFn [3]
concatMap testFn [3] :: Bool -> [Int]
$ (concatMap testFn [1,2,3]) True
[1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3]
它能够以某种方式 "pull" 从每个映射中提取第二个 "Bool" 到单个 curried 参数。
标准基础 Prelude GHCI 会话甚至拒绝编译此表达式:
$ :t concatMap testFn [3]
error:
• Couldn't match type 'Bool -> [Int]' with '[b]'
Expected type: Int -> [b]
Actual type: Int -> Bool -> [Int]
• Probable cause: 'testFn' is applied to too few arguments
In the first argument of 'concatMap', namely 'testFn'
In the expression: concatMap testFn [3]
原来 Yesod 使用 mono-traversable 库,它有自己的 concatMap
:
$ :t concatMap
concatMap
:: (MonoFoldable mono, Monoid m) =>
(Element mono -> m) -> mono -> m
以我目前的 Haskell 理解水平,我无法弄清楚类型在这里是如何分布的。有人可以向我解释(尽可能面向初学者)这个技巧是如何完成的吗?上面testFn
的哪一部分符合Element mono
类型?
我们首先列出一些我们知道的类型。 (为简单起见,我们假装数字是 Int
——这实际上并不相关。)
testFn :: Int -> Bool -> [Int]
[1,2,3] :: [Int]
True :: Bool
(concatMap testFn [1,2,3]) True
与 concatMap testFn [1,2,3] True
相同,因此 concatMap
必须具有匹配所有这些参数的类型:
concatMap :: (Int -> Bool -> [Int]) -> [Int] -> Bool -> ???
其中 ???
是结果类型。请注意,由于关联性规则,->
关联到右侧,因此上面的键入与:
concatMap :: (Int -> (Bool -> [Int])) -> [Int] -> (Bool -> ???)
让我们把通用类型写在那个上面。我添加了一些空格来标记相似性。
concatMap :: (MonoFoldable mono, Monoid m) =>
(Element mono -> m ) -> mono -> m
concatMap :: (Int -> (Bool -> [Int])) -> [Int] -> (Bool -> ???)
啊哈!如果我们选择 m
作为 Bool -> [Int]
,并且选择 mono
作为 [Int]
,我们就有了匹配。如果我们这样做,我们就满足了约束 MonoFoldable mono, Monoid m
(见下文),并且我们也有 Element mono ~ Int
,所以一切类型检查。
我们从m
的定义推导出???
是[Int]
。
关于限制:MonoFoldable [Int]
,没什么可说的。 [Int]
显然是一个具有 Int
元素类型的类列表类型,这足以使其成为 MonaFoldable
并以 Int
作为其 Element
。
对于Monoid (Bool -> [Int])
,它有点复杂。我们有任何函数类型 A -> B
是一个幺半群,如果 B
是一个幺半群。随后以逐点方式执行操作。在我们的具体情况下,我们依赖 [Int]
是一个幺半群,我们得到:
mempty :: Bool -> [Int]
mempty = \_ -> []
(<>) :: (Bool -> [Int]) -> (Bool -> [Int]) -> (Bool -> [Int])
f <> g = \b -> f b ++ g b