在编码工厂以生成 'X' 类型项目的容器时,如何一般地处理 Scala 高级类型

how to generically handle Scala Higher-kinded types when coding factories for generating containers of items of type 'X'

在研究了 this tutorial 中 Scala 高等类型的一些示例后,我开始想知道是否有可能编写一个方法来一般地处理一个特征的两个子类 被定义为更高种类的类型。

本教程定义了这个特征(稍微复杂一点的版本):

trait ContainerFactory[M[_]] { def put[A](x: A): M[A] }

我理解为类型参数化工厂的签名,它创建不同种类的容器(ListsSets 等,其中容器的类型由 M) 并且通过 put 方法插入到容器中的对象的类型由 A 给出。在您所在的调用站点(我认为这是正确的术语) 实例化容器,你指定你想要的容器类型(如注释行://factory for List containers)

val factory = new ContainerFactory[List] { def put[A](x: A) = List(x)  } // factory for List containers
factory.put("dog") // insert an element of type String to factory
res5: List[String] = List(dog)          //  and you get a List of String

factory.put(1)// insert an element of type Int to factory
res6: List[Int] = List(1) // and you get a List of Int

val factory2 = new ContainerFactory[Set] { def put[A](x: A) = Set(x)} // factory for Set containers
factory2.put("dog")
factory2.put(1)

我的目标是创建一个采用 ContainerFactory 的方法 以及要放入生成的容器中的对象。我希望该方法生成适当的容器(ListSet)参数化以保存我作为第二个对象传入的对象类型。

我认为像下面这样的方法真的很酷而且很有用,但是我在使用 Scala 语法时遇到了问题,无法让它工作。事实上,我什至不知道这是否可能。

// Code below does not compile
// Method for generating container (of type defined by first arg) that contains the second argument, and 
// which (after instantiation) is parameterized to hold  only objects of that type:

def genContainer[M[T]](factory: ContainerFactory[M], item : T) = {
    factory.put(item)
}


genContainer(factory2, "x")
// desired return value =>    Set[String] = Set(x)

genContainer(factory, 11)
// desired return value =>    List[Int] = List(11)

注意:当我尝试定义 genContainer 时得到的错误是:

<console>:10: error: not found: type T
       def genContainer[M[T]]( factory :  Container[M]  ,  item : T) = {

注意 2:我可以定义一个像这样的方法,它采用泛型 ContainerFactory

def genContainer[M[T]](factory:  ContainerFactory[M]) = { }

但是当我尝试将第二个参数指定为 T 类型(在参数化中引用)时,我收到有关 T 未找到的错误。

你们真的很亲密:

def genContainer[T, M[_]](factory:  ContainerFactory[M], item: T) = {
    factory.put(item)
}

您所要做的就是将每个类型参数指定为顶级类型参数!而且编译器足够聪明,可以在很多情况下推导出这些类型参数:

val factory = new ContainerFactory[List] { def put[A](x: A) = List(x)  }
genContainer(factory, "foo") //No need to specify the type parameters!

你非常接近。问题是你需要单独声明你的类型参数:

def genContainer[T, M[_]](factory: ContainerFactory[M], item: T) =
  factory.put(item)

这有点令人困惑,因为编译了以下内容:

def genContainer[M[T]](factory: ContainerFactory[M]) = "whatever"

此处T的范围仅限于M[...]的内部(详见the language specification的4.4节)。当你声明像 M[T <: Foo[T]] 这样的花哨边界时,这可能很方便,但通常给类型构造函数的类型参数一个名称只是噪音,最好使用 M[_](这正是相当于M[A]).