Значение по умолчанию для неявного параметра метода класса - PullRequest
1 голос
/ 14 января 2020

Я хочу иметь своего рода конструкцию «транзакции», в которой я делаю все изменения и затем решаю, следует ли зафиксировать или откатить в конце. Моя проблема в том, что я не знаю, как правильно определить / передать неявные значения, не определяя их вручную из места вызова функций. Как это можно сделать?

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

Ответы [ 2 ]

1 голос
/ 15 января 2020

Не комментируя мудрость этого (я думаю, это зависит), ничто не мешает вам предоставлять аргументы по умолчанию для ваших неявных параметров. Если вы сделаете это, разрешенный неявный будет иметь приоритет, но если неявный не найден, будет использован аргумент по умолчанию.

Однако ваша 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(...), который был отменен.

0 голосов
/ 14 января 2020

Вы можете определить свои неявные переменные в отдельном признаке или объекте следующим образом:

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)
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...