Why are you doing that?
What is XL good for?
Why would I want to use it?
Why does it look like Pascal?
See this page for an in-depth discussion of these questions.
XL is too verbose. Why so many keywords?
This comment is very frequent. But in reality, XL has about half
the number of keywords of C++ for the same expressive power. XL is
also quite often shorter than C or C++, in particular for non-trivial
pieces of code. The following listing shows this on a typical piece of
code, and this matches experience porting more significant pieces of
code.
// C++: 12 lines, 261 characters
Item *FindItem(Item *first, Item *last, const char *name) {
Item *ptr;
for (ptr = first; ptr; ptr = ptr->next) {
if (!strcmp(ptr->name, name))
return ptr;
if (ptr == last)
break;
}
if (ptr == last && ptr->next)
return ptr->next;
return NULL;
}
|
// XL: 7 lines, 220 characters
function FindItem(Item first, last; string name) return Item is
with Item ptr
for ptr in first..last loop
if ptr.name = name then
return ptr
if ptr = last and ptr.next <> NULL then
return ptr.next
return NULL
|
Besides the problem of dealing with an unfamiliar syntax, the real
issue people express with these comments, I believe, are related to
the cases where the XL syntax is significantly more verbose, in
particular when this syntax happens to be often used in C and
C++. This is essentially true for complex object declarations
(functions, arrays, pointers), which are in general more verbose in XL
than in C or C++.
The justification for introducing keywords is that the C and C++
syntax is ambiguous. This is well known and largely documented. In C
and C++, which are closed languages, this is acceptable (although in
C++, it quite often causes obscure programming errors). In XL, which
is designed for extensions, this is not acceptable. The declaration
syntax in XL allows the compiler to know what it is parsing, even if
something like array A[1..10] of integer has been completely
defined by a library. Also note that the notation is largely under
control of the user: array A[10,integer] is also acceptable.
In practice, this verbosity is not a problem, for reasons that
depend on the types being discussed:
- Pointer types are used extremely infrequently in XL compared to C
and C++, as the example above illustrates.
- Specifying arrays with fixed size and type is also relatively
infrequent. If a type such as array [1..10] of integer is
used often, it is easy enough to give it a name.
- A type like array being generic is often used in generic
algorithms. In that case, the XL notation is shorter:
procedure Sort(in out array A)
template<class T, int N> void Sort(T A[N]);
- Function declarations are easily searched for, something done
often in practice by programmers. To search for function foo in
XL, you search for "function foo". In C++, it is much more difficult.
- Function declarations for non simplistic code are also not
systematically shorter in XL than in C or C++:
module BANK_ACCOUNT body is
procedure Add(Client c) is ...
procedure Remove(Client c) is ...
#include "bank_account.h"
void BANK_ACCOUNT::Add(const Client &c) { ... }
void BANK_ACCOUNT::Remove(const Client &c) { ... }
Is XL strongly typed or not?
Yes, XL is strongly typed. However, it also has a root type, thing. More precisely, the base hierarchy is:
- thing, from which all entities derive (including subroutines or types)
- record, which derives from thing and is the base for all data objects
- module, a constant record type for building program modules
- object, a record type which can only be dynamically allocated. Anything deriving from objects behaves like Java reference objects.
Secondly, the any keyword allows the
creation of entities with dynamic type. An entity declared as any
object will receive any dynamic object. An entity declared as
any record will receive any object,
dynamic or stack-based. Finally, any thing can be used if you want to also be able to refer to types or subroutines.
// Adding debug code for anything
procedure Debug(any thing O) is
Write "unknown object"
procedure Debug(any integer X) is
Write X
Won't expression reduction be confusing?
What happens if you have both A+B, A*B and A+B*C?
How do you change operator priorities?
My experience so far did not allow me to find a case where expression reduction was confusing. It did allow me to find multiple cases where it improved significantly over C++ operator overloading.
Expression reduction selects the largest possible expression. If two candidates are ambiguous, the compiler warns you and picks one. You select the one you want using parentheses.
You cannot change operator priority. A+B*C is always parsed as A+(B*C). Of course, you can redefine a function that computes (A+B)*C for the above expression, but that wouldn't be smart. Named operators all share the lowest priority.
Where is the compiler? How advanced is it?
The compiler is under development. You can access it through the
Mozart CVS tree. You can refer to the status
page for details.
Why doesn't XL have 'classes'?
The type system in XL doesn't require a class keyword: class can be constructed. I have not made a final decision yet with respect to its implementation.
- I could have a class type that behaves like
record, but then people would try and write something like
class X with... and get a class object rather than a
class type.
- Alternatively, I could have class be a synonym for
type, which would be neat for Java or C++ users because they
could write class Glop is... and get what they expect. But I
don't really want to reserve a useful name such as 'class' for a
keyword.
- Last, my current preferred alternative, is to make class
a built-in type with reflective behavior. In that case, you would be
able to write class X is... and get the behavior you expect
(a type definition, not an object definition), yet class
would not be a keyword. In addition, the reflective behavior could
automatically create C++/Java style shortcut notations for "member function" calls
(i.e. you would be able to write a.f() rather than f(a))
So the reason class doesn't appear is because I did not
make my mind yet, but it's likely to be a "library implementation
detail" ultimately, although an important one. What matter is that the
current syntax supports using class without requiring a
modification of the compiler or language.
Why no private keyword? Information hiding is important!
Information hiding is important. But hiding the information is the
best way to practice it :-) In XL, private implementation details are
really hidden, they are not in the source code. In C++ or Ada, by
contrast, they are not hidden but forbidden (using a private
keyword). The XL approach allows you to use all the scoping rules to
perform information hiding.
For instance, the following is a C++ class. Many developers would
walk away from a class that looks like this, assuming incorrectly that
it can store only a few books, and saying "that's stupid". This shows
that our knowledge (or what we think we know) of the internal details
of the implementation influences if and how we use the class.
class Library
{
public:
void AddBook(Book *bk);
private:
Book *books[32];
int numbooks;
};
In contrast, the same interface in XL would look like the following.
No private information leaks out. The implementation is free to
intelligently hash book entries alphabetically without having users
making incorrect assumptions on the behavior of the class.
type Library
procedure AddBook(in out Library L; in Book b)
Does XL support functional programming?
The short answer is: yes. Functional programming is defined by three
essential characteristics.
- The value of a function depends only on its arguments
- Objects are immutable. Rather than modifying objects, one
creates new objects.
- Functions are first class objects: they can be used in
expressions, passed as arguments, part of a data structure, and
function literal can be created ("lambda functions")
XL does not always respect the first rule, and is therefore not a
pure functional language. Most functional languages are not different
in that respect: as long as you allow a user to enter data, the output
of the function that reads user input doesn't depend solely on its
inputs. However, in XL, the {functional} pragma can be used
to indicate that a given function has no side effects and that its
result only depends on the input arguments. In this case, the compiler
is allowed for instance to optimize f(1)+f(1) into 2 *
f(1).
The second aspect is not enforced by XL: XL does have pointers.
This is necessary to create data structures which are difficult to
create with languages that respect the rule, such as circular lists
or cyclic graphs. On the other hand, it is possible to write XL code
that completely respects the rule (this would be very difficult in C
or C++)
XL does qualify for the last aspect. Functions as objects are a
necessity for convenient use of reflection. See for instance how the
tasking example uses functional bodies
for the task objects. XL also has support for other
properties often associated with functional programming, such as
lists, garbage collection and weak typing.
However, it is important to understand that the functional
approach is often not the most efficient one in XL. For instance,
closures and lambda functions are often conveniently replaced with
generic code and functors in XL.
But functions are not first class objects
Yes, functions are indeed first class entities. They can be assigned to an any thing type, they can be created dynamically, they can be processed at compile-time using compiler plug-ins.
On the other hand, the compiler internal representation of a function as an
object (for reflection) is distinct from the representation of a
compiled function (a code pointer). The downside is that the structure of
A+B is not A-then-plus-then-B (matching the source code as it
would in Lisp), but rather a tree with + at the root and name
leafs. This surprises Lisp hackers, since the mapping between the source code and the internal compiler structure is much less obvious than in Lisp.
The upside is that the representation can contain additional
information (such as the structure of the generated code or
optimization annotations.) Also, it improves readability of the source code when you don't care about its reflective properties (which is most of the time). For most programmers, A*B+C is easier to read than (+ (* A B) C).
It's the fragile base class problem, not the
weak base class!
The two problem are subtly different, if related. It appears that
Daveed Vandevoorde and I may have created the terminology and I was
not aware of it. This is why I used it as if it was standard
terminology.
- The fragile base class problem is the issue that modifications in
the base class may break derived classes that use it. The base class
is fragile because changes in it break its role as a base
class. The fragile base class is essentially a maintenance
problem.
- The weak base class problem is the issue that a class cannot be
reused because it lacks necessary functionality, and said
functionality cannot be added without "opening" the class, which may
not be acceptable if the class is not owned (e.g. a library
class). The base class is weak because you can't build on top
of it. The weak base class is essentially a software reuse
problem.
Object-oriented languages with weak typing such as Objective-C or
Smalltalk don't generally have the weak base class problem. XL solves
the problem while preserving (not mandating) strong typing. XL also
helps solving the fragile base class problem by allowing better
encapsulation, but it doesn't totally eliminate it.
|