首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何定义在运行时工作的联合类型?

如何定义在运行时工作的联合类型?
EN

Stack Overflow用户
提问于 2016-07-11 04:02:50
回答 2查看 1.5K关注 0票数 3

下面是关于如何定义union types in Scala的一组出色的答案。我一直在使用Miles Sabin对Union类型的定义,但有一个问题仍然存在。

如果直到运行时才知道类型,那么如何处理这些类型呢?例如:

代码语言:javascript
复制
trait inv[-A] {}
type Or[A,B] = {
  type check[X] = (inv[A] with inv[B]) <:< inv[X]
}

case class Foo[A : (Int Or String)#check](a: A)

Foo(1)    // Foo[Int] = Foo(1)
Foo("hi") // Foo[String] = Foo(hi)
Foo(2.0)  // Error!

这个例子之所以有效,是因为参数A在编译时是已知的,调用Foo(1)实际上就是在调用Foo[Int](1)。但是,如果参数A直到运行时才知道,该怎么办?也许你正在解析一个包含Foo数据的文件,在这种情况下,Foo的类型参数直到你读取数据后才知道,在这种情况下没有简单的方法来设置参数A

我能想到的最好的解决方案是:

  • 模式与您读取的数据进行匹配,然后基于该类型创建不同的Foo。在我的例子中,这是不可行的,因为我的case类实际上包含几十个联合类型,因此将有数百种类型组合进行模式匹配。
  • 将您刚才读取的类型转换为(String or Int),因此您只需传递一个类型,当您使用它创建Foo时,该类型将传递type类约束。然后返回Foo[_]。这让Foo用户重新承担了确定每个字段的类型的责任(因为它们看起来是类型Any),但至少它推迟了知道类型的时间,直到字段被实际使用,在这种情况下,模式匹配似乎更容易处理。

第二个解决方案如下所示:

代码语言:javascript
复制
def parseLine: Any // Parses data point, but can be either a String or 
                   // Int, so returns Any.

def mkFoo: Foo[_] = {
  val a = parseLine.asInstanceOf[Int with String]
  Foo(a) // Passes type constraint now
}

在实践中,我最终使用了第二种解决方案,但我想知道是否有更好的方法可以做?

陈述问题的另一种方法是:返回Union类型意味着什么?函数只能返回单个类型,我们对Miles Sabin联合类型使用的技巧只适用于您传入的类型,而不适用于您返回的类型。

PS。对于上下文,为什么这在我的案例中是一个问题,是因为我正在从一个Json模式文件生成一组case-classes。Json自然支持联合类型,所以我想让我的case类也反映这一点。这在一个方向上效果很好:用户创建要序列化到Json的case-case。但在另一个方向上变得棘手:用户解析Json文件,以将一组填充的case类返回给它们。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-07-11 04:22:53

Scala对这个问题的“标准”解决方案是使用一个普通的联合判别类型(即完全放弃真正的联合类型):

代码语言:javascript
复制
sealed trait Foo
case class IntFoo(x: Int) extends Foo
case class StringFoo(x: String) extends Foo

正如您所观察到的,这反映了这样一个事实:成员的特定类型是一个运行时值;Foo实例的JVM type-tag提供了这个运行时值。

Miles Sabin的联合类型的实现非常聪明,但是我不确定它是否提供了任何实际的好处,因为它只限制了可以进入Foo的东西的类型,但是为Foo的用户提供了这种限制的可计算版本,就像match为您提供了sealed特征的可计算版本一样。一般来说,要使限制有用,它需要两个方面:检查只放入正确的内容,以及允许相同的正确内容从另一端出来的提取器(也称为消除器)。

也许,如果你给出一些解释,为什么你要寻找一个更纯粹的联盟类型,它将阐明常规的歧视联盟是否足够,或者如果你真的需要更多的东西。

票数 8
EN

Stack Overflow用户

发布于 2016-07-11 05:09:04

每个用于Scala的JSON解析器都需要定义良好的类型,以便将JSON转换为这些类型,即使某些字段必须删除,这是有原因的:您不能处理不知道其类型的内容。

举个例子,假设你有a,也许a是一个String,也许它是一个Int,但是你不知道它是什么。为什么你可以用a进行计算,而不知道它的类型?例如,如果你事先不知道它是一个数字,为什么你的代码要计算所有a的和呢?

通常,答案是在运行时对具有未知特征的数据执行用户提供的数据操作,因为用户自己看到它是一个数字,并决定他们想知道该字段的总和是多少。好吧,但如果是这样的话,你就错了。

在Scala中有一种定义良好的方式来表示JSON数据(就此而言,还有任何与JSON具有相同特征的数据。它使用类的层次结构。json值可以是json对象、数组或多个原语中的一个。json对象包含一系列键/值对,这些键/值对的键是json字符串,值是json值。诸若此类。这很容易表示,而且已经有很多库在这样做了。事实上,有很多这样的库,以至于有一个名为Json4s的项目,它提供了一个统一的应用程序接口,可以由前面提到的许多库使用和实现。

类似Miles Sabin的Shapeless库提供的记录,是为了在输入没有良好定义的模式时使用,但是程序知道它需要从该输入中得到什么。是的,如果aIntString,程序可能知道如何处理它,但不是所有可能的值。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38295995

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档