Monday, September 28, 2009

Resolving names in ParaSail

In ParaSail, the names exported by an interface can in most cases be used without any explicit qualification of the interface from which they came.  Hence, continuing with our Array interface example, we can generally write "First(Arr)" without having to specify which "First" function we mean, presuming we know the type of Arr.  If qualification is necessary, the "::" notation of C++ is used, such as "Array::First(Arr)".  ParaSail reserves "." for selecting a component of a composite object, such as "Arr.First".

ParaSail's name resolution works roughly as follows:  Going bottom up, if you have a simple name (not used as the name of an operation being called), and that name is declared in the current scope, you use that interpretation.  If you have an operation call, you determine the types of as many parameters as you can, and then look in the interfaces defining each of those types, for operations of the given name.  If you find one or more that "work," then you consider all of those possibilities.  If there are some operands which have no interpretations yet (perhaps because the operand is actually a parameterless call or an equivalent interface constant), then the type expected for that operand is used to help resolve the operand.  If you get to the "top" and you have exactly one interpretation that "works," you use that one.  If you have no interpretations, or you have more than one, the construct cannot be resolved, and an error is indicated.  This sort of overload resolution sounds more complicated than it typically ends up being, because most constructs are relatively simple.

2 comments:

  1. At some point you might post a compare-and-contrast discussion of ParaSail vs. one or more existing languages. It seems clear that much of ParaSail's structure is inherited from Ada - for those of us familiar with Ada, it would be interesting to know how ParaSail is different, and why ParaSail could not be implemented as an extension to Ada.

    As you said in an earlier posting, concrete examples can enhance understanding - I am suggesting that a comparison to an existing "concrete" language might also enhance understanding (at least mine ;-)...

    -Fred Mueller

    ReplyDelete
  2. I would identify the major influences coming from Pascal, Modula-2/3, Eiffel, ML/CAML, and Ada, but with a desire to be easily understandable by someone familiar with Java, C#, C++, CLOS, etc. You are right that the surface syntax is not fundamental, but it is hard to talk for long without one for examples, etc. It may look like Ada, but in fact that is probably more because Ada shares a common heritage with Pascal, Modula, Eiffel, and ML/CAML. The module-level semantics are pretty different from Ada, while the basic control-flow syntax comes very directly from the Pascal family (which might more properly be called the "Algol" family).

    A few overall key characteristics:

    There are only two kinds of modules, interfaces (specs) and classes (bodies). Every class has an interface, every non-abstract interface has at least one class. All modules are "generic templates" in the sense that they have formal "module" parameters which can be types, operations, or values. A type is produced by "instantiating" an interface by giving actual module parameters to correspond to its formal module parameters. There are no global variables -- access to variables is only via parameters.

    Annotations (specified inside "{...}") may appear in various places, and can act as preconditions, postconditions, or simple assertions.

    Parameters are evaluated in parallel, and statements are executed in parallel when separated by "||" or when nonconflicting; for-loop iterations are by default in parallel.

    ReplyDelete