Wednesday, February 27, 2013

Looking to learn something new? Try Rebol

Rebol is an odd language with an odd history.  It was originally put together in 1997 as an answer to the many things that plague modern programmers.  The goal was to create a language construction set using what we've learned from all the crazy trial-and-error in other languages.  For instance, have you ever wondered why we use curly braces?  In Rebol, square brackets are used (saving your shift key a lot of work [0]).

Alright, enough semantics, let's get into the real part of coding.  Download the Rebol binary.  While you're waiting, notice that you're not waiting, because the binaries are only half a megabyte.  This gets really impressive when you realize just how much is in Rebol.  Anyway, don't forget to chmod +x if you're on a good operating system.

So let's run through a basic "Hello World."  Step one:

>> print "Hello World!"

Well, that was boring.  How's about instead of going through the same boring syntactical tutorials, we check out the constructing power of Rebol and write helloworld in our own language?  Alright, now we're cooking.

To do this, I'll need to get a bit academic for a second (sorry, it'll be quick).  Everything you punch into the interactive shell (also known as REPL) is evaluated in Rebol's DO dialect.  In Rebol, a dialect consists of a set of instructions to work with a given input [1].  Let's take a look at the PARSE dialect, which takes the following format:

parse input rules /all /case

Let's ignore /all and /case for now and focus on the cool part.  Let's make an incredibly basic language where "a" is syntax for sending the string "Hello " to system.out and "b" does the same, but with the string "World!\n".  We could implement something like this in JavaScript like so:


While it's only a couple lines, it's still very difficult to extend.  In Rebol, it looks something like this:

parse "abc" [any ["b" (print "world!") | "a" (prin "Hello ")]]

The first two bits are simple: parse enters the PARSE dialect and "abc" is the input we're passing in.  The rules section is what we're really after..  Let's break it down:

[any ;If the parser finds any of the following
  ["b" ;an instance of "b"
    (print "world!") ;Output "world!" with a newline
                     ;parens mean that this code block will be executed in the DO dialect
  | "a" (prin "Hello ")] ;Same as above, but using prin, which doesn't output a newline
]

There you have it, your own special language (an incredibly contrived one, to be sure, but one nonetheless).  However, that's nowhere near as powerful as PARSE gets.  Let's taken on an interesting problem: determining if a mathematical equation is valid.  Benjamin Gruenbaum provides the following example in JS:


As you can see, this task is much more difficult for JS to express. It's a tad harder in Rebol, but not nearly as much.  To simplify things, we're going to do some assignment (using a colon instead of the equals sign, example taken from the Rebol docs slightly modified to reflect the latest version of Rebol):

expr:    [term ["+" | "-"] expr | term]
term:    [factor ["*" | "/"] term | factor]
factor:  [primary "**" factor | primary]
primary: [some digit | "(" expr ")"]
digit:   charset "0123456789"

The important thing to notice here is the recursive nature of the instructions.  "expr" is the top-level one you'll pass to the parse command:

parse "1+2*(3-2)/4" expr
== true
parse trim/all "1 + 2 * ( 3 - 2 ) / 4" expr ;use "trim/all" to remove whitespace
== true

There's much more in Rebol, so come to our our chat room and find out!  (This is a StackExchange chat, so you'll need 20 reputation on StackOverflow to talk).

[0] US keyboard only (sorry!)
[1] It's like a function, only more powerful.  Every expression runs in a dialect.  You can read about dialects in the docs or Wikipedia.

3 comments:

  1. The `parse` function just looks like a single function you'd have to program once in JS to have the same behavior.

    I don't think a single function is useful to show a programming language's strengths.

    ReplyDelete
  2. Hi Florian,

    I think Rebol's parse is so comprehensive and performant that it's not very useful to just say it "looks like a single function you'd have to program once in JS". That's kind of like saying creating a JavaScript VM is no big deal - you can write one in JavaScript (see Continuum at http://benvie.github.com/continuum).

    First, that doesn't say anything about how complex an endeavor that would be, but it's possible since, in the end, JS is a Turing-complete language.

    Second, what performance (and memory consumption for a given task) would you get with this JS implementation? Rebol's parse uses highly-tuned C code to accomplish its neat 'tricks'. Sure, for a simple parser needed for a calculator, what's it matter? The reality is that parse is suitable for a very wide range of tasks.

    A more interesting thing which could be said about parse is that it is actually the very thing to use when creating a language parser for a full-featured language like, hmmm, JavaScript.

    Parse really is one of Rebol's 'killer features'. If all you see there is a quick function that you could hack up in JS, maybe you haven't looked closely enough.

    Cheers,
    Adrian

    ReplyDelete
  3. Loved how you wrote this post.

    So minimal, but so much. And you make me want to forget the little Rebol that I know, and then accident;y land on this page and then get to Rebol.

    And I am too much of a newbie to say anything else, but being a newbie, the way you broke the single line example in the first case to multiple lines and with explaining comments was great from a newbie perspective.

    -- KK. from the SO room.

    ReplyDelete