当前位置: 首页 > 知识库问答 >
问题:

函数可以返回不共享共同祖先的多种类型之一吗?

燕鸿文
2023-03-14

当然,我意识到所有类型都有一个共同的祖先,但我的意思是:

在动态类型语言中,通常使用“混合”返回类型。一种常见情况是函数尝试从数据库中检索html" target="_blank">数据,然后返回一个对象(用找到的数据初始化)或FALSE(如果没有找到数据)。

一个小伪代码来演示这样的反模式:

function getObjectFromDatabase(object_id) {
  if(result = db_fetch_object("SELECT * FROM objects WHERE id = %d", object_id) {
    return result
  } else {
    return FALSE
  }
}

如果为我的对象id找到了数据,我会将DB记录作为对象返回。如果不是,我得到一个布尔值。然后,当然,由我,客户来处理多种可能的返回类型。

在 Scala 中实现此目的的唯一方法是为所有可能的返回类型找到一个共同的祖先,并在签名中将其声明为返回类型?

// Like so:
def getObjectFromDatabase(objectId: Int): Any = {
   val result = dbFetchObject("SELECT * FROM objects WHERE id = %d", object_id) 
   if(result) {
     return result
   } else {
     return false
  }
}

或者可以注释多个可能的返回类型吗?

(请注意,我不希望这样做,因为我更希望函数返回类型尽可能明确。了解到该语言禁止含糊不清的返回类型,我会松一口气,这也是我提出这个问题的原因。)

共有3个答案

微生雨泽
2023-03-14

如果您想在运行时知道在每个调用中查询什么类型,您的签名可以是:

def getObjectFromDatabase[T](object_id: Int): T = {

或者,为了模拟您的if/example逻辑,我建议在此处使用Option:

def getObjectFromDatabase[T](object_id: Int): Option[T] = {
  ...
  if(result) Some(result)
  else None
}

用法示例:

val result = getObjectFromDatabase[String](123123).getOrElse(whatever_you_need)
百里默
2023-03-14

您正在寻找的称为标记并集、变量、变量记录、区分并集、分离并集或和类型。

与产品类型相结合,它们成为代数数据类型。

Scala不直接支持代数数据类型,但它不需要,因为它们可以通过继承轻松建模。(Scala确实有密封修饰符来支持封闭ADT。)

注意:从Scala 3开始,Scala以< code>enum的形式直接支持代数和类型。

在您的示例中,如果您知道返回类型是SomeTypesomethertype,则可以这样建模:

sealed trait ReturnType

final case class SomeType extends ReturnType
final case class SomeOtherType extends ReturnType

def meth: ReturnType

如果您不知道返回类型是什么,只知道其中有两个,那么您可以对其进行类似的建模:

sealed trait ReturnType[A, B]

final case class Type1[A, B](a: A) extends ReturnType[A, B]
final case class Type2[A, B](b: B) extends ReturnType[A, B]

def meth: ReturnType[A, B]

这实际上是一种众所周知的数据类型,称为<code>或<code>(因为它持有<code>a<code>或者<code>B<code>),并且在Scala的标准库中以<code>Scala.util.or<code>的形式存在。

但是在您的特定情况下,有一种更具体的类型,称为也许Option,它封装了一个值可能存在或可能不存在的想法。它看起来像这样:

sealed trait Maybe[T]

case object None extends Maybe[Nothing]
final case class Just[T](value: T) extends Maybe[T]

def meth: Maybe[T]

同样,Scala已经将其作为scala提供。选项

与< code >选项相比,< code >任一的优势在于,它还允许您在失败的情况下返回信息,而不仅仅是指示没有值,您还可以说明为什么没有值。(按照惯例,< code >要么的左边是错误,右边是“有用”的值。)

Option的优点是它是一个monad。(注意:您可以通过将其偏向左或向右来制作monad。

于恺
2023-03-14

是的,使用<code>或<code>:

def getObjectFromDatabase(objectId: Int): Either[Boolean, DbResult] = {
   val result = dbFetchObject("SELECT * FROM objects WHERE id = %d", object_id) 
   if (result) Right(result) else Left(false)

}

getObjectFromDatabase(id) match {
  case Right(result) => // do something with result
  case Left(bool) => // do something with bool
}

或者,如果没有结果的情况不需要特定的值,请使用< code>Option:

def getObjectFromDatabase(objectId: Int): Option[DbResult] = {
   val result = dbFetchObject("SELECT * FROM objects WHERE id = %d", object_id) 
   if (result) Some(result) else None
}

getObjectFromDatabase(id) match {
  case Some(result) => // do something with result
  case None => // do something about no results
}

查看Tony Morris的选项备忘单,了解您可以调用<code>选项</code>的最常用方法列表,以及它们如何转化为模式匹配。

另外两个选择是scalaz的< code>Validation和Scala 2.10中新增的< code>Try。

对于<code>验证</code>有一些关于StackOverflow的非常好的答案,例如:Scala中的方法参数验证,以及用于理解和单子的验证。

有关试用,请参阅此博客文章:Scala 新手指南第 6 部分:尝试错误处理。同一位作者在OptionAys上有很好的帖子。

 类似资料:
  • 我的问题是树中有大量的节点和许多查询。是否有一种算法,它进行预处理,使查询能够在恒定的时间内得到答复。 我研究了使用RMQ的LCA,但我不能使用该技术,因为我不能对树中的这么多节点使用数组。 如果知道它是满二叉树,节点之间的关系如上所示,那么有人能给我一个高效的实现来快速回答许多查询。 但是当有很多查询时,这种算法非常耗时,因为在最坏的情况下,我可能必须遍历30的高度(树的最大高度)才能到达根(最

  • 问题内容: 这是一个受欢迎的面试问题,我唯一可以找到的有关该主题的文章是TopCoder的文章。对我来说不幸的是,从访谈答案的角度来看,它看起来过于复杂。 除了绘制到两个节点的路径并推导祖先之外,没有其他更简单的方法了吗?(这是一个很流行的答案,但是面试题有一个变体,要求一个固定的空格答案)。 问题答案: 恒定空间答案:(尽管不一定有效)。 有一个函数findItemInPath(int inde

  • Topics 共同的返回值 一些模块返回’facts’(例如 setup), 这些是通过一个’ansible_facts’作为key和内部一些自动收集的值直接作为当前主机的变量并且他们不需要注册这些数据 Status 每一个模块都必须返回一个status, 来表示这个模块是成功的,是否有任何改变或没有. 当因为用户的条件(when: )或在检查模式下运行时发现该模块不支持, Ansible自己将会

  • 如何在有向无环图中找到多个节点的最小共同祖先? 我已经找到了很多关于这个主题的论文,但它们似乎都在DAG中找到了两个节点的LCAs。

  • 问题内容: 在Java中的各个类之间共享数据的最佳方法是什么?我有一堆变量,它们由不同的类以不同的方式在不同的文件中使用。让我尝试说明问题的简化版本: 这是我之前的代码: 现在看起来像这样: 所以无论如何,我应该每次都传递x和y(其中x,y是存储在辅助类func中的变量)吗? 我的想法是要有一个特殊的容器类,其中存放x和y。顶级类将具有容器类的实例,并使用set方法更改x,y。 我的帮助程序类还将