Scala 泛型函数混淆

发布于 2025-01-05 09:46:55 字数 347 浏览 1 评论 0原文

我遇到以下情况:

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

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

任何帮助表示赞赏。

I've got a following situation:

Got a method def f(lst: List[Any]), which does some transformation of the list and returns result of that transformation (all those Any's are case classes). What I need to accomplish is when the input list is empty, generate a list, containing one element of the correct type and do that transformation with it.

Is it possible to guarantee on a type level, that some case class has a no-arg constructor? If so, what should Any be replaced with? If no, what is the best way to accomplish this? Maybe I should just change my method to something like def f[T](lst: List[T], default: T)?

Any help appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

凉城 2025-01-12 09:46:55

您在寻找这样的东西吗?

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)

如果是,你可以参考这篇文章< /a> 我不久前写过这个主题。

Are you looking for something like this?

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)

If yes, you can refer to this post I wrote on the subject a while ago.

残疾 2025-01-12 09:46:55

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

但是,您可以使用类型类模式强制默认值位于范围内。这在概念上与 OP 中建议的添加额外的默认参数非常相似,但使用隐式来隐藏它们。它在馆藏库中被大量使用。 Missingfaktor 使用 scalaz.Zero 的答案是这种情况的一个特例,但在普通的 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] 不被接受,因为没有隐式 <代码>默认[Bar](注3)。


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

注意 2:这等于糖化版本 def firstItem[T: Default](lst: List[T]) =...,您可以使用 < 召唤 ev代码>隐式[默认[T]]。随你挑选。

注 3:我们只需提供一个即可使其工作:

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

The simple answer is no, the type system can't tell you if a class has a default constructor. Remember that case classes don't normally have a default constructor, since no-arg case classes are deprecated. The concept of default constructors isn't so useful with immutable objects. AFAIK there's no reason why there shouldn't be in principle (Scala does support structural types where a type must have a method of a particular name), but it would require a language change. You could check at runtime with reflection, but that's not what you want.

You can however use the type class pattern to force a default value to be in scope. This is conceptually very similar to adding an extra default arguement as suggested in the OP, but using implicits to hide them. It's used heavily in the collections library. missingfaktor's answer using scalaz.Zero is a special case of this, but it's dead easy to do in vanilla Scala and for some arbitrary default that isn't necessarily some sort of zero.

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

Now let's look at an example usage:

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 **

So we see that this compiles with a List[Foo], but a List[Bar] is not accepted because there is no implicit Default[Bar] (note 3).


note 1: This implicit could be defined on object Foo - which would make sure it's in scope if you import the class elsewhere. But it doesn't have to be: you can also define similar implicits for an arbitrary class, Int, String, whatever (try it).

note 2: This is equal to the sugared version def firstItem[T: Default](lst: List[T]) =..., where you summon ev with implicitly[Default[T]]. Take your pick.

note 3: We can make it work by simply supplying one:

firstItem(barList)(Default(Bar(42)))      // Bar(101)
firstItem(emptyBarList)(Default(Bar(42))) // Bar(42)
岁月如刀 2025-01-12 09:46:55

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

I'm not exactly sure what you're trying to do (maybe you can include a few more details), but some advice I can give immediately is if you have a bunch of related cases classes, they should all extend a sealed trait. Not only will this allow you to have better type safety (no more Any), but the compiler will be able to check for in-exhaustive pattern matching. For example:

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

Then you can define your function like so

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

This will allow list to contain items of any of the case classes, but require that default is the type specified by the type parameter (which is required to be a subtype of Foo)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文