哇哦,这是一个旧的!我将首先清理一下代码并使其符合当前的惯用约定:
case class Flat[T, U](fn: T => List[U])
implicit def recFlattenFn[T, U](
implicit f: Flat[T, U] = Flat((xs: T) => List(xs))
) = Flat((xs: List[T]) => xs flatMap f.fn)
def recFlatten[T, U](xs: List[T3])(implicit f: Flat[List[T], U]) = f fn xs
然后,事不宜迟,分解代码。首先,我们有我们的Flat 类:
case class Flat[T, U](fn: T => List[U])
这只不过是函数T => List[U] 的命名包装器,当给定T 类型的实例时,该函数将构建List[U]。注意这里的T也可以是List[U],也可以是U,或者List[List[List[U]]]等。一般情况下,这样的函数可以直接指定为参数的类型。但是我们将在隐式中使用这个,因此命名包装器避免了任何隐式冲突的风险。
然后,从recFlatten 向后工作:
def recFlatten[T, U](xs: List[T])(implicit f: Flat[List[T], U]) = f fn xs
此方法将采用xs(List[T])并将其转换为U。为了实现这一点,它定位了Flat[T,U] 的隐式实例并调用封闭的函数fn
然后,真正的魔法:
implicit def recFlattenFn[T, U](
implicit f: Flat[T, U] = Flat((xs: T) => List(xs))
) = Flat((xs: List[T]) => xs flatMap f.fn)
这满足recFlatten要求的隐式参数,它还需要另一个隐式参数。最关键的是:
-
recFlattenFn 可以作为自己的隐式参数
- 它返回一个Flat[List[X], X],所以
recFlattenFn只会在T是List时被隐式解析为Flat[T,U]
- 如果隐式解析失败(即
T 不是List),隐式f 可以回退到默认值
也许这在其中一个例子的上下文中是最好的理解:
recFlatten(List(List(1, 2, 3), List(4, 5)))
-
T 类型被推断为List[List[Int]]
- 尝试对 `Flat[List[List[Int]], U] 进行隐式查找
- 这与递归定义的
recFlattenFn 匹配
广义地说:
recFlattenFn[List[List[Int]], U] ( f =
recFlattenFn[List[Int], U] ( f =
Flat[Int,U]((xs: T) => List(xs)) //default value
)
)
请注意,recFlattenFn 只会匹配对 Flat[List[X], X] 的隐式搜索,而类型参数 [Int,_] 则无法匹配,因为 Int 不是 List。这就是触发回退到默认值的原因。
类型推断也可以逆向运行该结构,在每个递归级别解析 U 参数:
recFlattenFn[List[List[Int]], Int] ( f =
recFlattenFn[List[Int], Int] ( f =
Flat[Int,Int]((xs: T) => List(xs)) //default value
)
)
这只是Flat 实例的嵌套,每个实例(最里面的除外)都执行flatMap 操作以展开嵌套List 结构的一层。最里面的Flat 只是将所有单独的元素包装在一个List 中。
Q.E.D.