LX Frequently Asked Questions
Christophe de Dinechin
Version 1.10 (updated 2001/10/25 03:27:45)
>>>>>>> 1.9
This comment is very frequent. But in reality, LX has about half the number of keywords of C++ for the same expressive power. LX is also quite often shorter than C or C++, in particular for non-trivial pieces of code. The following table shows this on a typical piece of code, and this matches experience porting more significant pieces of code.
// 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; } |
-- 9 lines, 241 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 exit if ptr = last 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 LX 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 LX 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 LX, which is designed for extensions, this is not acceptable. The declaration syntax in LX 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:
procedure Sort(in out array A) template<class T, int N> void Sort(T A[N]);
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) { ... }
-- Adding debug code for anything procedure Debug(any thing O) is Write "unknown object" procedure Debug(any integer X) is Write X
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.
The compiler is under development. You can access it through the
Mozart CVS tree. You can refer to the status
page for details. The only types defined by keywords in LX are the type
type, and the procedure / function types. In
addition to this, there is a root built-in type that the compiler
knows about (object) and a few types that the compiler
uses for built-in constants (integer, real,
string, character.) All these types are otherwise
not different than normal types to the compiler. In particular,
integer is not a keyword, it is a type defined in the
implicitly imported module LX.BUILT_IN. This module also
defines a few generic types, such as array or
pointer (don't know yet about list, knowing that
string is actually a dynamic vector of objects, with a
default type of character.)
Regarding class, I have not made a decision yet.
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. The convenience I see in
having a class type defined by the library is to
automatically enable short-cut notations for invokation of "class
member functions" (like a.f(), which implicitly passes a as an
argument to function f).
Information hiding is important. But hiding the information is the
best way to practice it :-) In LX, 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 LX 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.
In contrast, the same interface in LX 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.
The short answer is: yes. Functional programming is defined by three
essential characteristics.
LX 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 LX, 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 LX: LX 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 LX code
that completely respects the rule (this would be very difficult in C
or C++)
LX 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. LX also has support for other
properties often associated with functional programming, such as
lists, garbage collection and weak typing.
On the other hand, the internal representation of a function as an
object (for reflection) is distinct from the representation of a
compiled function (a code pointer), essentially for performance
reasons. The upside is that the representation can contain additional
information (such as the structure of the generated code or
optimization annotations.) 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. Thus, Lisp hackers will often find the LX way a bit clumsy.
However, it is important to understand that the functional
approach is often not the most efficient one in LX. For instance,
closures and lambda functions are often conveniently replaced with
generic code and functors in LX.
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.
Object-oriented languages with weak typing such as Objective-C or
Smalltalk don't generally have the weak base class problem. LX solves
the problem while preserving (not mandating) strong typing. LX also
helps solving the fragile base class problem by allowing better
encapsulation, but it doesn't totally eliminate it.
Where is the compiler? How advanced is it?
Why doesn't LX have 'classes'?
Why no private keyword? Information hiding is important!
class Library
{
public:
void AddBook(Book *bk);
private:
Book *books[32];
int numbooks;
};
type Library
procedure AddBook(in out Library L; in Book b)
Does LX support functional programming?
It's the fragile base class problem, not the
weak base class!