2011-05-27 91 views
10

假設我想捕獲異常,修復導致異常的問題並返回到發生異常的同一個執行點繼續。Scala繼續和異常處理

如何在Scala中繼續實現它?它有任何意義嗎?

回答

11

這裏是實現可恢復錯誤處理的可能方式之一:

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)) 

此代碼將產生以下輸出:

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

我曾經做過類似紅寶石的事情。這只是一個測試,看看我能否在Ruby中實現常見的lisp的「可恢復異常」。你應該可以在Scala中做同樣的事情,但我沒有嘗試過。您對一般概念或實施細節有疑問嗎?

總之,這裏是代碼(不附帶;))

#!/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

這個功能應該這樣做(放置,在foo ARG引發異常的代碼):

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

我使用這些類+隱式轉換:

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)

它可以讓你蟒蛇般的異常處理,像 "1a".toInt or 5