2014-10-05 8 views
5

Szukam takiego typu, który pozwoli mi reprezentować kontekst, w którym uruchamiany jest fragment kodu. Na przykład:Czy istnieje typ kontynuacji do zawijania bloku wykonawczego, jak (Ctx => R) => R lub (=> R) => R?

def withinContext[R]: ((=> R) => R) = 
    (inner) => { 
    initializeSomeResource() 
    try { 
     inner 
    } finally { 
     releaseTheResource() 
    } 
    } 

które następnie można używać po prostu jako

withinContext { 
    ... 
} 

lub, jeżeli wewnętrzny blok kodu wymaga pewnych informacji z kontekstu, uogólnić go jako

def withinContext[R]: ((Ctx => R) => R) = ... 

Ich przypadki użycia w przybliżeniu odpowiadają Haskellowi bracket_ i bracket.

mogę używać typów (=> R) => R i (A => R) => R bezpośrednio, ale wtedy nie mam funkcje ułatwiające łączenie takich obwolut kontekstu, więc zastanawiam się, jest coś takiego już istniejący w ekosystemie Scala?

zamyka co wiem, to scala.util.control.Exception.Catch, który zapewnia przyjemne funkcje do budowy i łączenia Catch instancje, ale wydaje się, że nie ma mowy o żadnej inicjalizacji przed uruchomiony wewnętrzny blok jest wykonywany. Również (nie jest to tak ważne dla mojego przypadku użycia) nie pozwala na podanie parametrów do wewnętrznego obliczenia, tak jak w przypadku (A => R) => R.

Typ to monada kontynuacyjna, odpowiadająca Haskellowi ContT r IO a, ale nie mogłem znaleźć implementacji kontynuacyjnej monady w żadnej standardowej bibliotece Scali (być może jest ukryta gdzieś głęboko w Scalazie, gdzie tęskniłem).

+0

Nie żeby to w ogóle pomogło, ale dla własnego zrozumienia, czy byłoby sens definiować wewnątrz Kontekstu jako "def w obrębie Kontekstu [R] (r: => R): R"? – acjay

+0

@acjay To prawda, chciałem tylko wskazać pełny typ, którego szukam. –

Odpowiedz

0

Robię tego rodzaju rzeczy dość często do użytku z kodem testowym Specs2. Zasadniczo chcemy, aby jakiś kontekst został ustawiony tak, aby otaczał blok kodu. Idiom używam jest tak:

def contextName[TYPE,RSRC](doit: (RSRC) => TYPE) : TYPE = { 
    val thing : RSRC = acquireResource(args) 
    try doit(thing) 
    finally releaseResource(thing) 
} 

Zdaję sobie sprawę, staramy się kontynuację kontynuacji, ale muszę zapytać: dlaczego? Podany przeze mnie idiom jest kontynuacją monady typu (RSRC => TYPE) => TYPE i może być używany tak, jak zasugerowałeś ze swoim withContext. Na przykład, prawdziwy przypadek użycia z obiektu kontekstowego ReactiveMongo pisałem:

def withDatabase[T](dbName: String)(doit: (DefaultDB) => T) : T = { 
    val db = connection.db(dbName) 
    try doit(db) 
    finally db.drop() 
} 

ten przejmuje bazę danych, przekazuje go do kontynuacji, a następnie spadnie do bazy danych, gdy jest dokonywane nawet jeśli kontynuacja zgłasza wyjątek . Jest używany w następujący sposób:

val numCollections = withDatabase("foo") { db => 
    db.collectionNames.map { list => list.size } 
} 

Który po prostu używa bazy danych do uzyskania (przyszłej) liczby zbiorów w bazie danych.

Mam nadzieję, że to pomoże.

Powiązane problemy