Project Fortress sources /

Filename Size Date modified Message
746 B
773 B
374 B
19.8 KB
811 B
504 B
927 B

This README exists in the top-level directory of the Fortress project.
Information about Fortress can be found at the following web site:

If you have Subversion installed, you can check out the Fortress
repository by going to the directory in which you want to check it out
and issuing the following command:

    svn checkout PFC

(The name "PFC" merely specifies the name of the directory you want
to check the code into.  Feel free to substitute another directory
name if you prefer.)

You'll now have a subdirectory named 'PFC'.  Go into that
directory and you'll see several subdirectories:

Fortify: The Fortify tool for converting Fortress code into LaTeX,
both interactively and in batch mode.  Scripts are provided for
conveniently producing rendered Fortress code in LaTeX documents,
for producing PDF "doc" files from Fortress source code, etc.  See
Fortify/fortify-doc.txt for more information.

ProjectFortress: The Fortress interpreter.  You'll need to build the
interpreter by following the instructions below for setting up your
environment in order to have a complete Fortress installation.

Emacs: A directory holding the Emacs Lisp file fortress-mode.el,
which defines a Fortress mode for Emacs.  To use this file, load
it from your .emacs file with the following command:

    (load (concat (getenv "FORTRESS_HOME")

SpecData: Machine-readable files used by the Fortress Language
Specification (e.g., a list of all reserved words).  Editors and other
tools may also benefit from using these files.  Moreover, all examples
included in the language specification are included in the directory

Library: The home for all of the Fortress standard libraries.

bin: Shell scripts for our various projects.  These are bash scripts;
you will need an installation of Bash on your system to run them.

You will also see the following files:

ant: A small bash script used for invoking the build.xml with
specific Ant options.  (This script defers to the script with the
same name in directory ProjectFortress.)

build.xml: The interpreter build script, written in Ant.  (This
script defers to the script with the same name in the directory
ProjectFortress.) This file defines several environment variables
used by the internals of the Fortress interpreter.  (Normally, there is
no reason to override the settings in this file.)


We assume you are using an operating system with a Unix-style shell
(for example, Solaris, Linux, Mac OS X, or Cygwin on Windows).  You
will need to have access to the following:

* J2SDK 1.5 or later.  See
* Ant 1.6.5 or later.  See
* JUnit 3.8.1 or later.  See
* Bash version 2.5 or later, installed at /bin/bash.

In your shell startup script, define environment variable
FORTRESS_HOME to point to the PFC directory you checked out.
It is very important to set this environment variable correctly;
it is used by several scripts and build files.

In your shell startup script, add $FORTRESS_HOME/bin to your path.
The shell scripts in this directory are Bash scripts.  To run them,
you must have Bash accessible in /bin/bash.

Make sure the following environment variables are set in your startup


(Although our scripts are sometimes able to guess the locations of
JAVA_HOME and ANT_HOME, it is preferred that you set them manually.)

Once all of these environment variables are set, build the interpreter
by going to the directory $FORTRESS_HOME and typing the command:

    ./ant clean test

If that doesn't work, there's a bug in the interpreter; please issue a
bug report.

Once you have built the interpreter, you can call it from any directory,
on any Fortress file, simply by typing one of the following commands at a
command line:

    fortress compile somefile.fs{s,i}
    fortress [run] [-test] [-debug] somefile.fss arg...
    fortress help

A command of the form "fortress compile somefile.fss" or
"fortress compile somefile.fsi" calls the static checker on the given
file and stores the result in a hidden "cache" directory.  No user-visible
object file is generated.  (At present, the static checker has limited
functionality.  Most significantly, static type errors are not yet
signaled.)  A file with suffix .fsi should contain a single API definition.
The name of the API should match the name of the file.  Similarly, a file
with the suffix .fss should contain a single component definition.  The name
of the component should match the name of the file.

A command of the form "fortress run somefile.fss" checks whether a cached
and up to date result of compiling the given file exists.  If so, it runs
the cached file.  Otherwise, it compiles the given file and runs the result.
This command can be abbreviated as "fortress somefile.fss".  If the optional
flag -test is given, all test functions defined in the given file are run.
If the optional flag -debug is given, stack traces from the underlying
interpreter are displayed when errors are signaled.

If all else fails, look at the script bin/fortress to see if your system
has peculiarities (for example cygwin requires ; separators in the


The directory ProjectFortress/demos/ contains some demonstration Fortress
programs.  Among them are:

buffons.fss: Buffon's needle.  Estimates pi using a Monte Carlo

lutx.fss: Naive dense LU decomposition.  Demonstrates how to define
new subclasses of Array2.

conjGrad.fss: Conjugate gradient, including the snapshot from the NAS
CG benchmark that you've seen in many Fortress talks.  Uses the Sparse
library for sparse matrices and vectors.

sudoku.fss: Solve a simple sudoku by elimination.  Includes a
tree-based set implementation.

aStar.fss: Generic A* search, accompanied by a specific instance for
solving sudoku that cannot be solved by elimination alone.

Lambda.fss: A simple interpreter for the lambda calculus that permits
top-level binding and reduces to both WNHF and NF.  If you're curious
how to parse text using the Fortress libraries, you should look here
(it's presently far more painful than we'd like).


Fortress currently lacks a full-blown component system.  All the code
in your Fortress program should reside in API and component file pairs.
If you take a look at the Fortress programs in ProjectFortress/tests/,
ProjectFortress/demos/, or SpecData/examples, you'll see that they have
the same overall structure:

component MyComponent
  exports Executable

  ...  Your program here ...

  run(args:String...):() = ...



* Object and trait declarations, including polymorphic traits.
  Constructor invocations must *always* provide the static arguments

* Overloaded functions and ordinary methods.  Top-level overloaded
  functions can be polymorphic.  Nested functions and methods must be

* Polymorphic top-level functions and methods, so long as the methods
  are not overloaded.

* Checking and inference of argument types to functions, methods, and
  operators.  These checks use the dynamic types of the arguments.
  Return types are NOT checked.  Inference of static parameters is not
  complete yet; it is often necessary to provide static arguments
  explicitly.  It is *always* necessary to do so in a constructor call
  and in any situation where a static parameter occurs only in the result
  and not in the arguments to a function.  For example, you must always
  provide the array element type E and size n when invoking the
  factory array1[\E,n\]().

* Arrays of up to three dimensions.  Note that there isn't yet a
  single overarching Array type.  For more details on the array types
  and operations defined see below.  In particular, note that array
  comprehensions are not yet implemented; the array types provide
  functions to work around this lack.  Another caveat: due to a bug we
  haven't fully understood, some (but not all) uses of the compact
  notation T[n,m] for an array type cause the interpreter to fail.
  Desugaring the code by hand to e.g. Array2[\T,0,n,0,m\] works around
  this bug.

* Array aggregates except singleton arrays.

* Parallel tupling and argument evaluation.

* Parallel for loops over simple ranges such as 0#n.

* Sequential for loops over simple ranges.  The functional method seq()
  and the equivalent function sequential() can be used to turn any
  Generator into a SequentialGenerator.

* While loops, typecase, if, etc.  Note that for parametric types
  typecase isn't nearly as useful as you might think, since it cannot
  bind type variables; we are working to address this shortcoming.

* The "atomic" construct uses code based on the DSTM2 library.  Nested
  transactions are flattened.  We use their obstruction free algorithm
  with a simple lowest-thread-wins contention manager.  Reduction
  variables in for loops are not yet implemented, so perform an
  explicit atomic update or just use a reduction expression instead.

* throw and catch expressions.

* Generators.

* at expressions.

* spawn

* also (multiple parallel blocks)


* Numerals with radix specifiers (which implies that some numerals may be
  recognized as identifiers)

* Unicode names

* Dimensions and units

* Static arguments: nat (using minus), int, bool, dimension, and unit

* Modifiers

* Keyword arguments

* True type inference

* Any checking of return types at all

* Where clauses

* Coercion

* Constraint solving for nat parameters

* Reduction variables

* Distributions

* Any of the types which classify operator properties

* Any of the bits and storage types

* Non-RR64 floats

* Integers other than ZZ32 and ZZ64

* Use of ZZ64 for indexing (the JVM uses 32-bit indices)


* This release of the Fortress language interpreter is the first to be
released in tandem with the language specification, available as open source
and online at:

Each example in the specification is automatically generated from
a corresponding working Fortress program which is run by every test run
of the interpreter.

* To synchronize the specification with the implementation, it was
necessary to temporarily drop the following features from the specification:

 - Static checks (including static overloading checks)
 - Static type inference
 - Qualified names (including aliases of imported APIs)
 - Getters and setters
 - Array comprehensions
 - Keyword parameters and keyword expressions
 - Most modifiers
 - Dimensions and units
 - Type aliases
 - Where clauses
 - Coercions
 - Distributions
 - Parallel nested transactions
 - Abstract function declarations
 - Tests and properties
 - Syntactic abstraction

* Libraries have significantly changed.

* Syntax and semantics of the following features have changed:
 - Tuple and functional arguments
 - Operator rules: associativity, precedence, fixity, and juxtaposition
 - Operator declaration
 - Extremum expression
 - Import statement
 - Multiple variable declaration
 - Typecase expression

* The following features have been added to the language:
 - "native" modifier
 - Operator associativity
 - Explicit static arguments to big operator applications

* The following features have been eliminated from the language:
 - Identifier parameters
 - Explicit self parameters of dotted methods
 - Empty extends clauses
 - Local operator declarations
 - Shorthands for Set, List, and Map types
 - Tuple type encompassing all tuple types

* Significantly more examples have been added.


The components ProjectFortress/LibraryBuiltin/FortressBuiltin.fsi,
ProjectFortress/LibraryBiltin/NativeSimpleTypes.fss and
Library/FortressLibrary.fss are imported implicitly whenever any
Fortress program is run.


There are a bunch of types that are defined internally by the Fortress
interpreter.  With the exception of Any, these cannot be overridden.
The built-in types are found in
ProjectFortress/LibraryBuiltin/FortressBuiltin.fsi and
NativeSimpleTypes.fsi; documentation for the released version of these
libraries can be found in the accompanying specification release.
Most built-in types do not have any methods.  Note that the types
found in FortressBuiltin do not have methods.

Tuple and arrow types are always built in, and cannot be overridden in
any way.

Note that there isn't (yet) a trait Object!  Eventually user-written
trait and object declarations will extend Object by default; right now,
they instead extend Any by default.  We plan to migrate to a new
infrastructure for primitive objects (based on the one used for
Boolean in NativeSimpleTypes) at which point we will remedy this

Meanwhile, operations on the primitive types in FortressBuiltin can be
found in Library/FortressLibrary.fsi; again these primitive are
documented in the specification as well.  Note in particular that in
the absence of coercion, you may occasionally need to make use of widen
and narrow to convert between ZZ32 and ZZ64.


Your best guide to library functionality is the library code itself;
this can be found in Library/ and in ProjectFortress/LibraryBuiltin.
The APIs for these libraries can also be found in the language
specification (note, though, that if you downloaded the latest version
of the Fortress implementation then the two may differ).  This section
provides an overview of things you may not immediately realize are

Juxtaposition of strings means string append.  You may also
find the BIG STRING operation (that concatenates strings) useful.

Several functions attempt to convert data of type Any to a string.
These include print(), println(), assert(), and juxtaposition of Any
with a string.  Right now, the FortressBuiltin types are printed using
internal magic, and object types are printed using the toString
method.  The consequence of this is that you will see a runtime error
if you attempt to print an object without first defining a toString

In the absence of array comprehensions, there are several ways to
create and initialize an array (in these examples a 1-D array, but the
2- and 3-D arrays work the same way):

The simplest is to use an aggregate expression (this seems to fail at
top level in your program, which is a known bug):
    z : ZZ32[3] = [1 2 3]

If you know the size statically (it is a static parameter to your
function, or is fixed at compile time):

    a : T[size] = array1[\T,size\]()  (* lower bound 0 *)
    a[i] := f(i),  i <- a.bounds()

    a : T[size] = array1[\T,size\](initialValue)

    a : T[size] = array1[\T,size\](fn (index:ZZ32) => ...)

If you are computing the size at run time:
    a = array[\T\](size)
    a[i] := f(i),  i <- a.bounds()

    a = array[\T\](size).fill(initialValue)

    a = array[\T\](size).fill(fn (index:ZZ32) => ...)

At the moment, to create a non-0-indexed array you need to create a
correctly-sized 0-indexed array as described above, then use the
shift(newlower) method to shift the lower index.  Thus, to create an
nxn 1-indexed array you can do something like this:

    a = array2[\T,n,n\]().shift(1,1)

The replicate[\T\]() method on arrays is a little unintuitive at
first.  It creates a fresh array whose element type is T but whose
bounds are the same as the bounds of the array being replicated.  When
data distribution is fully implemented, it should respect that as well.
It is a bit like saying array[\T\](a.bounds().upper()) for 0-indexed
arrays but is slightly more graceful and deals well with non-0-indexed

You can convert any array to use 0 indexing simply by indexing it with
an empty range:
    a[:] or a[#]  ==>  a, only 0-indexed.

Any operation that yields a subarray of an underlying array shares
structure.  If you want a fresh copy of the data, use the copy() method.

To assign the contents of array a to array b, you can use:


if a is freshly allocated.  The following should work all the time:

    a[:] := b[:]

Right now type-level ranges don't really exist, so if you want to
operate on subarrays with statically type-checked bounds, you'll need
to work with the subarray method:

    subarray[\nat b, nat s, nat o\]():Array1[\T, b, s\]

This returns a structure-sharing subarray with base b and size s
starting from offset o in the current array.

The special factory functions vector and matrix are restricted to numeric
argument types and static dimensionality:

    x' : ZZ64[1000] = vector[\ZZ64,1000\](17)

At the moment, any Array1 or Array2 whose element type extends Number
is considered to be a valid vector or matrix respectively (this will
eventually be accomplished by coercion, and vectors will be a distinct
type).  Note that the t() method on matrices is transposition, and
will eventually be replaced by opr ()^T.


Defining new generators is discussed in detail in the Fortress
language specification, but if you're trying it yourself for the first
time, you may find it instructive to browse the source code of the libraries.


It is relatively easy to add new primitive functions to Fortress.  To
do this, you simply invoke the builtinPrimitive function with the name
of a loadable Java class which extends glue.NativeApp.  Useful
subclasses are NativeFn1 and NativeFn2, and any of the classes in
glue.prim (particularly the classes in glue.prim.Util).  Here's a
sample native binding, which defines the floor operator which returns
an integer:

    opr |\a:RR64/|:ZZ64 = builtinPrimitive("glue.prim.Float$IFloor")

You should *not* mention the type parameter to builtinPrimitive when
invoking it; doing so will confuse the interpreter.  Note also that
the interpreter requires that you declare appropriate argument and
return types for your native functions as shown above.  If you give an
incorrect type declaration on the Fortress side, you'll get
non-user-friendly error messages when the Java code is run.


To define a new primitive class, you will need to write a native
component.  Examples of these can be found in Library; anything that
starts with "native component" is a native component.  Here's the
first few lines of File.fss:

native component File
import FileSupport.{...}
export File


object FileReadStream(transient filename:String)
        extends { ReadStream, FileStream}
    getter fileName():String =

Note that we import a non-native component that defines traits
mentioned in the extends clause.  The first two bindings must be
language and package in that order; right now only language="java" is
supported, and the package is where the backing class will be found.
In com.sun.fortress.interpreter.glue.prim.FileReadStream defines the
corresponding backing data type.  Note that FileReadStream extends
Constructor, and defines an inner class that extends FOrdinaryObject
that represents the actual values that get passed around at run time.

The methods must extend NativeMethod, but are otherwise referenced
using builtinPrimitive just as for top-level functions.

A native class can contain a mix of native and non-native method
code.  Note, however, that the namespace in which a native object is
defined is slightly odd from the perspective of library name
visibility.  For this reason, some primitive classes extend a parent
trait (defined in a non-native component) that contains most of their
non-native functionality and that has full access to the libraries.
For example, FileStream provides a number of generator definitions
that are inherited by FileReadStream.