Mal*_*off 9 dsl scala object partial-application case-class
考虑定义两个属性的抽象类
abstract class A {
def a: Int
def b: Int
// real A has additional members
}
Run Code Online (Sandbox Code Playgroud)
这是各种案例类的基类,如
case class Foo(a: Int, b: Int) extends A
case class Bar(a: Int, b: Int) extends A
// and many more
Run Code Online (Sandbox Code Playgroud)
目标:我最终希望能够以两种方式创建上述案例类的实例,即
val b1 = Bar(1, 2)
val b2 = Bar(1) has 2
assert(b1 == b2) // must hold
Run Code Online (Sandbox Code Playgroud)
方法:因此,定义一个帮助类来定义has并允许我部分构造As 似乎是合理的
case class PartialA(f: Int => A) {
def has(b: Int) = f(b)
}
Run Code Online (Sandbox Code Playgroud)
问题:当前机制不允许调用,Bar(1)因为这实际上Bar.apply(1)是apply对编译器生成的对象定义的方法的调用Bar.
如果我可以强制编译器生成Bar对象as object Bar extends PartialAConstructor,where 会很棒
abstract class PartialAConstructor{
def apply(a: Int, b: Int): A // abstract, created when the compiler creates
// object Bar
def apply(a: Int) = PartialA((b: Int) => apply(a, b))
}
Run Code Online (Sandbox Code Playgroud)
但是,似乎不可能影响案例类的伴随对象的生成.
期望的属性:
案例类:Foo,Bar等应保持case类,因为我想用编译器生成的东西,如结构平等,copy并自动生成提取.
"完全"结构相等:将案例类定义为
case class Bar(a: Int)(val b: Int)
Run Code Online (Sandbox Code Playgroud)
不是一个选项,因为编译器生成的equals方法只考虑第一个参数列表,因此以下将错误地保存:
assert(Foo(1)(0) == Foo(1)(10))
Run Code Online (Sandbox Code Playgroud)尽可能少的代码重复:例如,当然可以定义一个
def Bar(a: Int) = PartialA((b: Int) => Bar(a, b))
Run Code Online (Sandbox Code Playgroud)
但必须要做延长每一个案例类A,是,是Foo,Bar等
您可以严重依赖柯里化(以及事实上Foo.apply,与任何方法一样,将自动提升为函数)和一个小帮助器来增强语法:
object partially {
def apply[A1,A2,R]( f: (A1, A2) => R ) = f.curried
def apply[A1,A2,R]( f: (A1, A2) => R, a1: A1 ) = f.curried( a1 )
def apply[A1,A2,A3,R]( f: (A1, A2, A3) => R ) = f.curried
def apply[A1,A2,A3,R]( f: (A1, A2, A3) => R, a1: A1 ) = f.curried( a1 )
def apply[A1,A2,A3,R]( f: (A1, A2, A3) => R, a1: A1, a2: A2 ) = f.curried( a1 )( a2 )
def apply[A1,A2,A3,A4,R]( f: (A1, A2, A3, A4) => R ) = f.curried
def apply[A1,A2,A3,A4,R]( f: (A1, A2, A3, A4) => R, a1: A1 ) = f.curried( a1 )
def apply[A1,A2,A3,A4,R]( f: (A1, A2, A3, A4) => R, a1: A1, a2: A2 ) = f.curried( a1 )( a2 )
def apply[A1,A2,A3,A4,R]( f: (A1, A2, A3, A4) => R, a1: A1, a2: A2, a3: A3 ) = f.curried( a1 )( a2 )( a3 )
// ... and so on, potentially up to 22 args
}
Run Code Online (Sandbox Code Playgroud)
然后你可以这样做:
scala> val x = partially(Foo)(1)
x: Int => Foo = <function1>
scala> x(2)
res37: Foo = Foo(1,2)
Run Code Online (Sandbox Code Playgroud)
如果您确实想使用您的has方法(而不是直接应用该函数),请在其之上添加一个隐式类:
implicit class Func1Ops[-A,+R]( val f: A => R ) extends AnyVal {
def has( arg: A ): R = f( arg )
}
Run Code Online (Sandbox Code Playgroud)
现在你可以这样做:
scala> val x = partially(Foo)(1)
x: Int => Foo = <function1>
scala> x has 2
res38: Foo = Foo(1,2)
Run Code Online (Sandbox Code Playgroud)