【问题标题】:The most general type of "product" of two functions in HaskellHaskell中两个函数的最一般的“乘积”类型
【发布时间】:2015-09-30 11:51:27
【问题描述】:

我必须从 Haskell 中的给定函数中找到最通用的类​​型,或者更确切地说,找到两个函数的“乘积”的最通用类型(如果存在)。我不确定,但也许我应该使用 Robinson 统一算法,但我无法理解。我需要一个详细的解决方案,一步一步来,这样我就明白了。

功能:

map :: (a → b) → [a] → [b] 
iterate :: (a → a) → a → [a]

如何找到最通用的类​​型

  1. map iterate
  2. iterate map

这不是家庭作业。

【问题讨论】:

  • “一般”是什么意思?类型应该泛化吗?转变?请解释一下。
  • 类型应该是泛化的。例如:typ(map) = typ(iterate) = typ(map iterate) 或 typ(iterate) = typ(map) = typ(iterate map)。但是我不确定这些转换。
  • 告诉我们您尝试了什么,以及是什么阻止了尝试成功。
  • 我想到的另一个想法:这听起来很像家庭作业问题;如果是,您的老师(或助教)可能愿意亲自与您详细讨论。
  • 另外,您是否尝试过根据 GHCi 推断的类型检查您的直觉?

标签: haskell types type-inference unification


【解决方案1】:

首先,明确写出所有省略的括号。

为了推导出a -> b 类型函数和c 类型值的应用类型,我们需要统一ac 类型,注意任何结果类型等价。那么应用程序的类型是b,在上述等价下。规则是:

f   ::     a → b
x   ::     c
        ----------
f x ::         b      , { a ~ c }

要半机械地找到这个,只需对齐类型并注意等价:

map ::    (   a1     →     b1     ) → ([a1] → [b1]) 
iterate :: (a2 → a2) → (a2 → [a2])
          --------------------------
        { a1 ~ (a2 → a2) , b1 ~ (a2 → [a2]) }

所以,

map iterate :: [a1] → [b1]

代入等价,我们得到

            :: [a2 → a2] -> [a2 → [a2]]

现在我们可以将 a2 重命名为 at 或其他任何名称。您的第二个示例类似:

iterate :: (   a2     →       a2     ) → (a2 → [a2])
map ::      (a1 → b1) → ([a1] → [b1]) 
           ---------------------------
        { a2 ~ a1 → b1 ,  a2 ~ [a1] → [b1] }

由于a2 ~ a2,我们得到a1 → b1 ~ [a1] → [b1]

               a1  →  b1
              [a1] → [b1]
            --------------
        { a1 ~ [a1] ,  b1 ~ [b1] }

这两个等价都是不可能的,定义一个无限类型。所以第二个例子在 Haskell 中没有类型:

*Main> :t 映射迭代
地图迭代 :: [a -> a] -> [a -> [a]]

*Main> :t 迭代地图

:1:9:
发生检查:无法构造无限类型:a ~ [a]
预期类型:(a -> b) -> a -> b
实际类型:(a -> b) -> [a] -> [b]
在'iterate'的第一个参数中,即'map'
在表达式中:迭代地图

:1:9:
发生检查:无法构造无限类型:b ~ [b]
预期类型:(a -> b) -> a -> b
实际类型:(a -> b) -> [a] -> [b]
在'iterate'的第一个参数中,即'map'
在表达式中:迭代映射

【讨论】:

  • 我现在很困惑,rampion 写的不是正确的吗?
  • @Vucicasena 他的回答是关于两个函数的函数组合类型(f . g);我将它展示给一个应用程序 (f g)。
  • 我的练习是关于函数应用,而不是函数组合?
  • @Vucicasena 这就是我的想法。但你对此有点不清楚。 :) 但由于 (.) 本身也是一个函数,您可以采用这种方法并将其用于函数组合的情况:f . g === (.) f g === ((.) f) g。如果你这样做了,你会看到(.) 有它自己的类型规则:它的类型是(b -> c) -> (a -> b) -> (a -> c),所以为了使f . g 有意义,f 必须是b -> cg :: a -> b 类型,即g的输出类型必须与f的输入类型统一。
【解决方案2】:

让我们问 GHCi:

ghci> :type map . iterate
map . iterate :: (a -> a) -> [a] -> [[a]]
ghci> :type iterate . map
iterate . map :: (a -> a) -> [a] -> [[a]]

让我们看看它是如何得到它们的:

给定

map     :: (x -> y) -> [x] -> [y]
iterate :: (b -> b) -> b -> [b]
(.)     :: (s -> t) -> (r -> s) -> (r -> t)

并使用a ~ b 表示“ab 类型相等”。

然后在map . iterate 我们有

map :: s -> t where
 s ~ x -> y
 t ~ [x] -> [y]

iterate :: r -> s where
 r ~ b -> b
 s ~ b -> [b]

so
 x -> y ~ s ~ b -> [b]
=>
 x ~ b
 y ~ [b]
 t ~ [b] -> [[b]]

所以map . iterate :: (b -> b) -> [b] -> [[b]]

然后在iterate . map 我们有

iterate :: r -> s where
 s ~ b -> b
 t ~ b -> [b]

map :: s -> t where
 r ~ x -> y
 s ~ [x] -> [y]

so
 b -> b ~ s ~ [x] -> [y]
 b ~ [x]
 b ~ [y]
 x ~ y
 r ~ x -> x
 t ~ [x] -> [[x]]

所以iterate . map :: (x -> x) -> [x] -> [[x]]

虽然这些函数具有相同的类型,但它们的行为却截然不同。

  • iterate . map 将给定一个函数和一个长度为 k 的列表,生成一个长度为 k 的列表的无限列表。
  • map . iterate 将给定一个函数和一个长度为k 的列表,生成一个长度为k 的无限列表。

【讨论】:

  • 注意~是类型相等?这对 OP 来说可能并不明显。
  • 另外,这个问题似乎可能是在谈论iterate mapmap iterate 而不是作品(不清楚)。
  • 我在纸上又写了一遍,试图理解你写的东西,我终于明白了。非常感谢,但我还有几个问题。我们也可以为第一个函数组合 map.iterate 的“r”写:r~ b -> [b] 然后 t~ [b] -> [[b]]。因为你为 iterate.map 显示了“r”但不适用于 map.iterate 这就是为什么我有点困惑。第二个问题是,(a->a) 和 (a->b) 有什么区别?
  • Vucicasena:很抱歉给您带来了困惑。我之前在map . iterate中讨论iterate的类型时提到了r ~ b -> b,到最后仍然成立。至于你的第二个问题,a -> aa -> b 的区别在于后者匹配 any 函数,而前者只匹配自同构,即从类型 a 到自身的函数.
  • 所以写 r~b -> [b] 而不是 r~b -> b 是不正确的吗?如果我们用 map (a -> a) 而不是 ( a -> b) 或者我们会得到相同的结果吗?
猜你喜欢
  • 2012-02-18
  • 1970-01-01
  • 2017-08-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多