I have released a new version of Seed7: seed7_05_20070507.tgz
In the Seed7 programming language new statements and operators
can be declared easily. Types are first class objects and therefore
templates/generics need no special syntax. Object orientation is
used when it brings advantages and not in places when other
solutions are more obvious.
Seed7 is covered by the GPL (and LGPL for the Seed7 runtime library).
Changelog:
- As suggested by several users the include files were moved to the
'lib' directory.
- The search for include files was improved to work with an internal
search path array.
- The 'hi' interpreter now contains a hardcoded include directory
path defined in version.h.
- The makefiles mk_mingw.mak, mk_msys.mak and mk_nmake.mak were
added for compilation under MinGW.
- The bigInteger multiplication was changed to use Karatsuba
multiplication for bigger integers.
- Several constant types were defined in common.h.
Greetings Thomas Mertes
Seed7 Homepage: http://seed7.sourceforge.net
Project page: http://sourceforge.net/projects/seed7
- Curtis
Also, hello everyone, I'm back from my programming hiatus.
Unfortunately, I don't have as much free time as I did last summer to
converse here, mostly due to school, but also my parents decided that
I was addicted to "the computer," so my computer access is somewhat
restricted.
I decided aggainst the use of curly brackets because I
wanted to use a different logic than C for the 'if' and
'switch' statements (Always brackets and no breaks,
see FAQ). Instead of using curly brackets and not using
C like statements, I decided on keyword statements.
The expression 'func ... end func' is an expression which
is used to represent a procedure body.
Declarations of the form 'const aType: aName is aValue'
were used because I wanted one declaration mechanism
for everything (constants, variables, types, functions and
statements):
const integer: MAX_SIZE is 5;
const type: myType is string;
const proc: main is func
writeln("hello");
end func;
var char: command is ' ';
The decision to use 'is' in declarations instead of := was
because the initialisation of a constant or variable is not
the same as an assignment to a variable (In an assignment
the variable already has a valid value before the assignment
takes place). The symbol = was not used because it has
already the meaning of 'equal'. Additionally: Keywords in
(the middle of) statments (like 'then', 'do', 'range', 'to'
or 'is') cannot be operator symbols (like := or = ). This
restriction is on purpose and makes reading of statements
(for humans and computers) easier.
> 2) why you think macros replace the need for iterators.
Macros do not replace the need for iterators. Seed7 allows
you to declare new statements. Declaring a statement is
like declaring a function and not like declaring a Macro.
The interpreter treats statement calls like function calls.
Some 'for' statements are expanded inline by the compiler
(comp.sd7), but the logic is still that of a function call.
What I think is:
Since it is possible to declare 'for' statements for every type
(where it makes sense) these 'for' statements can be used
instead of an iterator. And declaring a 'for' statement is
IMHO easier than declaring an iterator.
The declaration of the 'for' statement for arrays (as declared
in "seed7_05.s7i") is as follows:
const proc: for (inout baseType: vari) range (in arrayType: arr) do
(in proc: statements)
end for is func
local
var integer: number is 0;
begin
for number range 1 to length(arr) do
vari := arr[number];
statements;
end for;
end func;
As you can see the declaration of this 'for' statement is
straight forward. The logic is obvious since it contains
just a simple loop. A 'for' loop which works on a more
complex data structure could contain several loops or
call itself recursively. But the logic would still be obvious.
Since iterators are called for every element they have
the problem of saving the current state. An array iterator
contains a pointer to the current array element. For more
complex data structures the state of the iterator becomes
also more complex. The logic to decide which is the next
state can also become complex. IMHO an iterator has some
sort of inverse logic compared to the logic used in a
'for' statement.
Conclusion:
You do not need iterators (IMHO most of the time)
because you have a more powerful feature: declaring
a 'for' statement. The programming languages which
introduced the iterator concept did not have the
possibility to declare statements.
After reading through what you wrote, your examples look pretty
unique. I should admit, I only browsed through the first few examples
on your website before becoming bored, although I later revisited them
after seeing the examples in your message... Maybe you should show the
more complicated stuff first ;-)
Just a few questions:
> > 2) why you think macros replace the need for iterators.
> Macros do not replace the need for iterators. Seed7 allows
> you to declare new statements. Declaring a statement is
> like declaring a function and not like declaring a Macro.
> The interpreter treats statement calls like function calls.
> Some 'for' statements are expanded inline by the compiler
> (comp.sd7), but the logic is still that of a function call.
Oh, I see. I have a few questions on how this is implemented, although
I'll group them together with the other questions down below.
> What I think is:
> Since it is possible to declare 'for' statements for every type
> (where it makes sense) these 'for' statements can be used
> instead of an iterator. And declaring a 'for' statement is
> IMHO easier than declaring an iterator.
>
> The declaration of the 'for' statement for arrays (as declared
> in "seed7_05.s7i") is as follows:
>
> const proc: for (inout baseType: vari) range (in arrayType: arr) do
> (in proc: statements)
> end for is func
> local
> var integer: number is 0;
> begin
> for number range 1 to length(arr) do
> vari := arr[number];
> statements;
> end for;
> end func;
What an interesting code sample. Based on your use of the word
'function' to describe this, I would have to assume 'proc' is a
closure type and vari is the variable the current value should be
bound to, correct? Indeed, I definitely prefer this method over the
use of macros, which are extremely overused in languages like CL,
especially in situations like this. I rather like the ability to
define the syntax of the function call--I'm actually implementing
something similar in my made-up language. The concept is the same,
although the implementation is fundamentally different.
Anyway, I digress. How exactly do you parse such a definition? The
parameters are obviously in parenthesis, and the syntax definition
appears to end at the 'is func' part, but does that mean you can't use
the keyword 'is' between parameters? Also, It appears from this
sample, and from others I've examined, that all local variables need
to be declared at the top of the block, correct? Why exactly did you
implement such an old construct? I can't imagine it would be for ease
of parsing, unless the syntax interferes with other constructs.
> Conclusion:
> You do not need iterators (IMHO most of the time)
> because you have a more powerful feature: declaring
> a 'for' statement. The programming languages which
> introduced the iterator concept did not have the
> possibility to declare statements.
Perhaps, although the nice thing about iterators is that they separate
the mechanics of iterating from the algorithm which your using to do
so. This can be particularly convenient when one considers such
constructs as lazy lists (aka generators in python), and you also
don't have to write each and every different looping structure over
again if you create a new iteratable type. Having iterators in a
language which doesn't generally make control structures member
functions, which would be basically everything except for Ruby and
Smalltalk, adds a lot of abstraction that wouldn't otherwise be there.
As far as I can tell, in your language I couldn't iterate over a
random variable without either importing the correct module or
accepting the 'for' function as an argument. the former is a big no-no
in generic library functions, and the latter is somewhat tedious and
unwieldy.
BTW. I sended the following answer 24 hours ago...
> After reading through what you wrote, your examples look pretty
> unique. I should admit, I only browsed through the first few examples
> on your website before becoming bored, although I later revisited them
> after seeing the examples in your message... Maybe you should show the
> more complicated stuff first ;-)
"Declare a statement" is point 2.5 in the manual/tutorial. May be I
should do something there. The third example of the examples
section (at the homepage) is: Declare a statement. And there is a
link from the index page to this example.
> > The declaration of the 'for' statement for arrays (as declared
> > in "seed7_05.s7i") is as follows:
>
> > const proc: for (inout baseType: vari) range (in arrayType: arr) do
> > (in proc: statements)
> > end for is func
> > local
> > var integer: number is 0;
> > begin
> > for number range 1 to length(arr) do
> > vari := arr[number];
> > statements;
> > end for;
> > end func;
>
> What an interesting code sample. Based on your use of the word
> 'function' to describe this, I would have to assume 'proc' is a
> closure type and vari is the variable the current value should be
> bound to, correct?
Yes
> Indeed, I definitely prefer this method over the
> use of macros, which are extremely overused in languages like CL,
> especially in situations like this. I rather like the ability to
> define the syntax of the function call
Thank you
> --I'm actually implementing
> something similar in my made-up language. The concept is the same,
> although the implementation is fundamentally different.
>
> Anyway, I digress. How exactly do you parse such a definition?
Basically I use a tree driven LL(1) parser.
> The parameters are obviously in parenthesis, and the syntax definition
> appears to end at the 'is func' part, but does that mean you can't use
> the keyword 'is' between parameters?
Here you are missing something. The example above is a
semantic definition. There is a syntax declaration of the for loop
in the file syntax.s7i which looks like follows:
$ syntax expr: .for.().range.().do.().end.for is -> 25;
This syntax declaration specifies the keywords, the places of the
parameters, a priority and an assoziativity for the 'for' statement.
There are are several syntax declarations for 'for' statements.
As you can see there is a special dot notation used in the syntax
declaration. Therefore it is possible that statements contain the
keyword 'is'. For example the statement which does the declaration
of constants:
$ syntax expr: .const.(). : .(expr).is.(expr) is -> 40;
> Also, It appears from this
> sample, and from others I've examined, that all local variables need
> to be declared at the top of the block, correct? Why exactly did you
> implement such an old construct?
At one side: There are reasons to have all declarations together
at one place. Readability and Maintainability can be better in such
cases.
OTOH: Seed7 declarations are just statements. Declarations just
happen to be executed at compile time while the other statements
are usually executed at runtime. The declaration statements change
the table of declared constructs (in the interpreter). Therefore they
need to be executed at compile time. A procedure declaration looks
like:
const proc: myName is func
local
# This statements are parsed and executed at compile time
# before the body (between 'begin' and 'end') is executed.
begin
# This statements are parsed at compile time but executed
# at runtime. This way declaration statements here would not
# make sense.
end func;
> I can't imagine it would be for ease
> of parsing, unless the syntax interferes with other constructs.
It is not because of ease of parsing. It is because of the logic
I chosed to do the processing (at compile time and at runtime).
> > Conclusion:
> > You do not need iterators (IMHO most of the time)
> > because you have a more powerful feature: declaring
> > a 'for' statement. The programming languages which
> > introduced the iterator concept did not have the
> > possibility to declare statements.
>
> Perhaps, although the nice thing about iterators is that they separate
> the mechanics of iterating from the algorithm which your using to do
> so.
Separating the mechanics of iterating from the algorithm is
a good reason for iterators. I am sure it is possible to declare
Iterators in Seed7. They would define an interface to the logic
behind. I just think that 'for' loops are a clearer way for most
of the stuff that iterators can do (when no interface is needed).
> This can be particularly convenient when one considers such
> constructs as lazy lists (aka generators in python), and you also
> don't have to write each and every different looping structure over
> again if you create a new iteratable type.
May be that some languages require you to write iterator types
for every new type, but in Seed7 it is not neccessary to write a
'for' statement for every new type. The "seed7_05.s7i" library
contains this declaration (I have ommited the downto loop here):
const proc: FOR_DECLS (in type: aType) is func
begin
const proc: for (inout aType: variable)
range (in aType: lower_limit)
to (in aType: upper_limit)
do (in proc: statements) end for is func
begin
variable := lower_limit;
if variable <= upper_limit then
statements;
while variable < upper_limit do
incr(variable);
statements;
end while;
end if;
end func;
end func;
This way it is easy to declare new for loops. If the the new
type hase some basic functionality Just write
FOR_DECLS(myType);
and you are finished.
> Having iterators in a
> language which doesn't generally make control structures member
> functions, which would be basically everything except for Ruby and
> Smalltalk, adds a lot of abstraction that wouldn't otherwise be there.
> As far as I can tell, in your language I couldn't iterate over a
> random variable
What do you mean with "iterating over a random variable"?
> > --I'm actually implementing
> > something similar in my made-up language. The concept is the same,
> > although the implementation is fundamentally different.
>
> > Anyway, I digress. How exactly do you parse such a definition?
>
> Basically I use a tree driven LL(1) parser.
>
> > The parameters are obviously in parenthesis, and the syntax definition
> > appears to end at the 'is func' part, but does that mean you can't use
> > the keyword 'is' between parameters?
>
> Here you are missing something. The example above is a
> semantic definition. There is a syntax declaration of the for loop
> in the file syntax.s7i which looks like follows:
>
> $ syntax expr: .for.().range.().do.().end.for is -> 25;
>
> This syntax declaration specifies the keywords, the places of the
> parameters, a priority and an assoziativity for the 'for' statement.
> There are are several syntax declarations for 'for' statements.
> As you can see there is a special dot notation used in the syntax
> declaration. Therefore it is possible that statements contain the
> keyword 'is'. For example the statement which does the declaration
> of constants:
>
> $ syntax expr: .const.(). : .(expr).is.(expr) is -> 40;
I see.
> > Also, It appears from this
> > sample, and from others I've examined, that all local variables need
> > to be declared at the top of the block, correct? Why exactly did you
> > implement such an old construct?
>
> At one side: There are reasons to have all declarations together
> at one place. Readability and Maintainability can be better in such
> cases.
Oh.
> > > Conclusion:
> > > You do not need iterators (IMHO most of the time)
> > > because you have a more powerful feature: declaring
> > > a 'for' statement. The programming languages which
> > > introduced the iterator concept did not have the
> > > possibility to declare statements.
>
> > Perhaps, although the nice thing about iterators is that they separate
> > the mechanics of iterating from the algorithm which your using to do
> > so.
>
> Separating the mechanics of iterating from the algorithm is
> a good reason for iterators. I am sure it is possible to declare
> Iterators in Seed7. They would define an interface to the logic
> behind. I just think that 'for' loops are a clearer way for most
> of the stuff that iterators can do (when no interface is needed).
I agree. I just feel that it's much more elegant to have the for loop
construct use the iterators, so that all iteration uses the same
interface, rather than disparate ones. For example, using iterators it
would be fairly easy to create a lazy list construct, which could
generate each element on demand.
> > This can be particularly convenient when one considers such
> > constructs as lazy lists (aka generators in python), and you also
> > don't have to write each and every different looping structure over
> > again if you create a new iteratable type.
>
> May be that some languages require you to write iterator types
> for every new type, but in Seed7 it is not neccessary to write a
> 'for' statement for every new type. The "seed7_05.s7i" library
> contains this declaration (I have ommited the downto loop here):
Actually, I meant different container types.
> > Having iterators in a
> > language which doesn't generally make control structures member
> > functions, which would be basically everything except for Ruby and
> > Smalltalk, adds a lot of abstraction that wouldn't otherwise be there.
> > As far as I can tell, in your language I couldn't iterate over a
> > random variable
>
> What do you mean with "iterating over a random variable"?
Well, when I say random variable, I mean a variable whose type may not
be known at compile time that comes from a module which isn't imported
in the current file. Of course, you could import the correct module to
solve the problem (by retrieving the correct 'for' declaration), but
this isn't always possible, e.g. in generic libraries. In this
example:
def apply(f, container):
for element in container:
f(element)
'container' would be the random variable, since its exact type is
unknown. Because this would be (conceivably) a library function, you
can't really rely on being able to import the correct module, either.
- Curtis
A for loop over the lines of a file (does not loop over a lazy list
but the elements are created on demand):
const proc: for (inout string: stri) range (inout file: myFile) do
(in proc: statements)
end for is func
begin
while not eof(myFile) do
stri := getln(myFile);
statements;
end for;
end func;
If you want iterators (for lazy lists or something else) you can
declare them in Seed7. I just have not declared them until now.
> > > This can be particularly convenient when one considers such
> > > constructs as lazy lists (aka generators in python), and you also
> > > don't have to write each and every different looping structure over
> > > again if you create a new iteratable type.
>
> > May be that some languages require you to write iterator types
> > for every new type, but in Seed7 it is not neccessary to write a
> > 'for' statement for every new type. The "seed7_05.s7i" library
> > contains this declaration (I have ommited the downto loop here):
>
> Actually, I meant different container types.
It was a design decision that you need to specify the element type of
a container. There are no such things as Java containers (before
generics where introduced) with elements of type 'Object' (and casts
to the correct type which can only be checked at runtime). I think it
is a big advantage when the compiler can check the element type of
a container. The following Seed7 types have 'string' elements.
array string
hash [time] string
See below how generic functions for this types are declared.
> > > Having iterators in a
> > > language which doesn't generally make control structures member
> > > functions, which would be basically everything except for Ruby and
> > > Smalltalk, adds a lot of abstraction that wouldn't otherwise be there.
> > > As far as I can tell, in your language I couldn't iterate over a
> > > random variable
>
> > What do you mean with "iterating over a random variable"?
>
> Well, when I say random variable, I mean a variable whose type may not
> be known at compile time that comes from a module which isn't imported
> in the current file.
It is a design decision that the type of every variable must be known
at compile time. If you want generic code you need to use templates
or use the OO mechanism (which is based on interfaces).
The Seed7 files can be viewed as some sort of iterators.
The type 'file' is an interface and contains several get functions
(getc, getwd, getln). The implementation of a 'file' can be
a NULL_FILE, an external_file, a screen_file, ... or some user
defined file type. If you want to write generic functions for
files you could just use the 'file' interface and iterate over
the 'file'.
> Of course, you could import the correct module to
> solve the problem (by retrieving the correct 'for' declaration), but
> this isn't always possible, e.g. in generic libraries. In this
> example:
>
> def apply(f, container):
> for element in container:
> f(element)
>
> 'container' would be the random variable, since its exact type is
> unknown. Because this would be (conceivably) a library function, you
> can't really rely on being able to import the correct module, either.
As I mentioned before Seed7 does not contain typeless variables.
Since your 'apply' function is needed for different container types
it could be implemented as a template (The following example
uses pseudocode for the function parameter f, since Seed7 has
currently no function parameters ... but they are planned):
const proc: CREATE_APPLY (in type: containerType) is func
begin
const proc: apply (
in proc: f (in base_type(containerType) param),
in containerType: container) is func
local
var base_type(containerType): element is
base_type(containerType).value;
begin
for element range container do
f(element);
end for;
end func;
end func;
Seed7 does not have implicitly instanciated templates (like in
C++). This is also a design decision like:
Variables must be declared.
There are no implicit type conversions.
There is no implicit instantiation of templates.
Therefore you need to instanciate the template explicit:
CREATE_APPLY(array string);
CREATE_APPLY(hash [time] string);
Although arrays and hashes are implemented totally different
the apply function can be called for them.
The other solution would be a OO one like the one I mentioned
before.
Oh, ok. Just making sure you knew :)
Thanks for your time,
Curtis