2015-08-27 1 views
4

У меня есть идея (расплывчатое), чтобы передать (или цепи) некоторое неявное значение таким образом, не вводя параметры, чтобы блокировать f:неявный параметр «цепочки» для DSL

def block(x: Int)(f: => Unit)(implicit v: Int) = { 
    implicit val nv = v + x 
    f 
} 

def fun(implicit v: Int) = println(v) 

таким образом, что, если я использовал что-то одно:

implicit val ii: Int = 0 
block(1) { 
    block(2) { 
    fun 
    } 
} 

Он напечатал 3.

Если бы я мог сказать def block(x: Int)(f: implicit Int => Unit).

Другими словами, я ищу несколько шаблонов проектирования, которые позволят мне реализовать этот DSL: получить доступ к некоторому кумулятивному значению внутри вложенных блоков, но явно не передавать его в качестве параметра. Является ли это возможным? (implicit s не нужны, просто намек, чтобы подчеркнуть, что я не хочу передавать этот аккумулятор явно). Конечно, верхний код напечатает 0.

EDIT: Одно из возможных использований: (? Как) составление HTTP маршрутов, в следующем порядке

prefix("path") { 
    prefix("subpath") { 
    post("action1") { (req, res) => do action } 
    get("action2") { (req, res) => do action } 
    } 
} 

Здесь post и get будет доступ накопленного префикса, скажем List("path", "subpath") или "/path/subpath/".

ответ

3

Рассмотрите возможность использования DynamicVariable для этого. Это очень простой в использовании, и поточно-:

val acc: DynamicVariable[Int] = new DynamicVariable(0) 

def block(x: Int)(f: => Unit) = { 
    acc.withValue(acc.value + x)(f) 
} 

def fun = println(acc.value) 
+0

Это именно то, что мне нужно, спасибо! Никогда не слышал об этом :) – dmitry

1

Состояние передачи через implicit грязное и приведет к неожиданным и трудноотслеживаемым ошибкам. То, что вы просите сделать, это построить функцию, которая может быть создана таким образом, чтобы вложенные вызовы накапливались при некоторой операции, а что-то еще использует это значение для выполнения функции?

case class StateAccum[S](init: S){ 
    val op: S => S 
    def flatMap[A <: S](f: S => StateAccum[A]) ={ 
    val StateAccum(out) = f(s) 
    StateAccum(op(init, out)) 
    } 
    def apply(f: S => A) = f(init) 
} 

, который может позволить вам выполнять именно то, что вы делаете, с небольшими изменениями в том, как вы его называете.

Теперь, если вы действительно хотите вложенные структуры управления, ваше применение должно будет использовать неявное значение, чтобы отличать типы возврата так, чтобы оно применяло функцию к одному и возвращало flatMap в StateAccum. Это становится сумасшедшим, но выглядит следующим образом:

def apply[A](f: S => A)(implicit mapper: Mapper[S, A]): mapper.Out = mapper(this, f) 

trait Mapper[S, A]{ 
    type Out 
    def apply(s: StateAccum[S], f: S => A): Out 
} 

object Mapper extends LowPriorityMapper{ 
    implicit def acuum[S, A <: S] = new Mapper[S, StateAccum[A]]{ 
    type Out = StateAccum[A] 
    def apply(s: StateAccum[S], f: S => StateAccum[A]) = s.flatMap(f) 
    } 
} 

trait LowPriorityMapper{ 
    implicit def acuum[S, A] = new Mapper[S, A]{ 
    type Out = A 
    def apply(s: StateAccum[S], f: S => A) = f(s.init) 
    } 
} 
+0

Ну, это может быть какой-то практического использования в других случаях, но в моем случае я хочу DSL, который позволит вложенности 'f' и используя некоторые значение там, например 'block (1) {что-то, использующее кумулятивное значение; block (2) {что-то, использующее кумулятивное значение; }} '. 'for' не подходит для этого. – dmitry

+0

Не могли бы вы объяснить, как использовать «Mapper»? все еще не может попасть в него – dmitry

+0

@dmitry Это нормально, моя версия for-comp не будет делать то, что вы хотели. Однако редактирование на нем будет. Это связано с «магией». – wheaties