login  home  contents  what's new  discussion  bug reports     help  links  subscribe  changes  refresh  edit

Edit detail for ProgrammingSPAD revision 12 of 28

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
Editor: surowitz
Time: 2014/02/15 17:19:01 GMT+0
Note:

changed:
-       It is one one usually consideres as values in other programming languages.
       They are what one usually considers as values in other programming languages.

A very brief introduction to programming in SPAD

  • SPAD is an ordinary programming language, just like C, Java, and Python.
  • SPAD programs are ordinary text files that will be compiled to machine code by the SPAD compiler that comes with FriCAS?.
  • SPAD is a statically typed language with type levels.
    1. Elements: These are the basic data objects. Examples are: 42, 3.14159265, "abc", [3,5,11]. They are what one usually considers as values in other programming languages.
    2. Domains: These are the types of elements. For example, Integer is a type for 42, 3.14 is of type Float, "abc" is of type String, [1,2,4,8] is of type List(Integer).

      Domains are comparable to classes in object oriented programming languages.

    3. Categories: These are the types of domains. For example, Integer is of type IntegerNumberSystem, String is of type StringCategory, List(Integer) is of type ListAggregate(Integer).

      Categories are somewhat comparable to interfaces in Java, but are much more powerful.

    4. The highest type level is the constant keyword Category. In other words, IntegerNumberSystem, StringCategory, ListAggregate(Integer) are of type Category.
  • SPAD programs are usually definitions of categories and domains whose functionality will later be used in an interactive session or by other definitions.
  • While categories define the exports of domains, i.e. which function are provided, domains themselves implement the corresponding functions.
  • Domains form a type hierarchy where only inheritance from one other domain is allowed.
  • Categories form a type hierarchy with a multiple inheritance mechanism.
  • Categories and domains are often called constructors and (as shown above) can be parametrized.
  • SPAD has only very few built-in constructors. Most of the constructors are defined in a library. Builtin are Record, Tuple, Join, Mapping (abbreviated via ->). Library defined are Integer, List, String, Symbol, Monoid, Field, etc.

A running example

Let us start with a little program. We do not to rely on any previously defined library, but we prefix every constructor with My in order to avoid name conflicts with existing names.

Our goal is to provide a domain MyFun that is parametrized by a domain S and represents functions from S into itself. We would like to be able to turn any function of type S -> S into an element of the MyFun(S) domain. Furthermore, we want to turn this domain into a monoid MyMonoid.

First we define the category MyMonoid.

spad
)abbrev category MYMON MyMonoid
MyMonoid: Category == with
    1: %
    _*: (%, %) -> %
spad
   Compiling FriCAS source code from file 
      /var/lib/zope2.10/instance/axiom-wiki/var/LatexWiki/3904508595934233674-25px001.spad
      using old system compiler.
   MYMON abbreviates category MyMonoid 
------------------------------------------------------------------------
   initializing NRLIB MYMON for MyMonoid 
   compiling into NRLIB MYMON 
;;; *** |MyMonoid| REDEFINED Time: 0 SEC.
finalizing NRLIB MYMON Processing MyMonoid for Browser database: --->-->MyMonoid(constructor): Not documented!!!! --->-->MyMonoid(((One) (%) constant)): Not documented!!!! --->-->MyMonoid((* (% % %))): Not documented!!!! --->-->MyMonoid(): Missing Description ; compiling file "/var/aw/var/LatexWiki/MYMON.NRLIB/MYMON.lsp" (written 15 FEB 2014 05:24:13 PM):
; /var/aw/var/LatexWiki/MYMON.NRLIB/MYMON.fasl written ; compilation finished in 0:00:00.005 ------------------------------------------------------------------------ MyMonoid is now explicitly exposed in frame initial MyMonoid will be automatically loaded when needed from /var/aw/var/LatexWiki/MYMON.NRLIB/MYMON

Every constructor needs an )abbrev line where one specifies whether the constructor to come is a category or domain. Then follows a capitalized identifier of at most 7 characters and finally the identifier for the constructor.

By convention, constructors begin with an uppercase letter and capitalize the first letter of each new word. Underscores are not commonly used.

Supposed the above code goes into a file mymonoid.spad, then this file can be compiled via:

    )compile mymonoid.spad

indide a FriCAS? session.

Now comes the corresponding domain definition.

spad
)abbrev domain MYFUN MyFun
MyFun(S: SetCategory): MyMonoid with
    coerce:  (S -> S) -> %
    coerce: % -> (S -> S)
 == add
    Rep ==> S -> S
    rep x ==> (x@%) pretend Rep
    per x ==> (x@Rep) pretend %
    coerce(f: S -> S): % == per f
    coerce(x: %): S -> S == rep x
    1: % == per((s: S): S +-> s)
    ((x: %) * (y: %)): % == per( (s: S): S +-> (rep x)(rep y)(s) )
spad
   Compiling FriCAS source code from file 
      /var/lib/zope2.10/instance/axiom-wiki/var/LatexWiki/3891112245197782095-25px002.spad
      using old system compiler.
   MYFUN abbreviates domain MyFun 
------------------------------------------------------------------------
   initializing NRLIB MYFUN for MyFun 
   compiling into NRLIB MYFUN 
   processing macro definition Rep ==> S -> S 
   processing macro definition rep x ==> pretend(@(x,$),S -> S) 
   processing macro definition per x ==> pretend(@(x,S -> S),$) 
   compiling exported coerce : S -> S -> $
      MYFUN;coerce;M$;1 is replaced by f 
Time: 0 SEC.
compiling exported coerce : $ -> S -> S MYFUN;coerce;$M;2 is replaced by x Time: 0 SEC.
compiling exported One : () -> $ Time: 0 SEC.
compiling exported * : ($,$) -> $ Time: 0 SEC.
(time taken in buildFunctor: 0)
;;; *** |MyFun| REDEFINED
;;; *** |MyFun| REDEFINED Time: 0 SEC.
Cumulative Statistics for Constructor MyFun Time: 0 seconds
finalizing NRLIB MYFUN Processing MyFun for Browser database: --->-->MyFun(constructor): Not documented!!!! --->-->MyFun((coerce (% (Mapping S S)))): Not documented!!!! --->-->MyFun((coerce ((Mapping S S) %))): Not documented!!!! --->-->MyFun(): Missing Description ; compiling file "/var/aw/var/LatexWiki/MYFUN.NRLIB/MYFUN.lsp" (written 15 FEB 2014 05:24:13 PM):
; /var/aw/var/LatexWiki/MYFUN.NRLIB/MYFUN.fasl written ; compilation finished in 0:00:00.018 ------------------------------------------------------------------------ MyFun is now explicitly exposed in frame initial MyFun will be automatically loaded when needed from /var/aw/var/LatexWiki/MYFUN.NRLIB/MYFUN

This above code for MyFun can be in the same file as the code for MyMonoid, then one compilation would be enough. If, however, it is in another file myfun.spad, then a call to:

    )compile myfun.spad

indide a FriCAS? session would be necessary.

Now we can use our little program. For that, we enter a FriCAS? session and type the following.

fricas
I ==> Integer
Type: Void
fricas
II ==> I -> I
Type: Void
fricas
MI ==> MyFun I
Type: Void
fricas
inc(i: I): I == i+1
Function declaration inc : Integer -> Integer has been added to workspace.
Type: Void
fricas
double(i: I): I == 2*i
Function declaration double : Integer -> Integer has been added to workspace.
Type: Void
fricas
minc    := inc :: MI
fricas
Compiling function inc with type Integer -> Integer 
LISP output: (#<FUNCTION |*1;inc;1;initial|>)
Type: MyFun?(Integer)
fricas
mdouble := double :: MI
fricas
Compiling function double with type Integer -> Integer 
LISP output: (#<FUNCTION |*1;double;1;initial|>)
Type: MyFun?(Integer)
fricas
f := (mdouble * minc) :: II

\label{eq1}\mbox{theMap (...)}(1)
Type: (Integer -> Integer)
fricas
g := (minc * mdouble) :: II

\label{eq2}\mbox{theMap (...)}(2)
Type: (Integer -> Integer)
fricas
f 1

\label{eq3}4(3)
Type: PositiveInteger?
fricas
g 1

\label{eq4}3(4)
Type: PositiveInteger?

Note that the multiplication is not commutative.

Comments on the above program

  • SPAD distinguishes between % and Rep. That's the reason for the definition of rep and per before MyFun. The percent sign is a name for the current domain, it is comparable to this or self in other programming languages, but it does not denote the object, but rather its type, i.e., % stands for a domain.

    In the definition of MyFun, % basically stands for MyFun(S). In contrast to that, Rep denotes the domain that the current domain inherits its data representation from (but not it's exports).

    The distinction between % and Rep is in what they export. Whereas % exports all the functions that are listed in the category part of the domain, Rep points to a previously defined domain and thus exports exactly what is given there.

    In our case Rep is the same as S -> S. Whereas % exports *, Rep does not. In contrast to that. Rep allows to write f(s) if f is of type S -> S and s is of type S, i.e. one can apply f to an argument of type S. % and equally MyFun(S) do not export this functionality. That is the reason for the complicated looking expression:

          (rep x)(rep y)(s)
    

    that just says f(g(s)) if f and g denote the functions, corresponding to x and y, respectively. In other words, juxtaposition in FriCAS? associates to the right and usually means function application.

  • Similar to Python, instead of braces, SPAD uses indentation to group code blocks.
  • The excape character in SPAD is an underscore, not a backslash. Currently, the * identifier in the definition of MyMonoid must be escaped in that position. (There is hope that this need will go away in the future.)
  • The symbol 1 in the definition of MyMonoid is not a number, but rather an identifier. Since in mathematics, 0 and 1 are used so often, both can be used as identifiers.
  • One usually writes t: T to denote that t is of type T, i.e. with:
          _*: (%, %) -> %
    
        we declare that '*' is a function with two arguments of the same type, that returns a result of
        exactly this type.
    
        SPAD defines a few binary operators, like '+', '*', 'rem', 'mod' to be infix.
        Except those few functions, all functions are used in prefix form, though.
    
  • A category is defined by the following pattern:
          C: Category == Join(C1,...,Cn) with
              f1: T1
              ...
              fk: Tk
    
        where 'Join(...)' can be missing or just be a single category 'C1'.
    
  • A domain is defined by the following pattern:
          D: C == A add
              Rep ==> A
              rep x ==> (x@%) pretend Rep
              per x ==> (x@Rep) pretend %
              f1: T1 == ...
              ...
              fk: Tk == ...
    
          where 'C' is a category and 'A' is a domain from which 'D' inherits.
    
          If a domain 'A' appears in front of the 'add' keyword, then 'D' inherits
          also all the implementations of the functions that are listed in the
          category part 'C'.
    
  • SPAD has macros. :
           X ==> Y
           macro X == Y
    
        Both of the above lines are doing the same thing, they define a macro X that
        expands to Y whenever it appears elsewhere in the program code.
        Of course, only one of these lines would be sufficient.
    
  • The notation :
          (s: S): S +-> ....
    
        is a way to denote lambda expression (unnamed functions).
    
  • The notation x @ X means x will be of type X. That is rarely seen in SPAD, but since SPAD not only allows to distinguish functions by their input types, but also their output types, it is sometimes necessary.

    For example, in SPAD = is not builtin. It is an ordinary function of type:

          (%, %) -> T
    

    where T can be different things. For example, the domain Integer exports a function:

          _=: (%, %) -> Boolean
    

    with the usual meaning of equality. However, there is another domain in FriCAS?, namely Equation(Integer) that exports a function:

          _=: (Integer, Integer) -> %
    

    Now, without @ it would be impossible to tell what the type of:

          42 = 7
    

    is. It could be Boolean or Equation(Integer). If the result should be of type Boolean, we write:

          (42 = 7)@Boolean
    

  • The use of pretend in t pretend X is very dangerous. It tells the compiler to consider t as an element of type X even though it might be of a type T with a completely different memory layout. In other words "abc" pretend Integer would interpret the storage of "abc" as an element of type Integer. Careless use of pretend usually leads to a program crash and should thus better be avoided.

    Since % and Rep are supposed to have the same memory layout, pretend is safe in:

          rep x ==> (x@%) pretend Rep
    

    Nevertheless is pretend a way to make the safety that SPAD brings with its type system void if it is not used with great care. In fact, pretend should be used only in these rare situations where the compiler is unable to figure out the right type itself.

  • The notation t :: X is, in fact, equivalent to coerce(t)@X, i.e. a function with name coerce is called to turn the element t (which might be of type T) into an element of type X. In contrast to @ or pretend :: leads to the execution of this coercion at runtime.

More information about SPAD can be found in the Axiom book .

Since the Aldor programming language is very similar to SPAD, it might be advantageous to read the Aldor User Guide . There are, however, a number of differences between SPAD and Aldor . Nevertheless, it is possible to use the Aldor compiler to program new functionality for FriCAS?.

You might want to try out Aldor .

To try out SPAD online you simply edit a wiki Sandbox page and put your code into \begin {spad} ... \end {spad} blocks.