跳转至

理论题

题干

traverseHaskell 语言的一个重要函数。其类型为:

(a -> f b) -> t a -> f (t b)

示例:

traverse (\a -> if a > 0 then Just a else Nothing) [1, 2, 3, 4]

上例可以得到 Just [1, 2, 3, 4]

问题:

  1. 将上例中的 [1, 2, 3, 4] 变成 [-1, 2, 3, 4] 会得到什么?
  2. 根据类型信息,将函数应用到 a 后得到的应该是 t (f b)。请说明你的疑问,并给出猜测。 提示:traverse 要求在类型 t 上定义“某个操作”。请说明该操作的作用,并推断当 t = List 时其实现。用自然语言描述即可。
  3. traverse (\i -> [0..i]) [0, 1, 2, 3] 会得到什么?
  4. 本题预设 t 是一个满足某个约束的类型。请分析本题语境,猜测 t 需要满足的约束。 提示:本题把将 a -> f b 应用到 t a 上看成应用到内部 a 上。

材料和解释

  1. a b f t 都是类型变量(即不是某个特定的类型,而是一系列可行的类型)。其中,t f 可以看成接收类型而给出类型的类型,可以视为某种“上下文”类型,即它为内部类型赋予了额外的信息。例如,[Int] 为整型添加了数组这一上下文,使得它可以表示多个整型,换句话说表示了“类型为整型的可能性”这样一个类型。
  2. 从语义上讲,traverse 作用在于,假如对于某一个被上下文 t 包裹的 a 进行操作导致产生了新的上下文 f,其可以将上下文 f 提到外部,并保留内部 t 的结构。
  3. 示例表达式该使得 traverse 的类型实例化(用具体类型取代)到了 (Int -> Maybe Int) -> [Int] -> Maybe [Int]
  4. type Maybe a = Just a | Nothing
  5. Maybe 表示“可能不存在”,List(通常也表示为[])表示“多种可能”。
  6. 在某种约束下,上下文类型 f 允许以下函数的存在:f (a -> b) -> f a -> f b
    • 显然,函数可以被推广到任意元数。
    • MaybeList 都是符合条件的 f。其中:
    • Maybe 上的该函数的语义为:如果被包裹在上下文中的函数或值有任意一个不存在,则结果不存在。
    • List 上的该函数的语义为:被包裹在上下文中的函数(们)和值(们)进行组合并应用。
  7. 柯里化:任何函数都是一元函数。多元函数到等价一元函数的转换被称为“柯里化”。柯里化步骤如下:消费一个元,并给出一个消费剩下元而后给出结果的函数。
    • f(a,b) = a + b 柯里化后可以读成“给定某个 a,可以得到一个函数,这个函数是:给定一个 b,得到 a + b”。注意,第二个函数给出的结果中的 a 是被第一个函数的参数“约束”了的。这种“越级现象”称为“函数闭包”。柯里化是基于函数闭包的。
    • Haskell 中,a -> b -> c 就可以视为接收任意类型 a 的变量,再接收任意类型 b 的变量,最后给出任意类型 c 的变量的“二元函数”。显然,-> 符号是右结合的,也就是说,它等价于 a -> (b -> c),而不是 (a -> b) -> c
  8. Haskell 中的 lambda 表达式 \a -> c 表示接收 a 作为参数,给出 c

文档和工具

  1. Haskell API 文档库:hoogle
  2. 可以使用 ghc 及其附带的 ghci (Haskell 的 REPL 工具)试验。
  3. 请自由使用任何资源和工具。