Eiffel exceptions

by Colin LeMahieu (modified: 2008 May 23)

A common question asked about Eiffel, especially those coming from Java or C# backgrounds is, where's my try...catch...finally block? Eiffel does provide exception handling mechanisms and the lack of try...catch...finally isn't a language oversight. The underlying reason for the mechanics of Eiffel's exception handling facilities lies in the purpose of routine; a routine should do one operation and either succeed or fail.

Try clauses work well when they're in isolation. When there are multiple try's per routine you run in to a spaghetti of flow control that's too complex to put in to a single routine.

Examples of try block breakdowns: function() // The second try block may not be executed depending on whether the catch block suppresses the exception.
// The first try block may not have been executed fully which requires the second try block to query the success of the first try block.
{ try{} catch{} try{} catch{} }

function() //This pattern occurs when cleanup in the catch block may fail. The outer and inner try blocks may not have fully completed, the inner try block will have to query to see what parts of the outer try block have completed. { try{} catch { try{} catch{} } }

function() //Yes, I've actually seen a function like this. This example looks a lot more benign than actual implementations with code inserted. { try { try{} catch{} finally { try{} catch{} } } catch{} }

How can I implement a try...catch...finally pattern in Eiffel? This simple pattern is equivalent to a try block and Eiffel limits it to one "block" per feature. feature is local l_no_retry: BOOLEAN do if not l_no_retry then --Try code here end l_no_retry := true -- Finally code here rescue if not l_no_retry then -- Catch code here l_no_retry := true retry end end

This example may look complex and this is semantically equivalent to a try block. Every time you write a try block this is what you're inferring to the compiler and people who have to maintain your code in the future. This is why try block are easy to write and nest but hard to maintain and be correct.

Just for completeness, an example with a retry counter: feature is local l_retry_count: INTEGER do if l_retry_count < 3 then --Try code here end l_retry_count := 3 -- Finally code here rescue if l_retry_count < 3 then -- Catch code here l_retry_count := l_retry_count + 1 retry end end