2013-11-27 3 views
1

Я пытаюсь соединить DSL-подобный набор функций в Скале, и то, что я хочу что-то эффект ...неявные параметры в «Closer» рамки для DSL

Где context что-то эффект ...

def context[A](f: Int => A) = f(2) 

проблема я бегу в том, что с этим синтаксисом, Scala сообщит, ambiguous implicit values. Я предположил, что если неявное было определено в «более близком» объеме, которое было бы перенесено на более отдаленное, но похоже, что это не так.

Есть ли способ сообщить scala, что один подразумеваемый более конкретный, чем другой (помимо того, что я думал, будет более сильным)?

ответ

3

Одним из возможных решений является определение внутреннего параметра таким же, как внешний параметр.

context { implicit y => 

в

context { implicit x => 

Это работает, но менее предпочтителен. Какие-нибудь лучшие решения?

1

Возможно, это не совсем то, что вы ищете, но это своего рода связанное, поэтому, возможно, это даст вам некоторые идеи.

Вы можете подтип класса неявных и воспользоваться тем фактом, что наиболее конкретным типа будет выбран:

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

    class A { override def toString = "A" } 
    class B extends A { override def toString = "B" } 

    implicit val a = new A 
    implicit val b = new B 

    def foo()(implicit x: A) { println(x) } 

// Exiting paste mode, now interpreting. 

defined class A 
defined class B 
a: A = A 
b: B = B 
foo:()(implicit x: A)Unit 

scala> foo() 
B 

Здесь мы определили метод foo принять неявный параметр типа А. Мы должны подразумевать vals типа A в области, a и b, но более конкретно, b, и поэтому нет никакой двусмысленности, и на выходе показано, что выбрано b.

Давайте попробуем адаптировать его к примеру:

class A 
class B extends A 

def foo()(implicit a: A) = a 

def bar[U](p: B => U) { p(new B) } 

implicit val a = new A 

bar { implicit b => 
    foo() 
} 

Хитрость здесь заключается в использовании подтипов класса в paremeter к bar методу (ваш context).

Немного расширенный пример, чтобы доказать, что он работает:

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

    class A { 
    override def toString = "A" 
    def getB = new B 
    } 

    class B extends A { 
    override def toString = "B" 
    } 

    def foo()(implicit a: A) { println(a) } 

    def bar[U](p: B => U)(implicit a: A) { p(a.getB) } 

    implicit val a = new A 

    def test() { 

    foo() // should print "A" 

    bar { implicit b => 
     foo() // should print "B" 
    } 
    } 

// Exiting paste mode, now interpreting. 

defined class A 
defined class B 
foo:()(implicit a: A)Unit 
bar: [U](p: B => U)(implicit a: A)Unit 
a: A = A 
test:()Unit 

scala> test() 
A 
B 

Есть два возможных подводных камней здесь: B должны удлинить, если вы забыли, что он будет печатать «А» в обоих случаях. И похоже, если вы забудете поставить ключевое слово implicit по параметру в вызове bar.

Что вы получили здесь, это возможность вызвать foo в разных контекстах с разными значениями A без их явного указания.

+0

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

Смежные вопросы