【问题标题】:Scala generic function confusionScala 泛型函数混淆
【发布时间】:2012-03-05 23:40:22
【问题描述】:

我遇到以下情况:

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

是否可以在类型级别上保证某些案例类具有无参数构造函数?如果是这样,应该用什么替换Any?如果不是,那么实现这一目标的最佳方法是什么?也许我应该把我的方法改成def f[T](lst: List[T], default: T)之类的?

任何帮助表示赞赏。

【问题讨论】:

  • 注意:f 是一个方法,而不是一个函数。函数是用=> 创建的(基本上只是匿名扩展FunctionN 特征之一并覆盖apply 的语法糖),def 定义方法。
  • 同意,感谢您的澄清。

标签: scala reflection types wildcard


【解决方案1】:

你在寻找这样的东西吗?

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

【讨论】:

    【解决方案2】:

    我不确定您要做什么(也许您可以包含更多详细信息),但我可以立即给出的一些建议是,如果您有一堆相关的案例类,它们都应该扩展密封性状。这不仅可以让您获得更好的类型安全性(不再有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 参数指定的类型(必须是Foo 的子类型)

    【讨论】:

      【解决方案3】:

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

      但是,您可以使用类型类模式强制默认值在范围内。这在概念上与 OP 中建议的添加额外的默认争论非常相似,但使用隐式来隐藏它们。它在收藏库中大量使用。使用scalaz.Zero 的missingfaktor 的答案是一个特例,但在普通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)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-07-11
        • 2020-02-01
        • 2016-09-28
        相关资源
        最近更新 更多