#scala
#scala
Вопрос:
У меня есть класс, который принимает неявный параметр, который используется функциями, вызываемыми внутри методов класса. Я хочу иметь возможность либо переопределить этот неявный параметр, либо, в качестве альтернативы, скопировать неявный аргумент из его источника. В качестве примера:
def someMethod()(implicit p: List[Int]) {
// uses p
}
class A()(implicit x: List[Int]) {
implicit val other = List(3) // doesn't compile
def go() { // don't want to put implicit inside here since subclasses that override go() have to duplicate that
someMethod()
}
}
Поведение, которого я хочу, заключается в том, что someMethod() получает неявный параметр, представляющий собой некоторую измененную версию x, которая была неявным параметром класса. Я хочу иметь возможность либо изменять x, не изменяя его для того, что передало его в конструктор A, либо иным образом переопределять его на новое значение по моему выбору. Похоже, оба подхода не работают. То есть в первом случае список не копируется, и компилятор находит неоднозначное неявное значение для последнего случая. Есть ли способ сделать это?
Я понимаю, что могу переопределить неявное значение в go (), но в моем случае это не лучший выбор, потому что этот класс многократно подклассировался, и я хотел бы обработать это неявное изменение только в базовом классе. Таким образом, это не обязательно должно быть в конструкторе, но это должно быть в методе, отличном от go ().
Комментарии:
1. Похоже, вы быстро вступаете на территорию не поддерживаемого кода. Какова ваша логика в том, что вы хотите использовать неявный для параметра на
someMethod
? Почему бы просто не сделать его явным параметром?2. @Kevin В моем конкретном случае существуют тысячи подклассов, подобных A, и они создаются тысячи раз. Удобно не передавать один и тот же параметр каждому из них. Почему вы говорите, что это не поддерживается?
3. При попытке разобраться в вашем коде, если вы видите конкретное неявное значение некоторого типа в области видимости, то естественно ожидать, что неявное значение того же типа, расположенное глубже в стеке вызовов, будет тем же экземпляром. Если это предположение неверно, то это может сильно затруднить отладку для тех, кто не знает об изменении.
Ответ №1:
Введите другой тип оболочки, просто чтобы устранить неоднозначность:
// badly named, choose something domain-specific
case class ListHolder(theList: List[Int])
def someMethod()(implicit holder: ListHolder) {
val xs = holder.theList
// uses xs ...
}
class A()(implicit xs: List[Int]) {
implicit val other = ListHolder(42 :: xs) // compiles
def go() {
// xs is never considered for the implicit param to someMethod()
// because it's now the wrong type
}
}
Это также делает код более самодокументируемым, поскольку становится совершенно очевидным, что два имплицита не являются одним и тем же.
Ответ №2:
Если вы хотите, чтобы вокруг плавали миллиарды подразумеваемых элементов, которые не сталкиваются друг с другом, вы можете создать класс-оболочку, который вы можете пометить признаками маркера для неявного использования. Существует множество синтаксисов, которые вы могли бы использовать; вот один пример:
object Example {
class Implication[A,B](val value: A) {
def apply[C](c: C) = new Implication[C,B](c)
}
object Implication {
def mark[B] = new Implication[Unit,B](())
implicit def implication_to_value[A,B](i: Implication[A,B]) = i.value
}
trait One {}
trait Two {}
implicit val x = Implication.mark[One]("Hello")
implicit val y = Implication.mark[Two]("Hi")
def testOne(implicit s: Implication[String,One]) = println(s: String)
def testTwo(implicit s: Implication[String,Two]) = println(s: String)
def testThree(s: String) = println("String is " s)
def main(args: Array[String]) {
testOne
testTwo
testThree(x)
testThree(y)
}
}
Который работает так, как вы надеетесь:
scala> Example.main(Array())
Hello
Hi
String is Hello
String is Hi
Поскольку вам приходится использовать объект-оболочку, это не очень эффективно, но может быть очень эффективным. (Или очень запутанный, учитывая, сколько всего происходит неявно.)
Ответ №3:
Эта модификация компилируется. Я изменил x на var:
class A()(implicit var x: List[Int]) {
def someMethod()(implicit p: List[Int]) {
// uses p
}
x = List(3)
def go() { // don't want to put implicit inside here since subclasses that override go() have to duplicate that
someMethod()
}
}