【发布时间】:2019-02-06 13:52:03
【问题描述】:
假设我有几个自动生成的类,比如MyEnum1、MyEnum2、...(它们不一定是 Scala 枚举类型,只是一些自动生成的类)。尽管MyEnum1 的类型与MyEnum2 的类型不同(它们不共享除Any 之外的自动生成的父类型),但我可以保证所有这些自动生成的类型都具有完全相同的公共、静态可用的方法,特别是 findById 和 findByName,它们允许根据索引或字符串名称查找枚举值。
我正在尝试创建一个函数,该函数将利用 findById 和 findByName 的特定类型版本,但通用接受 MyEnum1、MyEnum2、... 作为函数参数中的任何一个。
请注意,用典型的sealed trait + case class 模式从不同的枚举中创建一个 sum 类型在这里没有帮助,因为我说的是基于类型参数调度不同的静态方法,而且从来没有完全涉及实际值参数。
例如,假设MyEnum1 编码男性/女性性别。这样MyEnum1.findById(0) 返回MyEnum1.Female,其类型为MyEnum1。假设MyEnum2 编码眼睛颜色,所以MyEnum2.findById(0) 返回MyEnum2.Green,其类型为MyEnum2。
给我一个Map,其中key是type,value是要查找的索引,比如
val typeMap = Map(
MyEnum1 -> 0,
MyEnum2 -> 0
)
我想一般地这样做:
for ( (elemType, idx) <- typeMap ) yield elemType.findById(v)
|---------------|
the goal is to
avoid boilerplate
of defining this
with different
pattern matching
for every enum.
并取回一些看起来像
的序列类型(可以有元素类型Any)
MyEnum1.Female, MyEnum2.Green, ...
我一直在为sealed trait + case class 样板而苦苦挣扎,从概念上讲,它似乎不是正确的方法。无论我是否将MyEnum1 或MyEnum2 的values 包装到FromMyEnum1(e: MyEnum1) 之类的case 类值构造函数中,并尝试定义对那个value 进行操作的隐式,它都不会当我想做elemType.findById(...) 时,在上面的代码示例中没有帮助,因为编译器仍然说Any 类型(它为我的Map 中的键类型解析的内容)没有方法findById。
我强烈不希望将类型本身包装在案例类模式中作为键,但我可以这样做——除了我看不出如何将类型本身视为第一类值一个案例类的构造函数,天真的像
case class FromMyEnum1(e: MyEnum1.getClass) extends EnumTrait
(因此Map 键可以具有EnumTrait 类型,并且可能有一些隐式将每个案例类构造函数与findById 或findByName 的正确实现相匹配。
任何帮助理解 Scala 如何将类型本身用作案例类值构造函数中的值将不胜感激!
【问题讨论】:
-
“任何有助于理解 Scala 如何使用类型本身作为案例类值构造函数中的值的帮助将不胜感激!” – 不幸的是,这在 Scala 中根本不可能(事实上几乎所有静态类型的编程语言,除了极少数高度晦涩的研究语言)。在 Scala 中,像大多数静态类型的编程语言一样,types 和 values 的世界是严格分开的。
-
您可以在 Scala 中轻松看到这一点,您可以同时拥有同名的类型和值(例如
Seq)而不会遇到任何冲突。 -
@JörgWMittag 我认为您的评论不正确。例如,在 Haskell 中,因为您可以通过仅提供它们的实现来将现有类型变成类型类的实例,所以您可以轻松地对类型进行调度,而无需添加样板值构造函数来包装所有内容。 Haskell 和 Idris 也提供了完全依赖类型的能力。至少,Haskell 是相当主流的,这些类型的东西通常用于非研究目的。
-
我这样说并不是为了支持 Haskell(我碰巧非常喜欢 Scala)。只有 Scala 使用新的调度行为扩展现有类型的能力需要一定程度的样板文件,这对于从其他地方交给你的大量类型是不可行的(比如在我目前的情况下,使用来自遗留系统,其中类型生成过程无法更改,但它创建的类型必须在以后都进行相同的扩展)。在这种情况下,结构类型的匹配可以解决它。
标签: scala pattern-matching implicit case-class