Scala泛型函数混淆

时间:2012-02-14 15:37:21

标签: scala reflection types wildcard

我有以下情况:

得到一个方法def f(lst: List[Any]),它对列表进行一些转换并返回该转换的结果(所有Any都是case类)。我需要完成的是当输入列表为空时,生成一个列表,其中包含一个正确类型的元素并使用它进行转换。

是否可以在类型级别上保证某些case类具有no-arg构造函数?如果是这样,应该用Any替换什么?如果不是,最好的方法是什么?也许我应该将我的方法改为def f[T](lst: List[T], default: T)

任何帮助表示感谢。

3 个答案:

答案 0 :(得分:5)

你在找这样的东西吗?

import scalaz._
import Scalaz._

scala> def f[A : Zero](lst: List[A]) = {
     |   val xs = if(lst.isEmpty) List(mzero[A]) else lst
     |   xs ++ xs // some transformation
     | }
f: [A](lst: List[A])(implicit evidence$1: scalaz.Zero[A])List[A]

scala> f(List.empty[Int])
res1: List[Int] = List(0, 0)

scala> f(List("hello", "world"))
res2: List[java.lang.String] = List(hello, world, hello, world)

如果是,你可以参考我前段时间写过的this post

答案 1 :(得分:2)

简单的答案是否定的,类型系统无法告诉您类是否具有默认构造函数。请记住,case类通常没有默认构造函数,因为不推荐使用no-arg case类。对于不可变对象,默认构造函数的概念并不那么有用。 AFAIK没有理由不应该原则上(Scala支持结构类型,其中类型必须具有特定名称的方法),但它需要语言更改。您可以在运行时使用反射检查,但这不是您想要的。

但是,您可以使用类型类模式强制将默认值放在范围内。这在概念上非常类似于在OP中添加额外的默认争论,但使用隐含来隐藏它们。它在集合库中大量使用。 missingfaktor使用scalaz.Zero的答案是一个特例,但在vanilla Scala和一些不一定是零的任意默认值下都很容易做到。

case class Default[T](default: T)

case class Foo(value: String)
case class Bar(value: Int)

implicit val fooDefault = Default(Foo("I'm a default Foo"))  // note 1

现在让我们看一个示例用法:

def firstItem[T](lst: List[T]) (implicit ev: Default[T]) =   // note 2
  if (lst.isEmpty) ev.default else lst.head

val fooList      = List(Foo("cogito"), Foo("ergo"), Foo("sum"))
val emptyFooList = List[Foo]()
val barList      = List(Bar(101), Bar(102))
val emptyBarList = List[Bar]()

firstItem(fooList)                        // Foo("cogito")
firstItem(emptyFooList)                   // Foo("I'm a default Foo")
firstItem(barList)                        // ** error: missing implicit **

因此,我们发现这会使用List[Foo]进行编译,但不接受List[Bar],因为没有隐式Default[Bar](注3)。


注1:这个隐式可以在object Foo上定义 - 如果你将类导入其他地方,这将确保它在范围内。但它不一定是:你也可以为任意类定义类似的含义,IntString,无论如何(尝试它)。

注意2:这等于加糖版本def firstItem[T: Default](lst: List[T]) = ...,您将ev召唤到implicitly[Default[T]]。随便挑选。

注意3:我们可以通过简单地提供一个来实现它:

firstItem(barList)(Default(Bar(42)))      // Bar(101)
firstItem(emptyBarList)(Default(Bar(42))) // Bar(42)

答案 2 :(得分:1)

我不确定你要做什么(也许你可以包含更多细节),但我可以立即给出的一些建议是,如果你有一堆相关的案例类,它们都应该扩展一个密封特性。这不仅可以让您拥有更好的类型安全性(不再是Any),而且编译器将能够检查详尽的模式匹配。例如:

sealed trait Foo
case class Bar(x: Int) extends Foo
case class Baz(y: String) extends Foo

然后你可以像这样定义你的功能

def f[T <: Foo](lst: List[Foo], default: T)//...

这将允许list包含任何案例类的项目,但要求default是type参数指定的类型(必须是{{1}的子类型})