chicken-users
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Chicken-users] Gazette issue 18


From: Christian Kellermann
Subject: [Chicken-users] Gazette issue 18
Date: Tue, 25 Jan 2011 22:39:22 +0100
User-agent: Mutt/1.5.20 (2009-06-14)

Dear Fans,

after a two week resting phase I am proud to give you issue 18.

Thanks to

Moritz for writing the Chicken Talk section, Peter providing support
and listen to me whine, Mario for being the net reporter, rien_ for
proofreading and the other nice people on #chicken.

Enough now, enjoy!


     _/_/_/  _/        _/            _/
  _/        _/_/_/          _/_/_/  _/  _/      _/_/    _/_/_/
 _/        _/    _/  _/  _/        _/_/      _/_/_/_/  _/    _/
_/        _/    _/  _/  _/        _/  _/    _/        _/    _/
 _/_/_/  _/    _/  _/    _/_/_/  _/    _/    _/_/_/  _/    _/

--[ Issue 18 ]------------------------------------- G A Z E T T E
                               brought to you by the Chicken Team


== 0. Introduction

Welcome to issue 18 of the Chicken Gazette.

== 1. The Hatching Farm

The last two weeks have been busy times in our egg repository. The
following eggs have been updated:

  * Peter Bex
    * epeg released v2.4 * imlib released v0.13 * slatex released
    20090928.1 * wmiirc released 0.5 * svn-client released v0.16
  * Thomas Chust
    * openssl released version 1.5.1
  * Moritz Heidkamp
    * remote-mailbox-threads released version 0.0.1 * char-set-literals
    tag version 0.1 * pool tag version 0.2 * hyde tag version 0.14
  * Kon Lovett
    * bloom-filter released 1.1.1
  * Ivan Raikov
    * flsim has initial support for Octave code generation now
  * Jum Ursetto
    * sql-de-light released 0.4.3
  * Felix Winkelmann
    * unix-sockets tagged v1.5 * bind released 0.93^W0.94 * coops
    released v1.3

New eggs in our farm:

  * Moritz created validator * Nick Gasson released Slime v1.0, now
  that it is in an egg, you don't need to go through the git install
  hoop to get
    it. Thanks Nick!

Egg tickets fixed:

  * Ticket #480 utf8 egg noop->void in experimental branch.  *
  Ticket #476 unix-sockets missing (require-library posix) * Ticket
  #477 unix-sockets using obsolete pointer type * Ticket #471
  (message-digest tests fail * Ticket #470 locale egg should rename
  getenv calls * Ticket #468 MAGIC-LIMIT is undefined in lookup-table

Ongoing stuff

  * Peter Bex worked hard on crypt the last weeks * Alan Post put
  tremendous effort into bug fixes in genturfahi.  * Alaric Snell-Pym
  worked on egg:ugarit, adding as he says "...just a few more unit
  tests. [P]rogress is slow but
    steady though."

== 2. Core development

We part from the following bugs:

  * Ticket #484 -scrutinize not properly expanding match macro,
  reported by Alan Post.  * Ticket #479 bind egg should support
  ___blob * Ticket #466 csc -gui broken (OS X)

But also the core has been busy:

CR439 has been added by Peter Bex as voting time is up and no one
spoke (against it).

A Bug related ##sys#unblock-threads-for-i/ has been reported by Jim
Ursetto and fixed in experimental.

A new function called /condition->list/ has been added to the
library. This allows conversion and handling of conditions without
knowledge of the inner structure of conditions.

Of course some entries have been missing again from the types.db,
reported by Kon Lovett.

/(compile-file)/ now returns /#f/ on error.

The default /trace-buffer/ size is now 16.

Sven Hartrumpf provided a patch to the /make-dist/ helper scripts
fixing the deprecated /getenv/ procedure, the new one is named
/get-environment-variable/.

/__byte_vector/ has been marked deprecated. /__blob/ has been added
to bind.

The /-gui/ option has been fixed, as it tried to link with chicken.rc.o
on all platforms. Thanks to ddp for reporting that.

Taylor Venable has reported serious breakage in the accumulated
profiling code, thanks!

Christian Kellermann spotted a problem in the newly changed equal?
patch.

Kon also spotted inefficient type checks for the 64 bit integers.
Felix fixed it, then Sven provided a patch for broken integer64
type checks.

Felix Winkelmann appeased some compiler warnings in the runtime
code, apart from being busy applying all the patches :)

Jules Altfas spotted that /vector-copy!/ was missing from /types.db/.
Good catch!

== 3. Chicken Talk

Christian Kellermann announced
(http://lists.nongnu.org/archive/html/chicken-users/2011-01/msg00055.html)
that he has set up a salmonella instance for Chicken's experimental
branch on his server
(http://pestilenz.org/~ckeen/salmonella-report/index.html). If you
are an egg maintainer and would like to know whether your eggs will
break with the upcoming Chicken, check out the reports. Christian
is going to move the installation over to call-cc.org once the
configuration has stabilized.

David Dreisigmeyer had a nice surprise for the mailing list last
week. After asking
(http://lists.nongnu.org/archive/html/chicken-users/2011-01/msg00064.html)
a few suspicious questions
(http://lists.nongnu.org/archive/html/chicken-users/2011-01/msg00080.html)
about embedding Chicken he came out with a Chicken REPL embedded
in a Python REPL
(http://lists.nongnu.org/archive/html/chicken-users/2011-01/msg00103.html).
Now that's nice!

Sandro Schugk kicked off a discussion
(http://lists.nongnu.org/archive/html/chicken-users/2011-01/msg00099.html)
about the type relationship of lists and pairs, featuring insightful
comparisons with Common Lisp's view of this affair as well as an
ASCII art type diagram
(http://lists.nongnu.org/archive/html/chicken-users/2011-01/msg00119.html)
by Thomas Chust.

Vilson Vieira found a shortcoming
(http://lists.nongnu.org/archive/html/chicken-users/2011-01/msg00120.html)
in bind's typedef parsing. Felix Winkelmann announced to implement
the missing functionality as soon as he gets around to it
(http://www.quantumenterprises.co.uk/roundtuitimages/padded_case2_L.jpg).
Judging by Vilson's site (http://automata.cc/) we can probably hold
our breaths for something really cool to come out of this!

Finally, Alan Post (again) uncovered a problem
(http://lists.nongnu.org/archive/html/chicken-users/2011-01/msg00122.html)
while working on his infamous parser genturfahi. It turned out to
be caused by a bug
(http://lists.nongnu.org/archive/html/chicken-users/2011-01/msg00138.html)
in matchable's recently added tail-pattern support. Alex Shinn has
promised to fix it as soon as possible. Thanks Alan for shaking out
yet another bug and thanks Alex for your continued great job on
your Scheme libraries!

Meanwhile elsewhere on the internets... Mario has found these gems:

Vilson Vieira (http://automata.cc/) started coding Chicken bindings
to PortAudio in his github repo
(http://github.com/automata/chicken-portaudio). He's got some help
from Moritz via #chicken.

On a reddit thread
(http://www.reddit.com/r/scheme/comments/f5kxh/can_you_produce_programs_as_fast_in_scheme_as_in/),
Chris Targett mentioned he started binding inotify
(https://github.com/xlevus/chicken-stuff/blob/master/inotify) to
Chicken. Chris has also got his repository of Chicken stuff on
github (https://github.com/xlevus/chicken-stuff/).

After seeing a very silly benchmark
(http://call-with-hopeless-continuation.blogspot.com/2010/03/lies-damn-lies-and-benchmarks.html)
performed by Mario Domenech Goulart, Bradley Lucier
(http://www.math.purdue.edu/~lucier/) started a thread
(https://mercure.iro.umontreal.ca/pipermail/gambit-list/2011-January/004770.html)
on Gambit's mailing list showing some results which indicate that
"Chicken really smokes Gambit" on this femtobenchmark. The question
is, should it?" Marc Feeley follows up with the reassuring statement
(https://mercure.iro.umontreal.ca/pipermail/gambit-list/2011-January/004774.html):
/The x86 back-end runs fib_scm about 2.5 times faster than when
using the C back-end. That's faster than Chicken, and also Larceny
and Ikarus (which have x86 back-ends). The Gambit x86 back-end
supports both x86-32 and x86-64./ So we guess all is well again.

C'esar L. B. Silveira (http://cesarbs.notapipe.org/) translated the
C code for tinywm (http://incise.org/tinywm.html) to Chicken. The
resulting code can be found at
http://cesarbs.notapipe.org/tinywm-scheme.html.

== 4. Omelette Recipes

Today I will introduce you to a much asked for topic of Chicken
Scheme on our IRC channel last week: Exception handling in Chicken
Scheme.

=== These are our conditions

A newsgroup posting once had this quote:

  To err is human to restart divine

which relates to Common Lisp being able to do so called /restarts/
on error conditions. In Chicken Scheme there are no restarts but
it does come with a condition system that allows programmers to
recover from exceptional conditions.  Before I start, let me introduce
a bit of vocabulary:

  - exception: A situation that needs special attention. Often used
  synonymously with condition.  - condition: An object representing
  a current exceptional event.  - exception-handler: A procedure
  that gets called with a /condition/ object - signal: A procedure
  that "raises" a condition by invoking the condition-handler with
  a condition object.  - abort: A procedure that also raises a
  condition but ensures that the condition handler cannot return
  without
           raising another error. See below.

Conditions should be raised whenever the program encounters a
situation where it cannot go on without interaction with the calling
code. Typical situations are: Operations on closed file descriptors,
broken sockets, non existing files, memory allocation failures,
syntax or type errors.

As some of these conditions might be corrected in the handler and
then be allowed to continue, Chicken Scheme's condition system
distinguishes between two types of conditions: /continuable/ and
/non-continuable/ conditions. As the name suggests non-continuable
conditions are such severe events that the code cannot possibly
continue at the location where the condition has been raised. Type
errors fall in this category, i/o errors as well as others. Continuable
conditions on the other hand /can/ continue where they have been
signaled if the handler decides to do so. It may be possible to
correct the error in the handler and have the code go along with
it.

=== Condition objects

A condition object is a distinct data type that consists of a
condition /kind/ and of /property/ / /value/ pairs. These are also
called /property-conditions/ and therefore the constructing procedure
is

   (make-property-condition kind-key prop-key value ...)

Where /kind-key/ and /prop-key/ are symbols. The constructor accepts
any number of key / value pairs. The most basic condition signalled
by the Chicken Scheme system is the /exn/ condition kind. All /exn/
conditions have three properties:

  - message: Textual message explaining the error.  - arguments:
  The arguments passed to the condition handler.  - location: The
  name of the procedure which raised the condition.

To access properties in your code there are a couple of helper
procedures defined in SRFI-12: /condition-predicate/,
/condition-property-accessor/ and /get-condition-property/. The
/condition-predicate/ procedure generates a predicate function that
can be used to decide whether the condition kind is interesting to
you:

   (let ((exn (make-property-condition 'all-my-fault 'message "I
   have failed, master")))
       (if ((condition-predicate 'all-my-fault) exn)
           (print "He has failed again!") (print "Something else
           is rotten.")))
  ;;; Prints "He has failed again!"

The /condition-property-accessor/ allows us to inspect the exception
a bit more closely if we know what we are looking for:

   (let ((exn (make-property-condition 'all-my-fault 'message "I
   have failed, master")))
       (if ((condition-predicate 'all-my-fault) exn)
           (print ((condition-property-accessor 'all-my-fault
           'message "No message.") exn)) (print "Something else is
           rotten.")))
  ;;; Prints "I have failed, master!"

Note that the /"No message"/ argument is an extension to the SRFI-12
procedure. It will serve as a default in case your exception does
not provide the property you've asked for. If you omit the default
and the property is not available an error will be raised (yes,
another exception).

An easier form of /condition-property-accessor/ is /get-condition-property/
which corresponds to it like this:

   (define (get-condition-property EXN KIND PROPERTY [DEFAULT])
     ((condition-property-accessor KIND PROPERTY [DEFAULT]) EXN))

So it can save you a bit of typing parentheses.

To be able to generate more complex condition objects it is possible
to combine conditions to create so-called /composite-conditions/.
The constructing procedure is called

  (make-composite-condition condition ...)

and accepts property conditions objects and combines their kind
keys together in the order of the arguments. Properties are associated
with their conditions, meaning that more conditions can have the
same property names and those will not get mixed up. The Chicken
Scheme system provides these possible exceptions:

  - (exn arity): Signaled when a procedure is called with the wrong
  number of arguments.  - (exn type): Signaled on type-mismatch
  errors, for example when an argument of the wrong type is passed
  to a
                built-in procedure.
  - (exn arithmetic): Signaled on arithmetic errors, like division
  by zero.  - (exn i/o): Signaled on input/output errors.  - (exn
  i/o file): Signaled on file-related errors.  - (exn i/o net):
  Signaled on network errors.  - (exn bounds): Signaled on errors
  caused by accessing non-existent elements of a collection.  -
  (exn runtime): Signaled on low-level runtime-system error-situations.
  - (exn runtime limit): Signaled when an internal limit is exceeded
  (like running out of memory).  - (exn match): Signaled on errors
  raised by failed matches (see the section on match).  - (exn
  syntax): Signaled on syntax errors.

These are also the system conditions raised by the Chicken Scheme
conditions system.

Now let's have a look what handlers look like.

=== Condition handling forms

The exception-handler that is in charge at the time of rise of the
exception is determined by the parameter

  (current-exception-handler procedure)

which holds a procedure with unary arity expecting the condition
object as argument. SRFI-12, the enhancement document to R5RS that
Chicken Scheme took its condition system from suggests three forms
that can implement condition handling.  From lower to higher level
they are:

  (with-exception-handler handler thunk)

Sets the /current-exception-handler/ to /handler/ for the invocation
of /thunk/ and resets it afterwards. _Warning:_ This procedure is
very low level and should not be used in most cases, as you can
create infinite loops with it.

  (handle-exceptions var handle-expression expr1 expr2 ...)

This macro calls all the expressions with a special exception handler
installed:

  ((call-with-current-continuation
     (lambda (k)
       (with-exception-handler
         (lambda (var)
           (k (lambda ()
                handle-expression)))
         (lambda ()
           (call-with-values
             (lambda() expr1....) (lambda args
               (k (lambda () (apply values args))))))))))

This is a full expansion of the /handle-exceptions/ macro. It
transforms your code into a ((call/cc ..)) block, which will cause
evaluation of itself later. As you can see it fetches the current
continuation of the macro first to continue where it left off. Then
it builds the exception handling function which binds the condition
object to the name /var/ as specified in the macro and calls the
continuation /k/ with the result of /handle-expression/.

The expressions are called in order, returning the result of the
last expression.

  (condition-case expression clause ...)

The most high level of the three. It allows running /expression/
(a thunk) and then deal with the condition objects in a case like
manner. Each clause has the form

  ([variable] (kind1 kind2 ...) BODY ...)

Where the optional /variable/ provides a binding to the condition
object in /BODY/. The kind clause is matched against the kind key
of the condition object. So to simply catch /exn/ conditions have
a clause /((exn) (display "Oooh an exn condition!"))/, to filter
out a file exception use /((exn file) (display "I see, something's
wrong with a file!"))/.  The clauses are, like cond, matched in the
order of appearance so be sure to place more special clauses first,
general clauses next and an catch-all /exn ()/ clause (optionally)
last. If the catch all clause is omitted the exception is re-raised
by condition-case.

Because condition-case captures the continuation just as
'handle-exception' does, re-raising the condition causes this
continuation's exception handler to get called, so that the unhandled
condition is handed over to the next handler. If no handler does
handle the exception, the toplevel handler will finally abort the
program and prints a call chain as well as the exn properties OR
an unhandled exception message.

=== How to summon the beast

Now that we know how to handle exceptions we would like to raise
them ourselves. There are two basic procedures that raise conditions,
/signal/ and /abort/. /Signal/ will be the most commonly used
procedure for raising an exception.  It's signature looks like

  (signal condition-object)

and it calls /(current-exception-handler)/ with /condition-object/.
The other procedure /abort/ does a little bit more than that: It
calls the /current-condition-handler/ with the /condition-object/
then when the handler returns it calls itself with a condition
object signaling that the handler returned. Why?

=== To continue or not continue -- That's the question

As I wrote above there are now two ways to handle the conditions
as they occur. The handler can decide to return any value it chooses
or it can jump off somewhere else for example back to the top level
of the REPL printing a call chain and the error message. This
behavior is implemented in the primordial condition handler as it
is installed by the Chicken Scheme system from the start.

Depending on the application it might be also sensible to continue
the computation with a corrected value instead of halting the process
altogether.

This different treatment after a condition is raised distinguishes
/continuable/ conditions (that continue in the handler) from
/non-continuable/ conditions that have to do something else, usually
aborting the current computation.

So with this distinction in mind it becomes clear that /signal/
should be used for raising continuable exceptions whereas /abort/
should be used to raise non-continuable exceptions.

=== Making continuable exceptions

However one needs to be careful with constructing continuable
exceptions for a couple of reasons:

You need to do it yourself. Since the higher level constructs
handle-exceptions as well as condition-case will always jump to the
continuation where they have been called themselves, you will always
jump "out" of your desired expression.

Abort will call the handler again. Whenever in your code some system
exception is raised with /abort/ you will end up in an infinite
loop. This code for example does loop infinitely: /(with-exception-handler
(lambda (e) 1) (lambda () (+ 1 (/ 1 0))))/ Despite the author's
intention to just carry on with a bogus result this will call abort,
which then will call the /(current-exception-handler)/ again as we
have seen.

A cure for this would be to check if it is an exception you are
expecting (from your own code) or an exception raised by someone
else which could mean jumping out to the old handler:

  (define (handle-input i)
    (if (even? i)
        (signal (make-property-condition
               'my-condition 'message "Wrong input" 'arguments (list
               i)))
        i))

  (define (test l)
    (let ((old-handler (current-exception-handler)))
      (with-exception-handler
       (lambda (exn)
         (cond (((condition-predicate 'my-condition) exn)
              (printf "called.~%~!") #f)
             (else (old-handler exn))))
       (lambda () (map handle-input l)))))

The results are

  #;1> (test '(1 2 3 4)) called.  called.  (1 #f 3 #f) #;2> (test
  '(1 2 y a)) called.

  Error: (even?) bad argument type: y

  Call history:

    <eval>    [test] (map handle-input l)
    <eval>    [handle-input] (even? i)
    <eval>    [handle-input] (even? i)
    <eval>    [handle-input] (signal (make-property-condition (quote 
my-condition) ...
    <eval>    [handle-input] (make-property-condition (quote my-condition) ...
    <eval>    [handle-input] (list i)
    <eval>    [test] ((condition-predicate (quote my-condition)) exn)
    <eval>    [test] (condition-predicate (quote my-condition))
    <eval>    [test] (printf "called.~%~!")
    <eval>    [handle-input] (even? i)      <--

=== Non-continuable exceptions or business as usual

Non-continuable exceptions really are easier to understand due to
their 1:1 mapping to other languages with exception systems. They
come close to the Java /try {} catch (Exception e){}/ block.

>From the lessons learned above, always use /condition-case/ if you
just want to handle your exceptional state.

An example with handle-exceptions (from the SRFI-12 docs):

  (define (try-car v)
   (let ((orig (current-exception-handler)))
     (with-exception-handler
      (lambda (exn)
        (orig (make-composite-condition (make-property-condition
         'not-a-pair 'value v)
        exn)))
      (lambda () (car v)))))

  (try-car '(1)) ;=> 1

  (handle-exceptions exn
            (if ((condition-predicate 'not-a-pair) exn)
                (begin
                 (display "Not a pair: ") (display
                  ((condition-property-accessor 'not-a-pair 'value)
                  exn))
                 (newline))
                (ABORT exn))
    (try-car 0))
  ; displays "Not a pair: 0"


And with /condition-case/:

  (define (check thunk)
    (condition-case (thunk)
      [(exn file) (print "file error")] [(exn) (print "other error")]
      [var () (print "something else")] ) )

  (check (lambda () (open-input-file "")))   ; -> "file error"
  (check (lambda () some-unbound-variable))  ; -> "othererror"
  (check (lambda () (signal 99)))            ; -> "something else"

  (condition-case some-unbound-variable
    ((exn file) (print "ignored")) )      ; -> signals error

In my opinion condition-case makes it easier for 90% of your
programming needs to catch explicitly certain types of exceptions
in your code. If you need continuable exceptions you may resort to
a more manual approach as outlined above.

=== Conclusion

I hope I have been able to shed some light on the usage of exceptions
in Chicken Scheme. I am grateful for any kind of comments and
criticism to this piece. In the long term a merge of the results
of this experimental article and the SRFI-12 docs in Chicken could
benefit all.

I am also thankful to Alaric, Felix and Peter who have read through
drafts of this article.

So long, may your code be exceptionally well guarded from now on.

== 5. About the Chicken Gazette

The Gazette is produced bi-weekly by a volunteer from the Chicken
community. The latest issue can be found at http://gazette.call-cc.org
or you can follow it in your feed reader at
http://gazette.call-cc.org/feed.atom. If you'd like to write an
issue, consult the wiki (http://wiki.call-cc.org/gazette) for the
schedule and instructions!

[ --- End of this issue --- ]





reply via email to

[Prev in Thread] Current Thread [Next in Thread]