cocoon-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Ovidiu Predescu <ovi...@cup.hp.com>
Subject Re: [Schecoon] flow control layer
Date Wed, 27 Feb 2002 19:46:54 GMT
On Wed, 27 Feb 2002 12:25:30 +0100, Stefano Mazzocchi <stefano@apache.org> wrote:

> > > why isn't simple and friendly positional syntax good enough? (remember:
> > > I'm going to ask you to remove half of the things, and then half again,
> > > until everything that's there *has* to be there, I'll be your anti-FS
> > > nightmare :)
> > 
> > Fair enough ;-)
> > 
> > I guess I'm a little bit biased by languages like Python and Common
> > Lisp, where you can actually do this. It's a very nice feature and
> > very easy to implement. It also opens the road to functions with
> > variable number of arguments, something which I badly miss in Java.
> 
> Ok, you changed my mind. I've always missed the ability to have a
> variable collection of parameters to pass and I think it could be way
> cool to add it.
> 
> So, go on.

;-)

That's good.

> > > > The above things are working fine right now, although I don't have a
> > > > good example to demonstrate it. Since the flow would be written in
> > > > Scheme, I figured not many people would be interested in it. But
> > > > please let me know if there's interest to see a complete example using
> > > > the flow layer written in Scheme.
> > >
> > > Yes, I am. Definately. In order to provide a syntax, one has to
> > > understand the semantics. Without a detailed example, I think I might
> > > not be able to grasp the entire concepts that the syntax  should
> > > describe.
> > 
> > The more I think about it, the more I realize that writing a complete
> > sample in Scheme will scare the people away. So I think I'll postpone
> > this little exercise until I have some rudimentary implementation of
> > the flow language. I just hope to find the time to write the
> > translator...
> 
> Please, consider giving us more info so that you don't have to write a
> translator, we reject the syntax and you have to write another one.
> 
> Trust the power of open development and trust the open-mindness of the
> cocoon community.

I do trust the model, but sometime is hard to formulate what you want
to accomplish.

What I've been thinking about is a syntax which emulates the semantic
of the Scheme language as close as possible, yet provide a syntax, and
a tight integration with Java.

In Scheme there is no notion of statement, everything is an
expression. This makes possible all sorts of things, so I'm going to
follow this approach.

Scheme defines nested functions and lexical scoping. A free variable
in an inner function (aka a variable not declared in that function) is
bound in the lexical scope in which it appears, e.g. its definition is
looked up in the outer scopes. This mechanism is called closure, and
is a requirement for continuations as first class objects.

In Scheme identifiers can contain any symbol you like, except spaces,
because operators, which are really functions, always appear in the
first position in a list. So identifiers like call/cc, send-page,
clear+add are perfectly valid. To be able to call normal Scheme
functions from the flow language, we need to have the same
ability. However because operators in jWebFlow are going to be infix,
we need to require a space before them (see further why a space is not
required after it). Characters like '(' ')' ',' are not allowed in
identifiers. Special characters like '+' '-' '"' and ' are not allowed
at the beginning or end of an identifier. This allows for things like
'-a' 'a - b' 'a++', 'f(1)' to really mean what you intend. However
'a-b' is an identifier.

As I mentioned earlier functions also associate arguments by name. You
can call a function either by enumerating the expressions, or by
associating them with names. In the later case, you can pass fewer or
more arguments than are actually declared by the function:

 function my_func(a, b, c)
 {
   ...
 }

 // positional invocation:
 my_func(1, 2, 3);

 // by name invocation:
 my_func(a = 1, b = 2, c = 3);
 my_func(c = 3, a = 1, b = 2);
 my_func(a = 1);
 my_func(d = 4, c = 3, a = 1, b = 2);

By name invocation will allow invocation from the XML sitemap without
having to remember what is the order in which the arguments have been
declared in the function:

  <map:call function="my_func">
    <map:parameter name="c" value="2"/>
    <map:parameter name="a" value="1"/>
    <map:parameter name="b" value="3"/>
  </map:call>

The actual values of the arguments in my_func are associated using
their actual name. Any additional arguments which are not declared by
the function, will be simply ignored.

Like in Lisp, Scheme, JavaScript, Smalltalk, Python, etc. variables
don't have types associated with them, only values have. Variables are
declared using 'var':

 function my_func(a, b, c)
 {
   var d = 1;
 }

Functions are first class objects, you can assign functions to
variables. There is the notion of anonymous function, a function
without a name:

 var f = function(a, b) { a + b; };
 f(1, 2);

The above declares f as a variable that holds a function which takes
two arguments. The function returns the sum of the two
arguments. Anonymous functions are the equivalent of lambda functions
in Scheme.

Blocks of code are declared with curly braces ('{' and '}'). A block
is really an sequential list of expressions. The value returned by the
block is the last expression in the block:

 function my_func(a, b)
 {
   var c = { if (a > b) a; else b; };
 }

The 'return' special form, takes an optional expression as argument,
and terminates the execution of the current block. The value of the
block becomes the value of the 'return' expression, if any, otherwise
void is returned (see below for an explanation of void).

 function my_func(a, b)
 {
   if (a == 0)
     return b;

    // Some lengthy computation here
    ...
 }

As Scheme the language has proper tail recursion. This means the
following code consumes no stack at runtime:

 function f (i, acc)
 {
   if (i == 0)
     acc;
   else
     f (i - 1, acc + i);
 }
 
 f (100000);

Built-in datatypes are numbers, arrays, dictionaries and function
objects. There is a special type 'void', that has a single possible
value named 'void'.

With numbers there is no distinction between int, float etc. as in
Java. Instead, as in Scheme, the language supports a tower of numeric
subtypes, which allows arbitrarily large numbers, either exact or
inexact, in a transparent manner.

An array is declared using square brackets ('[' and ']'):

 var array = [1, 2, "abc"];

A dictionary is defined using comma in an expression context:

 var dict = {"a" = 1, "b" = 2};

As in Scheme and JavaScript, dynamic code can be created and executed
at runtime using the 'eval' function:

 eval("1 + 2");

parses and evaluates the "1 + 2" expression. Eval makes the parser and
evaluator available in the language.

The interface with Java is straightforward. You can write:

 import java.io.*;

 var a = new InputStream(new File("/tmp/a"));

The 'import' statement is a special expression which defines, in the
current scope, a set of variables with the same name as the imported
classes. E.g. this means that the scope of an imported class name is
valid only in the scope in which it was defined:

  {
    import org.MyClass;

    var a = new MyClass(); // refers to org.MyClass
  }
  new MyClass(); => illegal, MyClass not defined

  import com.MyClass;
  var b = new MyClass; // refers to com.MyClass;

Handling Java exceptions is similar with Java:

 try {
   a.write("abc");
 }
 catch(IOException e) {
   System.out.println("exception caught: " + e);
   throw new RuntimeException(e);
 }

The lexical scoping, of which I've talked about earlier, allows things
like this:

 function my_func(a, b)
 {
   function(c)
   {
     return a + b + c;
   }
 }

 var f1 = my_func(1, 2);
 var f2 = my_func(2, 3);

 f1(3); // returns 6
 f2(4); // returns 9

my_func is a function which returns a function object. Inside the
returned function object, the values of a and b are bound to the ones
at the time of my_func invocation. For example in the f1 case, the a
and b values are bound to 1 and 2 respectively. This is what is called
'closure' in functional languages.

I guess I can go on and on, but I'll stop here. Please let me know if
I forgot to mention something which you consider is important.

Right now I'm trying to decide on a way to implement the translator,
which also allows me to introduce support for macros at a later
point. Macros would be an advanced feature of the language, which
allows one to introduce new syntaces for the language, essentially
extending the language syntax at runtime. This is one of the very
powerful features of Scheme and Lisp languages in
general. Particularly Scheme has a very powerful way of defining
macros, and I'd like to have this in the language as well. In fact, if
you look at Scheme, the core language has very few built-in
constructs, and everything else is defined in terms of these using
macros.

While programming in Python some years ago, I always missed the
ability to define my own syntax. The Python parser is implemented very
nicely, which makes you think you can extend the syntax, or define new
mini-languages by using Python's parser. Unfortunately this is not
possible, as Python doesn't expose the parser at the language level.

OK, I'm not going to talk more about this feature, as I think Stefano
will quickly categorize it in the "flexibility syndrom" category ;-)
I'll see instead if I can implement the translator in a way which
leaves the doors open.

Greetings,
-- 
Ovidiu Predescu <ovidiu@cup.hp.com>
http://www.geocities.com/SiliconValley/Monitor/7464/ (GNU, Emacs, other stuff)

---------------------------------------------------------------------
To unsubscribe, e-mail: cocoon-dev-unsubscribe@xml.apache.org
For additional commands, email: cocoon-dev-help@xml.apache.org


Mime
View raw message