2011-05-27 20 views
10

Supongamos que me gustaría detectar una excepción, corregir el problema causado por la excepción y volver al mismo punto de ejecución donde se produjo la excepción para continuar.continuación de Scala y manejo de excepciones

¿Cómo puedo implementarlo con continuaciones en Scala? ¿Tiene algún sentido?

Respuesta

11

Aquí está una de las posibles formas de implementar el manejo de errores resumable:

import java.io.File 
import java.lang.IllegalStateException 
import scala.util.continuations._ 

// how it works 

ctry { 
    println("start") 

    val operationResult = someOperation(new File("c:\\ttttest")) 

    println("end " + operationResult) 
} ccatch { 
    case (DirNotExists(dir), resume) => 
    println("Handling error") 
    dir.mkdirs() 
    resume() 
} 

def someOperation(dir: File) = { 
    cthrow(DirNotExists(dir)) 
    println(dir.getAbsolutePath + " " + dir.exists) 
    "Operation finished" 
} 

// exceptions 

trait CException 
case class DirNotExists(file: File) extends CException 

// ctry/ccatch classes and methods 

sealed trait CTryResult[T] { 
    def get: T 
    def ccatch(fn: PartialFunction[(CException,() => T), T]): T 
} 
case class COk[T](value: T) extends CTryResult[T] { 
    def ccatch(fn: PartialFunction[(CException,() => T), T]) = value 
    def get = value 
} 
case class CProblem[T](e: CException, k: Any => Any) extends CTryResult[T] { 
    def ccatch(fn: PartialFunction[(CException,() => T), T]) = 
      fn((e,() => k(Unit).asInstanceOf[T])) 
    def get = throw new IllegalStateException("Exception was not processed: " + e) 
} 

def ctry[T](body: => T @cps[Any]) = reset (body) match { 
    case (e: CException, k: (Any => Any)) => CProblem[T](e, k) 
    case value => COk(value) 
} 

def cthrow(e: CException): Any @cps[Any] = shift((k: Any => Any) => (e, k)) 

Este código produce siguiente resultado:

start 
Handling error 
c:\ttttest true 
end Operation finished 
2

Una vez hice algo así en ruby. Fue solo una prueba para ver si podía implementar las "excepciones reanudables" comunes de lisp en ruby. Deberías poder hacer lo mismo en Scala, pero no lo he intentado. ¿Su pregunta es sobre el concepto general o sobre los detalles de implementación?

De todos modos, aquí está el código (sin garantía;))

#!/usr/bin/env ruby 

require 'continuation' 

#Module for adding elements of an array. Leaves error handling to the caller by using exceptions and continuations. 
module Adder 

#Exception class that offers continuations to the receiver. 
    class CcExc < Exception 
     def initialize(again, skip, index, sum) 
      @again = again 
      @skip = skip 
      @index = index 
      @sum = sum 
     end 
     def again 
      @again.call 
     end 
     def skip 
      @skip.call 
     end 
     attr_reader :index #where the problem occured 
     attr_reader :sum #current sum 
    end 

    #Method to get the current continuation 
    def Adder.getcc 
     cc = nil 
     callcc {|c| cc = c} 
     cc 
    end 

    #add all numbers in the array, raise an exception with continuations if an 
    #item doesn't have the right type 
    def Adder.addAll(array) 
     sum = 0; 
     array.each_with_index {|dummy,i| 
      again = getcC#save continuation before processing the item 
      if array[i].is_a? Numeric 
       sum += array[i] #process item normally 
      else 
       #raise exception with previously save continuation (again) 
       #and current continuation (skip) 
       callcc {|skip| raise CcExc.new again, skip, i, sum} 
      end 
     } 
     sum 
    end 
end 

data = [1,"2",3,"hello",Object,"4",5,"END",6] 
begin 
    puts "The sum is #{Adder.addAll data}." 
rescue Adder::CcExc => e 
    puts "Exception raised." 
    i = e.index 
    case data[i] 
     when /^\s*\d/ 
      data[i] = data[i].to_i 
      puts 'Problem fixed. Continue adding.' 
      e.again 
     when "END" 
      puts "'END' found. Stop processing." 
      puts "The sum is #{e.sum}" 
     else 
      puts "'#{data[i]}' of type #{data[i].class} can't be converted " + 
       "to interger. Item skipped." 
      e.skip 
    end 
end 
2

Esta función debe hacerlo (coloque el código que genera excepciones en foo arg):

def F[T](foo: => T, dealWithError: Exception => T): T = 
    try foo 
    catch{ 
    case ex: Exception => dealWithError(ex)} 

utilizo estos clase + conversión implícita:

class ORfoo[R](foo:() => R){ 
    def or(r: R): R = 
     try foo() 
     catch{ 
     case ex: Exception => r 
     } 
    } 

implicit def ORfooWrapper[R](f: => R) = new ORfoo(() => f)

que le permite el tratamiento excepción python-como, como "1a".toInt or 5