Common Lisp conditions

Discussion in 'Ruby' started by Gavin Sinclair, Feb 23, 2009.

  1. Hi,

    So far as the search engine on this newsgroup is concerned, there's
    been no discussion about including a Common Lisp-style condition
    system in a future version of Ruby. For information about these, [1]
    is a good readable resource.

    For those who don't know what they offer above normal exception-
    handling (as we know it in Ruby, Java, C++, ...), conditions need not
    be errors and they need not unwind the stack. A low-level function
    can signal that something is wrong, and provide multiple ways of
    handling it (which is appropriate, because the handling code is low-
    level). A high-level function can receive the signal and choose which
    low-level handler to run, or can "handle" it itself or ignore it (the
    latter two options being what's available in Ruby etc.). Another good
    example is low-level code issuing a warning and higher-level code
    deciding whether to print it. It's _very_ elegant.

    For those who _do_ know about Common Lisp conditions, is there any
    appetite for them in Ruby? I see great positives and no negatives.
    They don't seem at all un-Ruby-like; in fact the relationship between
    high- and low-level code reminds me of the elegance of blocks and
    yield.

    After a small amount of thought, I haven't come up with a suitable
    syntax. That's probably because I haven't used the things, only read
    about them.

    Just curious about people's thoughts, and quietly hopeful for Ruby
    2.0 :)

    Gavin

    [1] http://gigamonkeys.com/book/beyond-exception-handling-conditions-and-restarts.html
     
    Gavin Sinclair, Feb 23, 2009
    #1
    1. Advertisements

  2. They seem to work very well in Ola Bini's new language Ioke
    (<http://Ioke.Org/>), which borrows heavily from Ruby. So, I agree:
    they would fit very well. Replacing Exceptions with Conditions could
    probably even be done in a (somewhat) backwards-compatible way. Maybe
    they could replace throw/catch, too.
    I just tried converting the example from Practical Common Lisp, but it
    isn't too pretty. I hope that's because I mis-translated the original
    procedural Common Lisp into pseudo-OO Ruby and not because my chosen
    syntax is ugly.

    class MalformedLogEntryError < RuntimeError
    def initialize text
    @text = text
    end
    attr_reader :text
    end

    class LogEntryParser
    def parse_log_entry text
    error MalformedLogEntryError.new(text) unless text.wellformed_log_entry?
    LogEntry.new( ... )
    restart use_value(value)
    value
    restart reparse_entry(fixed_text)
    parse_log_entry fixed_text
    end
    end

    class LogfileParser
    def parse_log_file file
    File.each_line(file).map do |line|
    begin
    @log_entry_parser.parse_log_entry text
    restart skip_log_entry
    nil
    end
    end
    end
    end

    class LogAnalyzer
    def main
    find_all_logs.each do |log|
    analyze_log log
    end
    handle MalformedLogEntryError
    restart skip_log_entry
    end

    def analyze_log log
    @logfile_parser.parse_log_file(log).each do |entry|
    analyze_entry entry
    end
    end
    end

    class StrictLogAnalyzer # Annotates malformed entries instead of ignoring
    def main
    super
    handle MalformedLogEntryError => c
    MalformedLogEntry.new(c.text)
    end
    end

    jwm
     
    Jörg W Mittag, Feb 25, 2009
    #2
    1. Advertisements

  3. They seem to work very well in Ola Bini's new language Ioke
    (<http://Ioke.Org/>), which borrows heavily from Ruby. So, I agree:
    they would fit very well. Replacing Exceptions with Conditions could
    probably even be done in a (somewhat) backwards-compatible way. Maybe
    they could replace throw/catch, too.
    [Supersede: my original article had an example translation of the code
    example from the book Practical Common Lisp, but my translation was,
    for lack of a better word, bullshit. So I deleted it.]

    jwm
     
    Jörg W Mittag, Feb 25, 2009
    #3
  4. Exceptions don't need to be errors in Ruby, though conventionally they
    are used for that and throw/catch for similar non-errors. As far as
    the additional functionality of conditions, its not that hard to
    implement at least what I see as the most important one (restarts) on
    top of Ruby's existing exception-handling using continuations, e.g.,
    in Ruby 1.8 (and, I would presume, Ruby 1.9 with the addition of
    "require 'continuation'" at the top:

    # This code is untested on any but the demo case at the end.

    class Condition < Exception

    FAIL = Object.new

    def self.alert(*args)
    block = (block_given? ? lambda { |c| yield c } : nil)
    callcc { |cont| raise self.new(cont, block, *args) }
    end

    def initialize(c, block, *args)
    @continuation = c
    @restarts = {}
    block.call(self) if block
    end

    def add_restart(restart_name, &block)
    @restarts[restart_name] = lambda do |*args|
    result=block.call(self, *args)
    @continuation.call(*result) unless result==FAIL
    nil
    end
    self
    end

    def restarts
    @restarts.dup
    end

    def restart(restart_name, *args)
    @restarts[restart_name].call(*args) if @restarts.include? restart_name
    end
    end

    class BadValueCondition < Condition
    attr_reader :value

    def initialize(c, block, val, *args)
    @value = val
    super
    end
    end

    def process_value(val)
    if (val.div 2) == (val.quo 2)
    BadValueCondition.alert(val)
    else
    val.to_s
    end
    end

    def process(range)
    range.map { |val| process_value(val) }.compact
    rescue BadValueCondition => c
    c.add_restart:)ignore) {}
    c.add_restart:)substitute_warning) { |c, *args| "Bad: #{c.value}" }
    raise
    end


    [:ignore, :substitute_warning].each do |strategy|
    begin
    puts "\n---\nDemonstrating restart #{strategy.inspect}"
    arr = process(1..10)
    puts arr.join("\n")
    rescue BadValueCondition => c
    c.restart(strategy)
    raise
    end
    end
     
    Christopher Dicely, Feb 26, 2009
    #4
  5. Gavin Sinclair

    GS Guest

    That's very impressive and interesting, Christopher. Thanks!

    Gavin
     
    GS, Feb 27, 2009
    #5
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.