Scala 为可选参数建模的最佳方法

Scala 为可选参数建模的最佳方法,scala,design-patterns,optional,Scala,Design Patterns,Optional,正如标题所说,在Scala中建模可选参数的最佳方法是什么 对于可选参数,我指的是执行函数体不需要的值 要么因为该参数存在默认值,要么根本不需要该参数本身(例如,配置或调试标志);请注意,在Java上,我可能会将null传递给这些参数 这是Scala社区的常见问题解答,特别是由新手编写的 例如: 在这里的SO: 关于用户的话语: 在GitterScala频道中: 简单且社区接受的答案 一般而言,社区一致认为,以下列出的所有建议或备选方案都不值得权衡。 因此,建议的解决方案是只使用数据类型并手动

正如标题所说,在Scala中建模可选参数的最佳方法是什么

对于可选参数,我指的是执行函数体不需要的值


要么因为该参数存在默认值,要么根本不需要该参数本身(例如,配置或调试标志);请注意,在Java上,我可能会将
null
传递给这些参数


这是Scala社区的常见问题解答,特别是由新手编写的

例如:

  • 在这里的SO
  • 关于用户的话语
  • GitterScala频道中:
简单且社区接受的答案 一般而言,社区一致认为,以下列出的所有建议或备选方案都不值得权衡。
因此,建议的解决方案是只使用数据类型并手动/显式地将值包装在
Some

def测试(必需:Int,可选:选项[String]=None):String=
可选.map(*required).getOrElse(“”)
测试(要求=100)/“”
测试(必需=3,可选=Some(“Foo”)/“Foo”
然而,这种方法的明显缺点是在调用站点上有必要的样板文件。 但是,可以说,它使代码更易于阅读和理解,从而便于维护

尽管如此,有时您可以使用其他技术(如默认参数或重载)提供更好的API

备选方案和建议 隐式转换 由于前面解决方案的样板文件,使用隐式转换的常见替代方法被反复提及;例如:

implicit def a2opt[A](A:A):选项[A]=Some(A)
这样就可以像这样调用前面的函数:

测试(必需=3,可选=“Foo”)
这样做的缺点是,隐式转换隐藏了这样一个事实:
可选
是一个可选参数(当然,如果名称不同的话),并且这种转换可以应用于代码的许多其他(非预期)部分;这就是为什么通常不鼓励隐式转换的原因

另一种方法是使用扩展方法而不是隐式转换,例如
optional=“foo”.opt
。然而,扩展方法需要添加更多的代码,而且站点调用仍然有一些样板文件,这一事实使它看起来像一个平庸的中间点。
(免责声明,如果您使用的是cats,那么您已经在范围
中使用了这样一种扩展方法

默认参数 该语言支持为函数的参数提供默认值,如果未传递,编译器将插入默认值

有人可能认为这应该是对可选参数建模的最佳方式;然而,他们有三个问题

  • 您并不总是有默认值,有时您只想知道是否传递了该值。例如,国旗

  • 如果它在自己的参数组中,您仍然需要添加空括号,这可能看起来很难看(当然,这是一种主观意见)

  • def transact[A](config:config=config.default)(f:Transaction=>A):A
    transact()(tx=>??)
    
  • 您只能有一个带有默认参数的重载
  • 对象函数{
    def run[A](查询:查询[A],配置:config=config.default):A=???
    def run[A](查询:字符串,配置:config=config.default):A=???
    }
    
    错误:在对象函数中,方法run的多个重载替代项定义了默认参数

    超载 另一个常见的解决方法是提供该方法的重载版本;例如:

    def测试(必需:Int,可选:String):String=
    可选*必填项
    def测试(必需:Int):字符串=
    测试(必需,可选=“”)
    
    这种方法的优点是它将样板文件封装在定义站点上,而不是在调用站点上;还使代码更易于阅读,并得到工具的良好支持。
    然而,最大的缺点是,如果您有多个可选参数,那么这不能很好地扩展;例如,对于三个参数,您需要七个(
    7
    )重载

    但是,如果您有许多可选参数,那么最好只要求一个
    Config
    /
    Context
    参数并使用Builder

    构建器模式。 def foo(数据:Dar,配置:config=config.default) //最好不要使用case类来实现二进制兼容性。 //而是定义自己的私有复制方法或其他东西。 //但这超出了本问题/答案的范围。 最终案例类配置( flag1:选项[Int]=无, flag2:选项[Int]=无, flag3:选项[Int]=无 ) { def withFlag1(标志:Int):配置= 复制(flag1=Some(flag)) def withFlag2(标志:Int):配置= 这个。复制(flag2=Some(flag)) def withFlag3(标志:Int):配置= 复制(flag3=Some(flag)) } 对象配置{ def默认值:Config=new Config() }
    请求本地支持 在贡献者的论述中,有人建议为这个用例添加语言级或stdlib级支持。然而,由于上述相同的原因,所有这些都被丢弃了

    这些建议的例子如下:

    结论 一如既往,根据您的具体情况和您想要提供的API选择要使用的技术


    斯卡拉3 可能是引入了联合类型<