mono-traversable中的“ concatMap”如何能够“抽取”常见参数?

问题描述 投票:3回答:1

[我正在学习Haskell,并在偶然发现这种行为时发现它是为Yesod做的一个简单的DB种子程序,我很难理解:

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]

以某种方式,它能够从每个映射中“拉出”第二个“布尔”成为一个单独的咖喱参数。

[标准基本前奏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-traversableconcatMap库:

$ :t concatMap
concatMap
  :: (MonoFoldable mono, Monoid m) =>
     (Element mono -> m) -> mono -> m

以我目前对Haskell的理解水平,我无法弄清楚这里的类型如何分布。有人可以向我(尽可能多地面向初学者)解释这个技巧是如何完成的吗?上面testFn的哪一部分符合Element mono类型?

haskell traversal
1个回答
0
投票

我们首先列出一些我们知道的类型。 (为简单起见,我们假装数字为Int -这并不是很重要。)

testFn :: Int -> Bool -> [Int]
[1,2,3] :: [Int]
Bool

[(concatMap testFn [1,2,3]) TrueconcatMap 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,因此所有类型检查。

我们从???的定义推断[Int]m

© www.soinside.com 2019 - 2024. All rights reserved.