考虑一个定义两个属性的抽象类
abstract class A {
def a: Int
def b: Int
// real A has additional members
}
是各种案例类的基类,例如
case class Foo(a: Int, b: Int) extends A
case class Bar(a: Int, b: Int) extends A
// and many more
目标:我最终希望能够以两种方式创建上述案例类的实例,即
val b1 = Bar(1, 2)
val b2 = Bar(1) has 2
assert(b1 == b2) // must hold
方法:因此,定义一个定义has
的助手类并允许我部分构建A
s
case class PartialA(f: Int => A) {
def has(b: Int) = f(b)
}
问题:当前机制不允许Bar(1)
之类的调用,因为这实际上是Bar.apply(1)
的调用,即方法{{1}的调用}由编译器生成的对象apply
定义。
如果我可以强制编译器生成Bar
对象Bar
,那将是很好的,其中
object Bar extends PartialAConstructor
但是,似乎不可能影响案例类的伴随对象的生成。
所需的属性:
案例类: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))
}
,Foo
等应该保留案例类,因为我想使用编译器生成的好东西,例如结构相等,Bar
并自动生成提取器。
“完全”结构相等:将案例类定义为
copy
不是一个选项,因为编译器生成的case class Bar(a: Int)(val b: Int)
方法只考虑第一个参数列表,因此以下内容会被错误地保留:
equals
尽可能少重复代码:例如,当然可以定义
assert(Foo(1)(0) == Foo(1)(10))
但是必须对每个扩展def Bar(a: Int) = PartialA((b: Int) => Bar(a, b))
的案例类进行此操作,即A
,Foo
等。
答案 0 :(得分:3)
你可能会非常依赖于currrying(事实上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
}
然后你可以这样做:
scala> val x = partially(Foo)(1)
x: Int => Foo = <function1>
scala> x(2)
res37: Foo = Foo(1,2)
如果你真的想使用你的has
方法(而不是直接应用这个函数),那么在它上面引入一个隐式类:
implicit class Func1Ops[-A,+R]( val f: A => R ) extends AnyVal {
def has( arg: A ): R = f( arg )
}
现在你可以这样做:
scala> val x = partially(Foo)(1)
x: Int => Foo = <function1>
scala> x has 2
res38: Foo = Foo(1,2)
答案 1 :(得分:2)
有什么问题
val x = Foo(1, _: Int)
您还可以向同伴添加apply
方法,该方法只需1个arg并为您执行部分应用。
除此之外,对于尚未发布的类型宏可能有一种方法可以实现,但您可以在macro paradise中使用它们。
修改:
要向案例类伴随添加内容,只需按照通常的方式执行操作:
case class Foo(x: Int, y: Int)
object Foo {
def apply(x: Int): (Int => Foo) = Foo(x, _: Int)
}
scala> Foo(1,2)
res3: Foo = Foo(1,2)
scala> Foo(1)
res4: Int => Foo = <function1>
在申请表中,您也可以退回您的PartialA或任何您喜欢的内容。
答案 2 :(得分:1)
假设你真的想要&#34;有&#34; DSL,并且可能希望它可扩展,以下也适用:
abstract class A {
def a: Int
def b: Int
}
trait PartialHas[T] {
self: { def apply(a: Int, b: Int): T } =>
trait HasWord { def has(b: Int): T }
def apply(a: Int): HasWord = new HasWord { def has(b: Int): T = apply(a, b) }
}
case class Bar(a: Int, b: Int) extends A
object Bar extends PartialHas[Bar]
可能有一种方法可以使用类宏来完全取消显式伴随定义。