Scala 泛型函数混淆
我遇到以下情况:
有一个方法 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您在寻找这样的东西吗?
如果是,你可以参考这篇文章< /a> 我不久前写过这个主题。
Are you looking for something like this?
If yes, you can refer to this post I wrote on the subject a while ago.
简单的答案是否定的,类型系统无法告诉您类是否有默认构造函数。请记住,案例类通常没有默认构造函数,因为不推荐使用无参数案例类。默认构造函数的概念对于不可变对象不太有用。 AFAIK 原则上没有理由不应该这样做(Scala 确实支持结构类型,其中类型必须具有特定名称的方法),但它需要更改语言。您可以在运行时使用反射进行检查,但这不是您想要的。
但是,您可以使用类型类模式强制默认值位于范围内。这在概念上与 OP 中建议的添加额外的默认参数非常相似,但使用隐式来隐藏它们。它在馆藏库中被大量使用。 Missingfaktor 使用 scalaz.Zero 的答案是这种情况的一个特例,但在普通的 Scala 中很容易做到,并且对于某些不一定是某种零的任意默认值。
现在让我们看一个示例用法:
我们看到它使用
List[Foo]
进行编译,但List[Bar]
不被接受,因为没有隐式 <代码>默认[Bar](注3)。注意 1:这个隐式可以在
object Foo
上定义 - 如果您在其他地方导入该类,这将确保它在范围内。但它不一定是这样:您还可以为任意类定义类似的隐式,Int
、String
等等(尝试一下)。注意 2:这等于糖化版本
def firstItem[T: Default](lst: List[T]) =
...,您可以使用 < 召唤ev
代码>隐式[默认[T]]。随你挑选。注 3:我们只需提供一个即可使其工作:
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.Now let's look at an example usage:
So we see that this compiles with a
List[Foo]
, but aList[Bar]
is not accepted because there is no implicitDefault[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 summonev
withimplicitly[Default[T]]
. Take your pick.note 3: We can make it work by simply supplying one:
我不太确定你想要做什么(也许你可以包含更多细节),但我可以立即给出的一些建议是,如果你有一堆相关的案例类,它们都应该扩展一个密封的特征。这不仅可以让您获得更好的类型安全性(不再有
Any
),而且编译器将能够检查详尽的模式匹配。例如:然后您可以像这样定义您的函数
这将允许
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:Then you can define your function like so
This will allow
list
to contain items of any of the case classes, but require thatdefault
is the type specified by the type parameter (which is required to be a subtype ofFoo
)