如何编码工厂以生成类型为'

时间:2015-04-23 23:06:12

标签: scala functional-programming

this tutorial中完成Scala高级类型的一些示例之后,我开始想知道是否有可能编写一个通常处理特征的两个子类的方法 被定义为更高级的类型。

本教程定义了这个特性的一个稍微复杂的版本:

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

我理解为类型参数化工厂的签名,它创建了不同类型的容器(ListsSet等,其中容器的类型由M给出并且A给出了通过put方法插入容器的对象类型的位置。在你所在的调用网站(我认为这是正确的术语) 实例化容器,指定所需容器的类型(如注释中所示://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未找到的错误。

2 个答案:

答案 0 :(得分:2)

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

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])。

答案 1 :(得分:1)

你真的很亲密:

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!