Not written by Dmitriy Myshkin.
Flare is based on XML. In dialects of LISP, both the program and the program data are represented as lists. In Flare, the program and program data, and ideally the program state, are all represented as well-formed XML. Because XML is annotative (additional sub-elements can be easily added to any parent element without destroying the structural integrity of existing data) and extensible (new sub-element types can be easily created), these properties are shared by Flare objects and Flare programs.
Flare attempts to maintain a one-to-one correspondence between elements in XML, things in the program, and - ideally - chunks in the human mind. This holds for both the program and the program data. A number or a string might be represented as <num>4</num> or <str>Hello</str>. An object of type "human" might be represented as <human><arms>2</arms><legs>2</legs></human>.
Thus a variable, a procedure, a method, a class - all of these are elements in the XML representation of the program: <variable>, <procedure>, <method>, <class>. Furthermore, every element in the XML representation is accessible and annotable within the program.
The first fundamental principle of an annotative language is that adding new data should not destroy the integrity of existing data. It should be possible, furthermore, to add new data to an object without the programmer of that object needing to think about the possibility during the object's creation. Objects in C++ and Java have no space for extra data; lists in LISP change their meaning if new LISP atoms are added; even hashes in Perl often assign meaning to every attribute present and have no namespacing mechanism for safety. The substrate in Flare is an annotable and extensible tree structure; both new elements and new element types can be added without disturbing the integrity of existing data. By sheer luck, as 'twere, there exists a very widely used standard, XML, for representing such tree structures.
See FlareSpeak and FlareCode. Flare programmers do not write their programs as actual XML objects. Instead, they write in FlareSpeak, a Python-like dialect which is converted to FlareCode XML by the IDE. FlareSpeak is not compiled to FlareCode, but is instead an editable view of FlareCode. The FlareSpeak is never stored on disk; FlareSpeak is generated anew from "decompilation" of the FlareCode each time the IDE is opened. In theory, IDEs need not be limited to language-standard FlareSpeak; any interface that displayed all conforming FlareCode files and generated conforming FlareCode files would allow the user to write portable Flare programs that would appear as ordinary FlareSpeak to other viewers. For example, an internationalized Flare IDE might display keywords in French while continuing to store the FlareCode files in standard English.
If there is something substantial to be gained, future IDEs might move away from the plain-text idiom that has dominated the world of programming until now - for example, a standardized extension of semantic comments (i.e., /* C++ comment */ or # Python comment.) might allow the storing of voice comments as MP3 data, with an attached text representation produced by speech recognition and displayed as an ordinary Flare comment. More mundanely, FlareCode could define a standard for HTML comments as well as plaintext comments; thus, it would be possible to use italics, different fonts, or include an active HTML link inside a comment. A FlareSpeak editor not capable of displaying the HTML comment would display the included plaintext version instead, perhaps with a small cartoon bubble indicating an unknown content type.
Another advantage of having an underlying XML representation might be, for example, the ability to include version control information directly in the file.
See FlareSpeak and FlareCode for more information and examples.
In XML, a "tag" is the content between the brackets - in <foo>bar</foo>, the XML content is "bar" and the XML tag is "foo". Given the example of XML data:
<human>"arms" and "legs" both have numeric content. If the Flare interpreter knows that "human" is a class defined in the module "people", then it knows to expect that the tag "arms" refers to an instance member with numeric content. This is how the Flare interpreter knows that 2 means the number 2 and not the string "2".
Every XML element has content (what's <>here</>) and a tag (what's <here>). In Flare, the tag corresponds to the metadata. Conceptually, the metadata is the category that the Flare element belongs in; the "kind of thing" the Flare element is. Every Flare element has metadata, and the metadata can be accessed - and annotated.
Suppose, in the above example, that we want a piece code to distinguish between "upper" and "lower" appendages. This applies, not only to the "human" class, but to other classes defined in the "people" and "animal" modules. There are a number of awkward ways to do this in other languages; in Flare, the code can be extremely simple. The class definition may include "upper" or "lower" as annotations when the instance members are defined, or the extra metadata can be added at runtime. Dynamic creation and access would look like this:
human1.arms^.location = "upper" # Annote metadataThis isn't really good Flare for a number of reasons - see 4: Planar annotations - but it serves to show the basic metadata access idiom.
human2 = people.human() # Create new human
arms = human2.&arms # Get reference to 'arms' member of human2
meta1 = arms^ # Get reference to metadata of 'arms'
human3 = people.human()
meta2 = human3.arms^
# meta1 == meta2
humanclass1 = people.human
humanclass2 = human3^
# humanclass1 == humanclass2
if arms^.location == "upper"
arms += 1 # Add one arm to upper body.
The most important aspect of a tag/class is the XML context it defines. The XML context determines how the tags of any subelements are interpreted. For example, "arms" in "human" is defined as having numeric content. Elsewhere, "arms" might be an object representing military inventory. This is in accordance with the standard object-oriented idiom that the same property name may mean two different things in two different objects; tags are not "used up". Even if two tags have the same name, however, they may have some meanings in common; "tentacles", in some completely different class, might also have user-defined metadata indicating that "tentacles" is an "appendage" or "upper appendage". Ideally, this extra metadata should be contained in a planar annotation (see below).
An XML element can contain sub-elements, and additional elements - even new kinds of element - can be added dynamically at runtime. This is what makes Flare an annotative language. However, to prevent namespacing collisions, Flare provides a planar access idiom that should be used for most annotations.
In the underlying XML - which should, however, never be seen, for reasons that will become apparent shortly - a planar annotation would look like this:
<human>However, "bob.p-society.status = 19" is not a legal Flare expression. In FlareSpeak, the programmer writes "bob%society.status = 19". Within the "society" module, the programmer can write "bob.%status = 19", as long as the "society" module declares that its default plane is "society". This simplifies use of private annotations within a module, while still allowing cross-module communication through the use of standard planes.
The <p-society> tag is recognized by all enclosing elements that recognize planar data, and the <status> subelement will have whatever metadata (class) is defined in the plane of "society".
Flare divides "inheritance" - as that idiom is commonly understood in object-oriented programming - into two parts. "Classes" or "metadata" are characteristics that belong to the tag rather than the element; they determine what kind of subelements exist, how the Flare element behaves, and so on. Parenting is an idiom that, although not unique to Flare, is rarer. The only language in my experience which uses it is "MOO", a language used to describe text-based virtual realities.
Parenting allows an object to inherit values on instance members, including instance methods, from other objects. The parent, in turn, may inherit from other parents. However, the parent is only checked in cases when the property is not found locally, making it very easy to override parented data - just setting the property on the child object will override the parented data without changing the property on the parent object. Class metadata may define "default parents" for an object; this is the idiom that Flare uses for inheritance of instance methods.
First, an instance of direct parenting (not in a classical inheritance sense):
mother = people.human()Instance methods, rather than being stored directly on a class (like subelement types), are stored on the "default parent" for a class. The class inheritance hierarchy is thus echoed in two places; the classes, which inherit subelements and other properties from each other, and in the parents, which inherit instance methods (or other properties) from each other. Thus, to override instance methods locally is easy, but to override metadata is more difficult.
daughter = people.human()
mother.lastname = "Ridley"
daughter.?lastname # => fails; no such property exists
daughter.lastname # => "Ridley"
daughter.lastname = "Miller"
mother.lastname # => "Ridley"
daughter.lastname # => "Miller"
lastname = daughter.&lastname
daughter.lastname = void
lastname # => "Ridley"
location(lastname) # => daughter
lastname = "Smith"
daughter.lastname # => "Smith"
mother.lastname # => "Ridley"
daughter.lastname = void
daughter.lastname # => "Ridley"
Also, remember that, while metadata objects may inherit from each other, the metadata for any metadata object is the class Metadata, just as the class of any Java class is Class. Any object (including metadata objects) must have a class, but no problem exists if this class is circular (the Metadata class object is of class Metadata; Metadata^ == Metadata). An object may have zero parents, but circular parenting is impossible.
The programmer should never need to think about the difference between "classes" and "parenting", or between "metadata" and "parenting", unless the programmer wants to make specific use of this idiom. Usually, Flare should behave just like any other object-oriented language. In particular, defining a class hierarchy in FlareSpeak, or creating an instance of a class, should automatically do the right thing for both metadata and the parenting hierarchy.
In most programming languages, the primitive data types amalgate into structures, and the structures become objects. However, procedures have remained monolithic objects with no internal structure. Flare allows complexly structured code analogous to data structures in procedural languages or objects in object-oriented languages.
Currently, languages are typically either dynamically typed, such as Python and Perl, or statically typed, such as Java and C++. Both dynamic typing and static typing have known benefits. I have always been annoyed that Python, otherwise my favorite existing language, doesn't allow for the same kind of clearly defined, cleanly summarized header files possessed by C++. Similarly, in dynamic typing languages, argument types must be either remembered or recorded in comments, and either way, the interpreter doesn't know about it and won't catch any mistakes until the code runs. On the other hand, I like the convenience of not needing to declare my variables inside a single procedure, where all the relevant information exists in one place and there is no need to remember the variable's class outside the procedure.
In Flare, static typing is allowed, but optional. Static typing does not arise from a compiler's need to track data sizes, but instead is an optional safety feature that programmers can use to make code more understandable and to catch certain classes of errors. Underlying typing is dynamic and a static type is usually treated only as an invariant to be checked, although the static type may also contribute to the parsing or evaluation of some expressions where there is an ambiguity to be resolved.
I think that good programming practice will call for the use of static typing - even if the declared static type turns out to be "free variable" - wherever a nonlocal interaction exists. In particular, this includes procedure arguments (unless the procedure is a local subprocedure), and instance members (unless the instance member is created and referred to within a single instance method). It is also desirable to declare instance members statically in order to make use of Flare's other features, such as the ability to annotate or alter the metadata of instance members.
All references in Flare are two-way; it is always possible to obtain a complete list of the references to any Flare object. In this, Flare goes far beyond garbage collection. It is possible to have garbage-collected Flare objects that are deleted when the number of hard references reaches zero, Flare objects that are allocated on the stack and deleted when the method returns, and Flare objects that are explicitly deleted by the code. Because references are two-way, dynamically deleting a Flare object that has a remaining hard reference - rather than causing a "loose pointer" as in C++ and other compiled languages - can cause an exception; dynamically deleting a Flare object that has remaining soft references will cause those references to be set to null. Dynamically deleting a Flare object that was allocated on the stack will cause an error. A dangling hard reference to a Flare object defined on the stack will cause an error when the function pops off the stack.
Flare thus combines, in complete safety, the best of interpreted, garbage-collected languages, and compiled, dynamic-deletion or stack-based-deletion languages. Using garbage collection as the only object-management option can lead to slowness, nondeterministic deletion, and memory leaks when a dangling reference is left somewhere. In Flare, garbage collection need be used only when it is the appropriate paradigm; stack-based allocation and dynamic allocation can also be used in complete safety. If a dangling reference is left, or if a reference to a stack-based object is accidentally passed outside the method's scope, the result is not a memory leak, but an explicit exception. Flare programmers don't need to worry about leaving loose pointers or worry about second-guessing the garbage-collection algorithm.
Some implementation notes:
Flare references are by name. A name, which must be unique in the local program space, can be automatically generated if a pointer is created to a Flare element with no previously existing name. (The programmer does not have to keep track of this.) The two-wayness of Flare references is maintained by the interpreter. The XML definition of an object contains the name - the XML target - of the object, but does not contain a list of all the references to that target. Instead, it is "by definition" true that you can always magically get a list of all the references to an object by asking the interpreter. The list of two-way pointers is maintained as an interpreter internal variable at the lowest level of abstraction. It is not, conceptually, part of the XML representation, but is rather a property of the XML representation.
Similarly, the location of an XML element - that is, the enclosing superelement - is also always maintained by the interpreter, and that this location is accessible to Flare programs, even though it is not explicitly represented in XML. Imagine if each and every <human><arms>2</arms></human> had to be stored as <human id="human:0x03238921"><arms location="human:0x03238921">2</arms></human>! Like two-way references, element locations are a property of the XML representation, tracked at the lowest level of the interpreter (where there are explicitly stored backreferences), but not conceptually an explicit property of the representation. One writes location(human2) and not human2.location.
"Replacement" means that instead of the expected content, the interpreter
finds an object, or a reference to an object.
"Interception" means that an element contains a special annotation which changes the behavior of the element.
An example of a replacement might be, for example, <num><SpecialNumberType>...data...</SpecialNumberType></num>. This shows an element that expected numeric content finding an object. This object will be expected to have metadata indicating that it can act as a replacement for a number, and a set of instance methods allowing it to do so.
"Intercepted" means an element contains annotations which have a special meaning to the interpreter. For example, an intercepted instance member might look like this: <human><arms><p-intercept><modify_access>[reference to procedure]</modify_access><watch_store>[reference to procedure]</watch_store></p-intercept>2</arms></human>. In this case, a certain procedure will be called whenever this variable is accessed, and a second procedure will be called whenever a new value is stored in the procedure. Interceptions may independently implement the intercepted action, modify the value intercepted before proceeding, or passively observe the transaction.
The use of annotations which have special meaning to the interpreter, and modify the metadata or behavior of parent or grandparent elements, is a very powerful idiom. The use of this idiom must be tightly controlled or the code will melt down and the Flare project programmers will go insane. Whether an element has special meaning to the interpreter is controlled by the metadata assigned to that element by the enclosing context; if the enclosing context refuses to acknowledge the existence of planes, then adding data which looks like an interception will accomplish nothing. This permits some slight degree of safety in, for example, munching raw XML data received from an untrusted Internet source. (Note: Write more about security in "Features of Flare", including "tainting" methods.)
Both replacement and interception operate transparently. A number that has been replaced by an object is indistinguishable from an ordinary number except by its behavior. It can be passed to functions that accept numbers, whether or not those functions were designed with that in mind. (Of course, some restraint is necessary to prevent replacements from propagating through the system.) Similarly, a property that has been intercepted is used like any other property, and only the behavior differs.
Flare implements a number of powerful new features. With great power comes the temptation to abuse that power. The example above, replacement and interception, looks particularly easy to abuse. That's why Flare also has some powerful new safety features.
The language Eiffel is known chiefly for language constructs called invariants, especially preconditions and postconditions. Preconditions and postconditions are expressions which must hold true on entry to a function or on return from a function; for example, that an argument be less than zero, that a return value be greater than zero, that a stack not be empty when pop() is called, or that when "stack.push(x)" is done executing, "stack.top() == x". Objects can also have invariants; for example, an Alien object might specify that the number of tentacles must be greater than or equal to zero.
Flare, in which methods can be more complex than simple atomic procedures, naturally allows methods to have preconditions and postconditions. Flare's two-way references allows for the new language feature of reference-scoped invariants - in other words, invariants that are characteristic of a reference, but applied to a referent. "The car pointed to by this reference must have gasoline greater than zero." This invariant would hold for as long as the referent existed and pointed to that car, would vanish if the reference were set to null, or would switch to another car if the reference were changed.
Variables in a method can also have invariants; for example, it, when declaring the local variable graduationDate, invariants on that variable could specify that graduationDate must be greater than zero and greater than birthDate.
C++'s ability to declare static variables, procedure arguments, or instance methods as "const" is another example of a safety invariant. Declaring a static variable as "const" allows the compiler to make certain optimizations and also allows the compiler to catch attempts to modify the variable. Declaring a reference passed to a procedure as having a "const" referent means that the procedure may not modify the referent. Declaring an instance method as "const" means that the instance method may not modify its home object in the course of its execution. Control of side effects is another very powerful idiom, and is the basis for the popularity of functional programming languages such as Haskell.
Invariants are a good thing; they should be everywhere. However, violation of an invariant should always signify a programming error, rather than being used to throw an exception as part of the application logic. This is because invariants suck a great deal of computing power, and programmers may wish to turn on invariants only during debugging, or turn on only some invariants during debugging. (Eiffel programmers often say that when you run into a problem, you switch on invariant checking and the program debugs itself.) (3). Flare may allow programmers to declare pseudo-invariants that throw non-fatal exceptions and constitute part of the application logic, but this is a low-priority feature.
For projects that wish to implement universal safety features, Flare should also allow for program invariants. For example, "our code does not use interceptions", or "application modules may not use interceptions". Running with all the easily abusable (but still powerful) features disabled, but all the enhanced safety features turned on, should result in very clean programs. For mission-critical code a still higher standard of safety may exist; during debugging, the Flare interpreter might be asked to toss a random mutation(!) into the program execution every now and then, and the programmer would in each case go on to rewrite the code to make sure that the error was caught by an invariant, or better still, repaired before any invariants were violated - a practice which will hopefully lead to robust "armored" code.
Some languages are built on the philosophy that a language should constrain programmers to keep them from shooting themselves in the foot. (4). Flare's underlying philosophy is that we'll hand you the gun and give you the ammunition, but we'll also help you build armor-plated feet.
When writing multithreaded programs in C++, I often found that I wanted to have global variables with threaded scope. Since C++ has no idiom for thread-scoped globals, this took some doing - the variable accesses were ubiquitous enough that I didn't want to look up the current thread in a hashtable or some such. Frequently, the thread variables also wound up becoming procedure-scoped stacks - that is, a procedure would add an object to the top of the stack, then remove the object when the procedure returned. My nickname for these constructs was TPLVs, which stood for "telepathic local variables". They behaved like function arguments that could be passed across intervening calls; a function call high on the stack could talk to another function call much lower on the stack, and vice versa. This increased innocence - innocence being a higher octave of modularity - because the intervening function calls didn't need to pass the argument along. Exception handling, in most object-oriented languages, is another instance of this principle; an exception occurring far down on the stack can be handled much higher up, without the intervening function calls needing to know about it.
The generalized design pattern is one that I've named the Situation pattern. A Situation allows for distributed self-assembly of data as the thread of flow control sweeps across the hierarchy of function calls. Any function call can contribute to the continuing growth of the information being assembled, whether or not the higher-level calling functions are aware of exactly what is being contributed; this increases modularity. Unlike a global variable, Situations are local to the function stack in which they were created, and are deallocated when the function that created them returns. Even within a single stack, more than one Situation may exist, as functions recurse on a problem by allocating, assembling, and deallocating their own copies of the Situation variable.
In other programming languages, this language feature is known as dynamic scoping. Procedure variables in Perl were once declared using the local() statement, and variables thus declared could be accessed remotely by any functions called from the original declarer. However, since dynamic scoping is no fun if it's the only way to declare local variables, Perl's dynamic scoping went out of style with the introduction of the entirely private my() declaration in Perl 5.
My own experimentation with Situations shows that they can easily lead to a powerful self-assembly effect; individual functions can add data which then becomes the "target" for further data. For example, in the Causality design pattern for automated change propagation through runtime dependency tracking (see included file), the "currently computing object" is a dynamically scoped variable declared in one function, which then becomes the target for local declarations of "dependencies" in all functions called by the top function. Similarly, because multiple threads may be computing independently, and because computing one object may cause other objects to need to compute, the "currently computing object" must be local to a single thread and must have stack-based dynamic scope.
The "distributed self-assembly" affect of a Situation pattern only really come into play if information contributed by some function calls can affect how the information distributed by other function calls is targeted, or if the information contributed by early function calls affects the action of later function calls. Exception-handling is a good example of how distant functions can communicate across innocent intervening functions by matching a common target (the class of the exception thrown and the class targeted by the exception handler), but this doesn't really count as "distributed self-assembly"; see below.
Exception-handling allows distant functions to communicate across innocent intervening functions by matching a common target, the class of the exception thrown and the class targeted by the exception handler. Some languages require all procedures to declare all exceptions that can be thrown through them; a very bad decision from the perspective of innocence, since it means that someone who writes a function must be aware of all the functions that may be called by it, however far down the stack. If the application is simply not suited to this type of predictability, the programmer winds up declaring that all functions throw all exceptions; even then, the necessity of an intervening library call can ruin the smooth flow of the application logic and force the programmer to handle exceptions in inappropriate places. (In part, this attitude is probably due to regarding exceptions as strictly reserved for error conditions, rather than as part of the application logic.)
Flare generalizes the idiom of exception handling to active stacks, a feature which is well-suited to Flare, since methods in Flare can have complex internal structure. In particular, yanking back the thread of execution to a higher function should no longer need to be an error condition; it should be an action permitted as part of the application logic. It may also be possible for the currently executing function to temporarily transfer the thread of control back to a function higher on the stack, if that stack has declared a handler for some condition - think of it as a temporary exception, which, when handled, transfers control back again. A function doesn't need to become entirely passive until the thread of control returns - it can be a part of an active stack.
Active stacks allow a low-level function to communicate with a function higher on the stack without the intervening functions being aware; ideally, the two functions can communicate without needing to be aware of each other. An exception handler doesn't need to know where the exception was thrown, or vice versa. A Causality computation doesn't need to be aware of where the dependencies will occur, and the dependencies don't need to know which computation they're a part of. Two functions that access a common Situation or a common dynamically scoped variable can interact without being aware of each other.
If most languages have control threads, then Flare could be said to have "control trees". All of these operations are automatically parallel:
foo@.doSomething(3) # Call doSomething(3) on each element in listThe use of an '@.' or '[@]' operation, in addition to representing a normal mapping, gives the interpreter the option of carrying out the individual items of the mapping in parallel, rather than serially. If foo = [1, 2, 3, 4], then foo[@] += 1 does not mean that the interpreter needs to add 1 to foo, then add 1 to foo, and so on; the interpreter can optionally choose to fork off four subthreads; one thread that adds 1 to foo and returns; a second thread that adds 1 to foo and returns; a third thread that adds 1 to foo and returns; and so on.
# foo. May be carried out in parallel.
foo[@] += 3 # Add three to each element in list foo. The Flare
# interpreter may choose to carry out this operation
# in parallel.
foo[@] = bar[@] - baz[@] # Subtract list baz from list bar and
# store the results in list foo.
# Foo, bar, and baz must all have the same
# The interpreter may choose to carry out
# this operation in parallel.
foo@.bar()@.baz() # In parallel, call bar() on each element of
# foo, then call baz() on each result as it is
# produced. bar() may return a list, in which
# case baz() will be called on each item in
# each list returned.
# Note that each '@.' in an expression is a
# separate mapping, while all '[@]' accesses
# in a single expression belong to a single
# synchronized mapping.
In the above example, such an extraordinary degree of parallelism is not likely to yield efficiency improvements on current machines. In fact, unless the user has a symmetric multiprocessing (SMP) machine, currently quite rare, "parallel" operations will necessarily be performed one at a time, since only one CPU exists to handle the workload. It is thus to be expected that most "parallel" operations, on today's Flare implementions running on today's hardware, will be carried out in serial by the originating thread, rather than being delegated to subthreads. However, tomorrow's machines may have 32 processors, if the software exists to take advantage of it; if a program running on 256 small, cheap 500Mhz CPUs is sixteen times faster than a program running on a big, expensive 4GHz CPU (5). For that matter, the big, expensive 4GHz CPU of the future may package 16 auxiliary mini-CPUs on the same chip, but again, only if the software exists to create the demand. See "Scalability".
Any operation that could, conceptually, be carried out in parallel, is something that the interpreter should be given the option to carry out in parallel - "Parallelism By Default".
# 'for' operations are parallel by default.Flare is Parallel By Default mostly because of a belief about where programming needs to go over the next few years. However, there is also a direct benefit to programmers who are carrying operations that can block, such as graphics operations, disk operations, network operations, and so on. If `doSomething(item)` in the above example blocks on `doSomething(bar)`, the interpreter knows it can go ahead and start on `doSomething(bar)` while waiting for `doSomething(bar)` to finish. (This is arguably the only time when a Flare interpreter on non-SMP hardware should take advantage of Parallel By Default operations.)
for item in foo.bar # Do something with each item in a list.
doSomething(item) # Carried out in parallel.
for$ item in foo.bar # As above, but in serial instead of
doSomething(item) # parallel.
While many languages make it possible for programmers to carry out blockable operations in separate threads, many require that the programmer define a thread class or a separate function, initiate the thread, clean up the thread, and so on; there are few languages that provide for automatic parallelism as a language idiom. In Flare, the stack is not a stack; the stack is a tree.
void doManyThings(var list)In most current languages - in fact, in all pre-Flare languages of which I am aware - function calls always form a stack. A function can only call one subroutine at a time, and can only have one calling function. In Flare, a method can only have one calling method, but it is quite possible for a single method to call multiple subroutines simultaneously, and each subroutine will identify that caller as the 'calling method'. Thus, method calls in Flare conceptually form a tree rather than a stack. (An idiom that goes quite well with the Situation pattern above; multiple subroutines can simultaneously search out data to add to a growing Situation higher on the stack.)
for item in list
Parallelism is a long-term goal, and one that may be legitimately compromised in early implementations. However, the language design should always remain compatible with ubiquitous parallelism. The ultimate reasons for this goal are discussed in "Goals of Flare" under "Scalability".
Parallelism can also add complexity required to avoid problems such as race conditions, simultaneous attempts to modify a single object in a complex way, and so on. I would suggest that Flare have surface-level constructs to deliberately resolve such problems - for example, synchronized methods a la Java - and that Flare have interpreter-level constructs devoted to detecting, but not handling, unresolved issues. Rather than trying to automatically set up a low-level semaphore system that would invisibly and transparently operate to manage attempted simultaneous accesses of a single object or variable, I would suggest that the Flare interpreter (and certainly initial versions) confine itself to detecting rather than managing potential problems. For example, two simultaneous modifications to an object not explicitly declared "unsynchronized" should cause a runtime error, with the suggestion that the programmer introduce program logic to manage the problem. This strategy is likely to be substantially easier to implement than a system which, e.g., attempts to invisibly guarantee that simultaneous accesses will be handled correctly.
(Note - James Rogers, on the flarelang-devel mailing list, has said that it may be economically possible to manage synchronization automatically and universally, not just detect possible conflicts. So we may not need to compromise on this.)