2012-04-11 8 views
7

Mi trovo a ripetere semplici blocchi try/catch che dovrebbero, IMO, essere 1-liner.Scala DRYing try/catch

Ad esempio, supponiamo di dover convertire una data stringa uri nel formato yyyymmdd in Joda-Time. Con uno standard try/catch bloccare il metodo di conversione appare come:

def ymd2Date(ymd: Option[String]) = ymd match { 
    case Some(date) => 
    try { Right(ymdFormat.parseDateTime(date)) } 
    catch { case e => 
     log.info(e.getMessage) 
     Left(None) 
    } 
    case None => 
    Left(None) // no uri date param 
} 
val ymdFormat = DateTimeFormat.forPattern("yyyyMMdd") 

funziona abbastanza bene, l'intento è chiaro, ma quando faccio questo tipo di try/catch registrazione per tutti eventi non critici, poi ho cercare un modo per ASCIUGARLA. La ricerca mi ha portato a questo SO post su scala.util.control.Exception. Utile, ma è ancora un po 'difficile da convincere/farlo funzionare nel modo in cui mi piacerebbe farlo. In parole povere voglio solo dire "catch-get-get log-error-type" di qualche azione.

Così, ho inciso questo fuori in base alla parte del control.Exception mi interessa (o capire di essere utile):

class Catcher[T](f: => T) { 
    type Logger = (=> Any) => Unit 

    def either[T](logger: => Logger) = { 
    try { Right(f) } 
    catch { case e => 
     logger(e.getMessage) 
     Left(None) 
    } 
    } 
} 
def catching[T](f: => T) = new Catcher(f) 

e quindi utilizzare al posto di try/catch in questo modo:

catching(ymdFormat.parseDateTime(date)) either log.info 

possono aggiungere in opzione, un prefisso msg, ecc, ma ... probabilmente sarebbe meglio trovare un modo per ottenere control.Exception per funzionare come sopra, come l'equipaggio Typesafe sta per produrre mondi di codice migliori di quanto immaginerei mai di scrivere.

Qualcuno sa come creare questo tipo di sintassi utilizzando control.Exception dove si può passare in una funzione di logger per nome da utilizzare nel blocco catch?

sarebbe bello se ci fosse un "casi d'uso" per control.Exception, ma ho la sensazione questa utility è per maggiori sviluppatori Scala avanzate

+3

Perché usi 'O'? Se tutto quello che stai per mettere in 'Left' è' None', sembra che tu possa usare 'Option [T]'. – huynhjl

+0

buon punto, volevo piegare il risultato e O sembrava un adattamento naturale ... – virtualeyes

+0

che vuol dire che al momento non esiste un'opzione di piegatura, o almeno non incorporata nella lingua – virtualeyes

risposta

7

Questo dovrebbe fare quello che vuoi:

import scala.util.control.Exception 
def log(logger: => Logger)(e: Throwable) = { 
    logger(e.getMessage) 
    None 
} 
Exception.allCatch withApply log(logger) apply Some(ymdFormat.parseDateTime(date)) 

Ma questo genere di cose è meglio gestita da Scalaz Validation, a mio parere.

+0

giusto, devo ancora avventurarmi in fila Scalaz, solo bagnandomi i piedi in Scala. La convalida nella sua forma più semplice appare come Either, ma poi la curva si innalza rapidamente. E 'sulla cosa da fare ... – virtualeyes

+0

che è abbastanza bello, btw, sembra che posso rompere il mio trucco e prendere un approccio che qualcuno diverso da me capirà ;-) – virtualeyes

+2

@virtualeyes - Per quello che vale, il tuo "hack" per me è molto più chiaro dell'approccio di Daniel (sarebbe più chiaro se non lo chiamassi 'catching' ma piuttosto 'tryOrLog' o qualcosa che non suggerisca che il primo argomento verrà catturato). –

3

Un esempio veloce:

import scala.util.control.Exception._ 

def throwingStuff { 
    throw new Exception("Hello World!") 
} 

catching(classOf[Exception]).withApply{err => println(err.toString); None}.apply(Some(throwingStuff)) 

Puoi utilizzare withApply per sovrascrivere la logica dell'applicazione della classe Catch per eseguire operazioni come la scrittura su un registro.

+0

Il tipo di ritorno non è abbastanza lavorare con questo. 'withApply' converte il' Throwable' in un valore di ritorno, quindi non otterrai mai 'Left', e il tipo di' Right' sarà 'Any' o' AnyRef'. –

+0

Sì, realizzato e modificato poco prima del tuo commento! Usare 'Some' è una buona idea, però. – Submonoid

+0

È necessario conservare il tipo. Se vado Opzione [T] sarà il tipo essere conservato/accessibile? – virtualeyes