Не комментируя мудрость этого (я думаю, это зависит), ничто не мешает вам предоставлять аргументы по умолчанию для ваших неявных параметров. Если вы сделаете это, разрешенный неявный будет иметь приоритет, но если неявный не найден, будет использован аргумент по умолчанию.
Однако ваша 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)
Так что «сработало». Но на самом деле, поскольку вы вставляете автоматический c коммит после завершения блока, если он не завершается с исключением, мой вызов 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
просто не работает, а за ним следует автоматический c коммит.
Чтобы увидеть откат, вам нужно бросить 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(...)
, который был отменен.