ParaSail (programming language)

Last updated

ParaSail
Logo for ParaSail Programming Language.jpg
Logo for ParaSail Programming Language
Paradigm compiled, concurrent, imperative, structured, object-oriented
Designed by S. Tucker Taft
Developer AdaCore
First appeared2009;13 years ago (2009)
Stable release
9.3 / 6 June 2021;15 months ago (2021-06-06)
Typing discipline strong, static
Platform x86
OS Linux, macOS, Windows
License GPL v3
Filename extensions .psi, .psl
Website parasail-lang.org
Major implementations
psli, pslc
Influenced by
Modula, Ada, Pascal, ML
Influenced
Nim [1]

Parallel Specification and Implementation Language (ParaSail) is an object-oriented parallel programming language. Its design and ongoing implementation is described in a blog [2] and on its official website. [3]

Contents

ParaSail uses a pointer-free programming model, where objects can grow and shrink, and value semantics are used for assignment. It has no global garbage collected heap. Instead, region-based memory management is used throughout. Types can be recursive, so long as the recursive components are declared optional. There are no global variables, no parameter aliasing, and all subexpressions of an expression can be evaluated in parallel. Assertions, preconditions, postconditions, class invariants, etc., are part of the standard syntax, using a Hoare-like notation. Any possible race conditions are detected at compile time.

Initial design of ParaSail began in September 2009, by S. Tucker Taft.

Both an interpreter using the ParaSail virtual machine, and an LLVM-based ParaSail compiler are available. Work stealing is used for scheduling ParaSail's light-weight threads. The latest version can be downloaded from the ParaSail website. [3]

Description

The syntax of ParaSail is similar to Modula, but with a class-and-interface-based object-oriented programming model more similar to Java or C#.

More recently, the parallel constructs of ParaSail have been adapted to other syntaxes, to produce Java-like, Python-like, and Ada-like parallel languages, dubbed, respectively, Javallel, Parython, and Sparkel (named after the Ada subset SPARK on which it is based). Compilers and interpreters for these languages are included with the ParaSail implementation. [3]

Examples

The following is a Hello world program in ParaSail:

funcHello_World(varIO)isIO.Println("Hello, World");endfuncHello_World;

The following is an interface to a basic map module:

interfaceBMap<Key_TypeisOrdered<>;Element_TypeisAssignable<>>isop"[]"()->BMap;// Create an empty mapfuncInsert(varBMap;Key:Key_Type;Value:Element_Type);funcFind(BMap;Key:Key_Type)->optionalElement_Type;funcDelete(varBMap;Key:Key_Type);funcCount(BMap)->Univ_Integer;endinterfaceBMap;

Here is a possible implementation of this map module, using a binary tree:

classBMapisinterfaceBinary_Node<>is// A simple "concrete" binary node modulevarLeft:optionalBinary_Node;varRight:optionalBinary_Node;constKey:Key_Type;varValue:optionalElement_Type;// null means deletedendinterfaceBinary_Node;varTree:optionalBinary_Node;varCount:=0;exportsop"[]"()->BMapis// Create an empty mapreturn(Tree=>null,Count=>0);endop"[]";funcInsert(varBMap;Key:Key_Type;Value:Element_Type)is// Search for Key, overwrite if found, insert new node if notforM=>BMap.TreeloopifMis nullthen// Not already in the map; add itM:=(Key=>Key,Value=>Value,Left=>null,Right=>null);BMap.Count+=1;elsecaseKey=?M.Keyof[#less]=>continueloopwithM.Left;[#greater]=>continueloopwithM.Right;[#equal]=>// Key is already in the map;// bump count if Value was null;ifM.Valueis nullthenBMap.Count+=1;endif;// in any case overwrite the Value fieldM.Value:=Value;return;endcase;endif;endloop;endfuncInsert;funcFind(BMap;Key:Key_Type)->optionalElement_Typeis// Search for Key, return associated Value if present, or null otherwiseforM=>BMap.TreewhileMnot nullloopcaseKey=?M.Keyof[#less]=>continueloopwithM.Left;[#greater]=>continueloopwithM.Right;[#equal]=>// Found it; return the valuereturnM.Value;endcase;endloop;// Not found in BMapreturnnull;endfuncFind;funcDelete(varBMap;Key:Key_Type)is// Search for Key; delete associated node if foundforM=>BMap.TreewhileMnot nullloopcaseKey=?M.Keyof[#less]=>continueloopwithM.Left;[#greater]=>continueloopwithM.Right;[#equal]=>// Found it; if at most one subtree is non-null, overwrite// it; otherwise, set its value field to null// (to avoid a more complex re-balancing).ifM.Leftis nullthen// Move right subtree into MM<==M.Right;elsifM.Rightis nullthen// Move left subtree into MM<==M.Left;else// Cannot immediately reclaim node;// set value field to null instead.M.Value:=null;endif;// Decrement countBMap.Count-=1;endcase;endloop;// Not found in the mapendfuncDelete;funcCount(BMap)->Univ_Integeris// Return count of number of items in mapreturnBMap.Count;endfuncCount;endclassBMap;

Here is a simple test program for the BMap module:

importPSL::Core::Random;importBMap;funcTest_BMap(Num:Univ_Integer;Seed:Univ_Integer)is// Test the Binary-Tree-based MapvarRan:Random:=Start(Seed);// Start a random-number sequence// Declare a map from integers to stringsvarM:BMap<Key_Type=>Univ_Integer,Element_Type=>Univ_String>;M:=[];// Initialize the map to the empty mapforIin1..Num*2forwardloop// Add elements to the mapconstKey:=Next(Ran)modNum+1;constVal:="Val"|To_String(I);Println("About to insert "|Key|" => "|Val);Insert(M,Key,Val);endloop;Println("Count = "|Count(M));forIin1..Numloop// Search for elements in the mapconstKey:=Next(Ran)modNum+1;Println("Looking for "|Key|", found "|Find(M,Key));endloop;forIin1..Num/3loop// Delete some elements from the mapconstKey:=Next(Ran)modNum+1;Println("About to delete "|Key);Delete(M,Key);endloop;Println("Count = "|Count(M));forIin1..Numforwardloop// Search again for elements in the mapPrintln("Looking for "|I|", found "|Find(M,I));endloop;endfuncTest_BMap;

Related Research Articles

In object-oriented programming, the iterator pattern is a design pattern in which an iterator is used to traverse a container and access the container's elements. The iterator pattern decouples algorithms from containers; in some cases, algorithms are necessarily container-specific and thus cannot be decoupled.

In computer programming, an iterator is an object that enables a programmer to traverse a container, particularly lists. Various types of iterators are often provided via a container's interface. Though the interface and semantics of a given iterator are fixed, iterators are often implemented in terms of the structures underlying a container implementation and are often tightly coupled to the container to enable the operational semantics of the iterator. An iterator performs traversal and also gives access to data elements in a container, but does not itself perform iteration.

In mathematics and computer science, a higher-order function (HOF) is a function that does at least one of the following:

In computer science, a tagged union, also called a variant, variant record, choice type, discriminated union, disjoint union, sum type or coproduct, is a data structure used to hold a value that could take on several different, but fixed, types. Only one of the types can be in use at any one time, and a tag field explicitly indicates which one is in use. It can be thought of as a type that has several "cases", each of which should be handled correctly when that type is manipulated. This is critical in defining recursive datatypes, in which some component of a value may have the same type as the value itself, for example in defining a type for representing trees, where it is necessary to distinguish multi-node subtrees and leaves. Like ordinary unions, tagged unions can save storage by overlapping storage areas for each type, since only one is in use at a time.

In computer science, a generator is a routine that can be used to control the iteration behaviour of a loop. All generators are also iterators. A generator is very similar to a function that returns an array, in that a generator has parameters, can be called, and generates a sequence of values. However, instead of building an array containing all the values and returning them all at once, a generator yields the values one at a time, which requires less memory and allows the caller to get started processing the first few values immediately. In short, a generator looks like a function but behaves like an iterator.

<span class="mw-page-title-main">Foreach loop</span> Control flow statement for traversing items in a collection

In computer programming, foreach loop is a control flow statement for traversing items in a collection. foreach is usually used in place of a standard for loop statement. Unlike other for loop constructs, however, foreach loops usually maintain no explicit counter: they essentially say "do this to everything in this set", rather than "do this x times". This avoids potential off-by-one errors and makes code simpler to read. In object-oriented languages, an iterator, even if implicit, is often used as the means of traversal.

<span class="mw-page-title-main">Java syntax</span> Set of rules defining correctly structured programs

The syntax of Java refers to the set of rules defining how a Java program is written and interpreted.

In mathematics and in computer programming, a variadic function is a function of indefinite arity, i.e., one which accepts a variable number of arguments. Support for variadic functions differs widely among programming languages.

In some programming languages, const is a type qualifier that indicates that the data is read-only. While this can be used to declare constants, const in the C family of languages differs from similar constructs in other languages in being part of the type, and thus has complicated behavior when combined with pointers, references, composite data types, and type-checking. In other languages, the data is not in a single memory location, but copied at compile time on each use. Languages which utilize it include C, C++, D, JavaScript, Julia, and Rust.

typedef is a reserved keyword in the programming languages C and C++. It is used to create an additional name (alias) for another data type, but does not create a new type, except in the obscure case of a qualified typedef of an array type where the typedef qualifiers are transferred to the array element type. As such, it is often used to simplify the syntax of declaring complex data structures consisting of struct and union types, although it is also commonly used to provide specific descriptive type names for integer data types of varying sizes.

The syntax of JavaScript is the set of rules that define a correctly structured JavaScript program.

<span class="mw-page-title-main">Recursion (computer science)</span> Use of functions that call themselves

In computer science, recursion is a method of solving a computational problem where the solution depends on solutions to smaller instances of the same problem. Recursion solves such recursive problems by using functions that call themselves from within their own code. The approach can be applied to many types of problems, and recursion is one of the central ideas of computer science.

The power of recursion evidently lies in the possibility of defining an infinite set of objects by a finite statement. In the same manner, an infinite number of computations can be described by a finite recursive program, even if this program contains no explicit repetitions.

C++11 is a version of the ISO/IEC 14882 standard for the C++ programming language. C++11 replaced the prior version of the C++ standard, called C++03, and was later replaced by C++14. The name follows the tradition of naming language versions by the publication year of the specification, though it was formerly named C++0x because it was expected to be published before 2010.

This is an overview of Fortran 95 language features. Included are the additional features of TR-15581:Enhanced Data Type Facilities, which have been universally implemented. Old features that have been superseded by new ones are not described – few of those historic features are used in modern programs although most have been retained in the language to maintain backward compatibility. The current standard is Fortran 2018; many of its new features are still being implemented in compilers. The additional features of Fortran 2003, Fortran 2008 and Fortran 2018 are described by Metcalf, Reid and Cohen.

In computer programming, an anonymous function is a function definition that is not bound to an identifier. Anonymous functions are often arguments being passed to higher-order functions or used for constructing the result of a higher-order function that needs to return a function. If the function is only used once, or a limited number of times, an anonymous function may be syntactically lighter than using a named function. Anonymous functions are ubiquitous in functional programming languages and other languages with first-class functions, where they fulfil the same role for the function type as literals do for other data types.

Generics are a facility of generic programming that were added to the Java programming language in 2004 within version J2SE 5.0. They were designed to extend Java's type system to allow "a type or method to operate on objects of various types while providing compile-time type safety". The aspect compile-time type safety was not fully achieved, since it was shown in 2016 that it is not guaranteed in all cases.

In object-oriented computer programming, a null object is an object with no referenced value or with defined neutral (null) behavior. The null object design pattern, which describes the uses of such objects and their behavior, was first published as "Void Value" and later in the Pattern Languages of Program Design book series as "Null Object".

This Comparison of programming languages compares the features of associative array data structures or array-lookup processing for over 40 computer programming languages.

In computing, associative containers refer to a group of class templates in the standard library of the C++ programming language that implement ordered associative arrays. Being templates, they can be used to store arbitrary elements, such as integers or custom classes. The following containers are defined in the current revision of the C++ standard: set, map, multiset, multimap. Each of these containers differ only on constraints placed on their elements.

A bitwise trie is a special form of trie where each node with its child-branches represents a bit sequence of one or more bits of a key. A bitwise trie with bitmap uses a bitmap to denote valid child branches.

References

  1. Rumpf, Andreas (19 October 2017). "Nim without GC". Araq's Musings. Retrieved 1 September 2020.
  2. ParaSail blog
  3. 1 2 3 ParaSail website

General references