scala - 选项和命名默认参数就像 Scala API 中的油和水吗?

标签 scala optional-parameters named-parameters

我正在开发一个 Scala API(顺便说一下,适用于 Twilio),其中的操作具有相当大量的参数,并且其中许多参数都有合理的默认值。为了减少输入并提高可用性,我决定使用带有命名参数和默认参数的案例类。例如,对于 TwiML Gather 动词:

case class Gather(finishOnKey: Char = '#', 
                  numDigits: Int = Integer.MAX_VALUE, // Infinite
                  callbackUrl: Option[String] = None, 
                  timeout: Int = 5
                  ) extends Verb

这里感兴趣的参数是callbackUrl。它是唯一真正可选的参数,因为如果没有提供任何值,则不会应用任何值(这是完全合法的)。

我已将其声明为一个选项,以便在 API 的实现端使用它执行单子(monad)映射例程,但这给 API 用户带来了一些额外的负担:

Gather(numDigits = 4, callbackUrl = Some("http://xxx"))
// Should have been
Gather(numDigits = 4, callbackUrl = "http://xxx")

// Without the optional url, both cases are similar
Gather(numDigits = 4)

据我所知,有两个选项(没有双关语)可以解决这个问题。要么让 API 客户端将隐式转换导入作用域:

implicit def string2Option(s: String) : Option[String] = Some(s)

或者我可以使用 null 默认值重新声明案例类,并将其转换为实现端的选项:

case class Gather(finishOnKey: Char = '#', 
                  numDigits: Int = Integer.MAX_VALUE, 
                  callbackUrl: String = null, 
                  timeout: Int = 5
                  ) extends Verb

我的问题如下:

  1. 有没有更优雅的方法来解决我的特殊情况?
  2. 更一般地说:命名参数是一项新的语言功能 (2.8)。难道选项和命名默认参数就像油和水一样吗? :)
  3. 在这种情况下,使用 null 默认值可能是最佳选择吗?

最佳答案

这是另一个解决方案,部分灵感来自 Chris' answer 。它还涉及到一个包装器,但包装器是透明的,您只需定义一次,并且 API 的用户不需要导入任何转换:

class Opt[T] private (val option: Option[T])
object Opt {
   implicit def any2opt[T](t: T): Opt[T] = new Opt(Option(t)) // NOT Some(t)
   implicit def option2opt[T](o: Option[T]): Opt[T] = new Opt(o)
   implicit def opt2option[T](o: Opt[T]): Option[T] = o.option
}

case class Gather(finishOnKey: Char = '#', 
                  numDigits: Opt[Int] = None, // Infinite
                  callbackUrl: Opt[String] = None, 
                  timeout: Int = 5
                 ) extends Verb

// this works with no import
Gather(numDigits = 4, callbackUrl = "http://xxx")
// this works too
Gather(numDigits = 4, callbackUrl = Some("http://xxx"))
// you can even safely pass the return value of an unsafe Java method
Gather(callbackUrl = maybeNullString())
<小时/>

为了解决更大的设计问题,我认为选项和命名默认参数之间的交互并不像乍一看那么复杂。可选字段和具有默认值的字段之间有明确的区别。可选字段(即 Option[T] 类型之一)可能永远没有值。另一方面,具有默认值的字段不需要将其值作为参数提供给构造函数。因此,这两个概念是正交的,并且字段可能是可选的并且具有默认值也就不足为奇了。

也就是说,我认为对于此类字段使用 Opt 而不是 Option 是合理的,而不仅仅是节省客户端的一些输入。这样做可以使 API 更加灵活,因为您可以将 T 参数替换为 Opt[T] 参数(反之亦然),而不会破坏该函数的调用者构造函数[1]。

至于对公共(public)字段使用 null 默认值,我认为这是一个坏主意。 “您”可能知道您期望一个 null,但访问该字段的客户端可能不知道。即使该字段是私有(private)的,当其他开发人员必须维护您的代码时,使用 null 也会带来麻烦。所有关于 null 值的常见争论都在这里发挥作用 - 我不认为这个用例有任何特殊的异常(exception)。

[1] 前提是您删除了 option2opt 转换,以便每当需要 Opt[T] 时调用者都必须传递 T

关于scala - 选项和命名默认参数就像 Scala API 中的油和水吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4199393/

相关文章:

scala - 案例类构造函数参数类型取决于前一个参数值

c# - 可以在 WCF 服务方法中使用可选参数吗?

c# - C# 2.0-3.0 是否支持方法的命名参数?

flutter - Dart 中命名参数和位置参数有什么区别?

java - 运行同时包含 Scala 代码的 Java 程序的最佳方式是什么?

javascript - 将数据从 html 引导到 webpack 捆绑的 javascript

Scala:访问Either的公共(public)子类型

ios - 方法不能标记为@objc,因为它的结果类型不能在 Objective-C 中表示

c# - 如何通过反射执行带有可选参数的私有(private)静态方法?