将额外的参数传递给多态函数?

时间:2016-09-21 23:36:35

标签: scala shapeless

我有一个多态函数,可以将列表转换为集合:

import shapeless.PolyDefns.~>
import shapeless._

val lists = List(1,2) :: List("A", "B") :: List(1.1, 2.2) :: HNil

object sss extends (List ~> Set) {
  def apply[T](l:List[T]):Set[T] = {
    l.toSet
  }
}

lists.map(sss) // I want: Set(1,2) :: Set("A", "B") :: Set(1.1, 2.2) :: HNil

但是如果我想改变这个函数的行为怎么办 - 我现在想要添加一个额外的参数,它将指定输入列表中的哪个项应该放入集合中。这里的语法不正确 - 你能告诉我正确的方法吗?

object sss extends (List ~> Set) { // Compiler says no!
  def apply[T](i:Int)(l:List[T]):Set[T] = {
    l.slice(i,i+1).toSet
  }
}

我认为这是失败的,因为附加的参数使它不再适合List~>的签名。设置,那我怎么能克服这个?

2 个答案:

答案 0 :(得分:3)

有一些参数化Poly的解决方法,其中一个在另一个答案中提到,尽管那里的确切实现是行不通的。相反,你需要这样做:

import shapeless._, shapeless.poly.~>

val lists = List(1, 2) :: List("A", "B") :: List(1.1, 2.2) :: HNil

class sss(i: Int) extends (List ~> Set) {
  def apply[T](l: List[T]): Set[T] = l.slice(i, i+1).toSet
}

object sss1 extends sss(1)

lists.map(sss1)

...其中sss1被定义为对象(不是val)的事实对于最后一行编译是必要的。

这种方法可以编译,但不可能在很多情况下使用它 - 例如。你不能在hlist类型是通用的方法中定义你的sss1(或其他)对象。

这是我以前用过的稍微麻烦但更灵活的解决方法:

import shapeless._

val lists = List(1, 2) :: List("A", "B") :: List(1.1, 2.2) :: HNil

object sss extends Poly2 {
  implicit def withI[T]: Case.Aux[List[T], Int, Set[T]] =
    at((l, i) => l.slice(i, i + 1).toSet)
}

lists.zipWith(lists.mapConst(1))(sss)
// Set(2) :: Set(B) :: Set(2.2) :: HNil

现在你实际上可以编写一个采用泛型L <: HList和切片参数i的方法 - 你只需要几个隐式参数来支持mapConst和{{1}应用程序。

但这两种方法都不是很优雅,而且我个人倾向于在大多数情况下避免zipWith - 定义一个自定义类型几乎会变得更干净,并且在很多情况下都需要。

答案 1 :(得分:1)

正如您已经指出的那样,您无法更改多态函数的签名。但是可以动态创建你的函数:

class sss(i: Int) extends (List ~> Set) {
  def apply[T](l:List[T]): Set[T] = {
    l.slice(i, i+1).toSet
  }
}

val sss1 = new sss(1)
lists.map(sss1) // Set(2) :: Set(B) :: Set(2.2) :: HNil