2012-02-14 48 views
3

我有以下情况:Scala的通用功能混乱

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

是否可以保证一个类型的水平,有些情况下类有一个无参数的构造函数?如果是这样,Any应该被替换成什么?如果不是,那么完成这个的最好方法是什么?也许我应该改变我的方法,如def f[T](lst: List[T], default: T)

任何帮助表示赞赏。

+3

注:'F'是一种方法,而不是一个函数。函数是用'=>'创建的(这基本上就是用于匿名扩展'FunctionN'特性之一和重载'apply'的语法糖),'def'定义了方法。 – 2012-02-14 16:07:17

+0

同意,谢谢澄清。 – folone 2012-02-15 08:26:50

回答

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我前段时间写过这个主题。

+0

是的,谢谢。 – folone 2012-02-14 16:11:08

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是由类型指定的类型参数(它必须是Foo的子类型)

2

简单的答案是否定的,类型系统不能告诉你,如果一个类有一个默认的构造函数。请记住,案例类通常不具有默认构造函数,因为不建议使用无参数的案例类。默认构造函数的概念对于不可变对象不是很有用。 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]) = ...,你在那里召唤evimplicitly[Default[T]]。拿你的选择。

注3:我们可以把它简单地提供一个工作:

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