1

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

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
4

2 回答 2

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(...) 函数并没有做你想做的事,现在,无济于事的是,它并没有引起人们注意它没有引起你想要的错误的事实。它只是做错事。in 的操作不是获取不在范围内的隐式值,而是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)

所以这“有效”。但实际上,由于您在块完成后自动提交,除非它以异常完成,否则我对 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)

尽管add(...)我明确要求完成rollback!那是因为这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(...)被恢复了。

于 2020-01-14T22:51:11.193 回答
0

您可以在单独的特征或对象中定义隐式变量,如下所示:

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)
  }
}
于 2020-01-14T18:11:00.413 回答