多集的高级类型

时间:2015-03-05 18:44:55

标签: scala higher-kinded-types

我想在Scala中编写一个Multiset[T, S[_]]类,它有两个类型参数:T是元素的类型,而S是集合的基础表示。 在此多集中,构造了S[(T, Int)]的实例(在每对中,T是元素,Int是其出现次数)。 这在C ++中是可能的:

template<typename T, template<typename> S>

两个问题:

  • 如何在S上声明它必须是一个集合的约束? Multiset[T, S[_] <: Set[_]]是否有效?

  • 是否可以声明可以实例化S[(T, Int)]实例的约束?这可以使用where S: new()约束在C#中完成。

1 个答案:

答案 0 :(得分:3)

第一个问题:请改用S[X] <: Set[X]。使用下划线标记高阶类型参数是方便的,不需要用无用的名称来引起注意,但实际上你可以使用任何标识符。对于这种特殊情况,下划线不起作用。你甚至可以做S[X] <: Set[List[X]]之类的事情。

第二个问题:没有直接的等价物,但我很少在C#中使用它,因为它暗示对象只能由无参数构造函数创建,这是一个很大的限制,对所有可能的用法都有效你的代码例如,您的类的用户可能希望在初始化时设置容量,或者使用池或其他任何东西。大多数时候,我在构造函数中需要Func<S>委托,我可以添加一个静态工厂,接受new作为方便,如

class Generic<T> 
{
   public Generic(..., Func<T> factory)
}

static class Generic 
{
  public Generic<T> Create(....) where T : new {
    return new Generic(..., () => new T());
  }
}

在scala中,您需要传递该函数。可能的替代方案是使用隐式参数,例如

trait Builder[A] {
   def build(): A
}

object Builder {
  def build[A: Builder] : A = implicitly[Builder[A]].build()
}

class Generic[A: Builder](....) {....
    ...
    // instead of val a = new A()
    val a = Builder.build[A]
    ....
}

然后,您可以确保构建器在参数类型的隐式作用域中可用,并且默认情况下将使用它。当默认值不是你想要的时候,你仍然可以明确地传递另一个。