class 方法隐式参数的默认值

Default value for implicit parameter of class method

我想要一种 "transaction" 结构,我在其上进行所有更改,然后决定最后是提交还是回滚。我的问题是我不知道如何正确定义/传递隐式值而不从调用函数的地方手动定义它们。如何实现?

class Foo {
  var m = scala.collection.mutable.HashMap.empty[String, String]

  case class Tx(mcopy: scala.collection.mutable.HashMap[String, String]) {
    def commit = (m = mcopy)
    def rollback = () // not copying mcopy will lose all changes made to it
  }

  def withTx(block: Foo => Unit): Unit = {
    implicit val tx = new Tx(m.clone)
    try {
      block(this)
      tx.commit
    } catch {
      case _: Throwable => tx.rollback
    }
  }

  implicit val emptyTx = new Tx(m) // non-tx operations will be performed directly on 'm'

  def add(k: String, v: String)(implicit t: Tx): Unit = (t.mcopy += k -> v)
}

val f = new Foo
f.add("k0", "v0") // error: no implicit t defined...
f.withTx { foo => foo.add("k1", "v1") } // errors as well on missing implicit

您可以像这样在单独的特征或对象中定义隐式变量:

trait MyImplicits {
  implicit val myImplicitParameter: Int = 0
}

trait MyInterface {
  def implicitMethod(a:Int)(implicit b: Int) = ???
}

object MyClass extends MyInterface with MyImplicits {
  def main(args: Array[String]) = {
    implicitMethod(1)
  }
}

在不评论这个智慧的情况下(我认为这取决于),没有什么能阻止你为你的隐式参数提供默认参数。如果这样做,已解析的隐式将优先,但如果未找到隐式,将使用默认参数。

但是,无论如何,您的 withTx 函数都不会工作,因为您定义的隐式不在函数块的范围内。 (您不可能从您在那里定义的函数中引用 tx。)

要修改您的示例(为交易添加标签以明确说明):

class Foo {
  var m = scala.collection.mutable.HashMap.empty[String, String]

  case class Tx(label : String, mcopy: scala.collection.mutable.HashMap[String, String]) {
    def commit = (m = mcopy)
    def rollback = () // not copying mcopy will lose all changes made to it
  }

  def withTx(block: Foo => Unit): Unit = {
    implicit val tx = new Tx("oopsy", m.clone)
    try {
      block(this)
      tx.commit
    } catch {
      case _: Throwable => tx.rollback
    }
  }

  implicit val emptyTx = new Tx("passthrough", m) // non-tx operations will be performed directly on 'm'

  def add(k: String, v: String)(implicit t: Tx = emptyTx): Unit = {
    println( t )
    t.mcopy += k -> v
  }
}

然后...

scala> val f = new Foo
f: Foo = Foo@3e1f13d2

scala> f.add( "hi", "there" )
Tx(passthrough,Map())

scala> implicit val tx = new f.Tx( "outside", scala.collection.mutable.HashMap.empty )
tx: f.Tx = Tx(outside,Map())

scala> f.add( "bye", "now" )
Tx(outside,Map())

但是你的 withTx(...) 函数没有做你想做的事,现在,毫无帮助的是,它没有提醒你注意它没有做你想做的事并出现错误。它只是做错了事。 block 中的操作不是获取不在范围内的隐式值,而是获取默认参数,这与您的意图相反。

scala> f.withTx( foo => foo.add("bye", "now") )
Tx(passthrough,Map(bye -> now, hi -> there))

更新:

要获得您想要的那种 withTx 方法,您可以尝试:

  def withTx(block: Tx => Unit): Unit = {
    val tx = new Tx("hooray", m.clone)
    try {
      block(tx)
      tx.commit
    } catch {
      case _: Throwable => tx.rollback
    }
  }

用户需要在他们的区块中将提供的交易标记为 implicit。应该是这样的:

scala> val f = new Foo
f: Foo = Foo@41b76137

scala> :paste
// Entering paste mode (ctrl-D to finish)

f.withTx { implicit tx =>
  f.add("boo","hoo")
  tx.commit
}

// Exiting paste mode, now interpreting.

Tx(hooray,Map()) // remember, we print the transaction before adding to the map, just to verify the label

scala> println(f.m)
Map(boo -> hoo)

所以"worked"。但实际上,由于您在块完成后自动提交,除非它以异常完成,否则我对 tx.commit 的调用是不必要的。

我认为这不是一个很好的选择。看这个:

scala> :paste
// Entering paste mode (ctrl-D to finish)

f.withTx { implicit tx =>
  f.add("no","no")
  tx.rollback
}

// Exiting paste mode, now interpreting.

Tx(hooray,Map(boo -> hoo)) // remember, we print the transaction before adding to the map, just to verify the label

scala> println(f.m)
Map(no -> no, boo -> hoo)

尽管我明确调用了 rollbackadd(...) 还是完成了!那是因为 rollback 只是一个空操作,然后是自动提交。

要真正看到回滚,您需要抛出一个 Exception:

scala> :paste
// Entering paste mode (ctrl-D to finish)

f.withTx { implicit tx =>
  f.add("really","no")
  throw new Exception
}

// Exiting paste mode, now interpreting.

Tx(hooray,Map(no -> no, boo -> hoo)) // remember, we print the transaction before adding to the map, just to verify the label

scala> println(f.m)
Map(no -> no, boo -> hoo)

现在,我们终于可以看到对 add(...) 的调用被还原了。