I was born at the cusp of the Millennial generation. I remember my parents' early home computers, and the seminal communities that paved the way for the World Wide Web. I received my first cell phone as a mid-teen, when access to the internet was distributed on physical CDs, printed in millions. Today, we are connected by technology so powerful as to deeply transform the human condition in the span of a single generation. Our voices have become multiplied, and divided. Our individual experiences have become the shared experiences of the world. Our thoughts, in a gesture, have become actions. This change, though unprecedented, is the exponential growth of science and technology predicted long ago, and long manifested. Modern young minds see inspiration and opportunity in pursuit of a new future. A command of our modern world is desirable both professionally and personally—the once arcane knowledge of the venerable ancients is, in a word, cool!

As more of us master the way in which we build our new experiences—programming, making, and collaboration—a measure of caution is warranted. We see that our haste to embrace these new ideas has left us compromised and vulnerable. Citizens are defrauded by nefarious agents, goverments are swayed by global propaganda, and the rise of the artificially intelligent may serve to decimate the middle class. As a practitioner of engineering, and a lifelong student of the application of critical thinking and problem solving, I feel it's my duty to work diligently and purposefully to prevent the exploitation of careless mistakes. Compromised technologies and paradigms are not justifiable means to quick ends. Rigorous and transparent application of the best engineering practices are essential to a sound global software infrastructure. As with medical devices, bridges, and clean water, we should accept no failure in the systems essential to our modern way of living. The teething infant of computational science and engineering is no different.

I am privileged to witness this explosive development of human intellectual endeavor, and I aspire to build an ecosystem of technology that is safe, fair, sophisticated, and for the benefit of all. I ask you to join me.

Plange is an open-source project to create a robust and comprehensive development suite including a programming language, standard library, runtime, and a toplevel (repl).

This site is for developer reference only, and may change at any time.

- license: New BSD
- source code: git repository
- status: pre-alpha
- monetization: compute resources and pro support
- pronunciation: plΓ¦ndΝ‘Κ [ipa]

goals:

- Be extensible
- Mix paradigms
- Provide a lot
- Do it the right way

print("Hello, world!");

End of line comment (red is comment text)

```
print("My name is HAL 9000."); //only kidding!
```

Inline comment (red is comment text) xkcd.com

getRandomNumber := { return 4; /*choosen by fair dice roll*/ }; //guaranteed to be random

print the voltage across a discharging capacitor

V := coerce<Real>(input("Volts: ")); R := coerce<Real>(input("Ohms: ")); C := coerce<Real>(input("Farads: ")); π‘ := coerce<Real>(input("Seconds: ")); π := R * C; print(V * π^(-π‘/π));

- BINARY_OP
- BINARY_ARITHMETIC_OP
- ADD_SUB
`Β±`

`+-`

- ADDITION
`+`

- DIVISION
`Γ·`

`/`

- EXPONENTIATION
`^`

- INTEGER_DIVISION
`\\`

- MODULATION
`%`

- MULTIPLICATIVE_OP
- CROSS_PRODUCT
`Γ`

`_x_`

- DOT_PRODUCT
`β`

`_dot_`

- MULTIPLICATION
`*`

- CROSS_PRODUCT
- SHIFTL
`<<`

- SHIFTR
`>>`

- SUBTRACTION
`-`

- ADD_SUB
- BINARY_COLLECTION_OP
- BIJECTION
`β`

`<->`

- INTERSECTION
`β©`

- SYMMETRIC_DIFFERENCE
`β`

- UNION
`βͺ`

- BIJECTION
- BINARY_LOGICAL_OP
- BITWISE_OP
- CONSTRUCTIVE_OP
- COMPOSITION
`β`

`_o_`

- COMPOUND
`|`

- PREPEND
`&`

- NULL_COALESCE
`??`

- COMPOSITION
- RELATIONAL_OP
- CASTS
`casts`

- DOWNCASTS
`downcasts`

- EXACTLY
`exactly`

- IMPLEMENTS
`implements`

- INEQUALITY
`β`

`/=`

- INHERITS
`inherits`

- IS
`is`

- RELATIONAL_COLLECTION_OP
- UPCASTS
`upcasts`

- CASTS

- BINARY_ARITHMETIC_OP
- UNARY_OP
- ALLOCATION
`alloc`

- CARDINALITY
`#`

- KLEENE_STAR
`*`

- UNARY_ARITHMETIC_OP
- UNARY_LOGICAL_OP
- ALL
`β`

`all`

- EXISTS
`β`

`exists`

- EXISTS_ONE
`β!`

`exists!`

- NOT
`Β¬`

`~`

- ALL

- ALLOCATION
- OP_ASSIGNMENT
- ADD_SUB_ASSIGNMENT
`Β±<-`

`Β±β`

`+-<-`

`+-β`

- ADDITION_ASSIGNMENT
`+<-`

`+β`

`β₯`

- AND_ASSIGNMENT
`β§<-`

`β§β`

`and<-`

`andβ`

- BIT_AND_ASSIGNMENT
`&&<-`

`&&β`

- BIT_NOT_ASSIGNMENT
`~~<-`

`~~β`

- BIT_OR_ASSIGNMENT
`||<-`

`||β`

- BIT_XOR_ASSIGNMENT
`^^<-`

`^^β`

- COMPOUND_ASSIGNMENT
`|<-`

`|β`

- COMPOSITION_ASSIGNMENT
`β<-`

`ββ`

`_o_<-`

`_o_β`

- CROSS_PRODUCT_ASSIGNMENT
`Γ<-`

`Γβ`

`_x_<-`

`_x_β`

- DIVISION_ASSIGNMENT
`Γ·<-`

`Γ·β`

`/<-`

`/β`

- EXPONENTIATION_ASSIGNMENT
`^<-`

`^β`

- FACTORIAL_ASSIGNMENT
`!<-`

`!β`

- IMPLICATION_ASSIGNMENT
`β<-`

`ββ`

`=><-`

`=>β`

- INTEGER_DIVISION_ASSIGNMENT
`\\<-`

`\\β`

- INTERSECTION_ASSIGNMENT
`β©<-`

`β©β`

- MODULATION_ASSIGNMENT
`%<-`

`%β`

- MULTIPLICATION_ASSIGNMENT
`*<-`

`*β`

- NAND_ASSIGNMENT
`β<-`

`ββ`

`nand<-`

`nandβ`

- NEGATION_ASSIGNMENT
`-<-`

`-β`

- NOR_ASSIGNMENT
`β<-`

`ββ`

`nor<-`

`norβ`

- NOT_ASSIGNMENT
`Β¬<-`

`Β¬β`

`~<-`

`~β`

- NULL_COALESCE_ASSIGNMENT
`??<-`

`??β`

- OR_ASSIGNMENT
`β¨<-`

`β¨β`

`or<-`

`orβ`

- PRE_DEC
`--`

- PRE_INC
`++`

- PREPEND_ASSIGNMENT
`&<-`

`&β`

- POST_DEC
`--`

- POST_INC
`++`

- RADICAL_ASSIGNMENT
`β<-`

`ββ`

`sqrt<-`

`sqrtβ`

- SHIFTL_ASSIGNMENT
`<<<-`

`<<β`

- SHIFTR_ASSIGNMENT
`>><-`

`>>β`

- SUBTRACTION_ASSIGNMENT
`-<-`

`-β`

- SYMMETRIC_DIFFERENCE_ASSIGNMENT
`β<-`

`ββ`

- UNION_ASSIGNMENT
`βͺ<-`

`βͺβ`

- XOR_ASSIGNMENT
`β<-`

`ββ`

`xor<-`

`xorβ`

- ADD_SUB_ASSIGNMENT

Variables can be reassigned.

Reassign a variable

color β "Blue"; color β "Red";

Type constraint on a variable

<Number> x β 1337;

Memory Model

x β 2.718281; <Pointer<Float>> x_ptr = address_of(x); echo(dereference(x_ptr));

Symbolic manipulation

x = 1337; tan(y*2) = x; // y is a free variable echo(y); // arctan(1337) / 2 = { 1.570048, -1.571544 };

A symbol with an immutable value is a constant.

Example

echo(Ο);

`Ο`

or `pi`

is a predefined constant, and provides arbitrarily high precision in symbolic manipulation.

Constants are created using the definition operator `:=`

Example

daysInAWeek := 7;

Function are created with parenthesis `( )`

containing the parameter list, and curly braces `{ }`

containing the implementation. Nullary functions may elide their parameter list.

Example

doubler := (x) { return x * 2; };

Functions have arity.

A binary function

geometric_mean := (x, y) { return β(x * y); };

Constants and variables may be strongly typed implicitly or explicitly, late bound, or duck typed.

Explicit strong typing

<Int> x β 10;

`Int`

or `β€`

is shorthand for integer. The first line constrains x to `Int`

values. See Type System.

Create function types using the `β`

operator, or `->`

.

Example

<Int β Int> doubler = (x) { return x * 2; };

doubler will only accept an `Int`

as input, and will only return an `Int`

. Function arguments may also be given types.

Semantically equivalent to the previous example

doubler := (<Int> x) { return x * 2; };

Variables that have no specified type constraint are dynamically typed.

Assigning objects of varied type to a variable

x β 10; x β "Alice"; x β { print("fubar"); };

The `type`

keyword (not capitalized) is used to make a new `Type`

object (capitalized).

Example

Color := type { <Double> r; <Double> g; <Double> b; }; <Color> red β (| 1, 0, 0 |); print(type_of( (| 1, 0, 0 |) )); // output: Tuple<Number, Number, Number> print(type_of(red)); // output: Color print(type_of(Color)); // output: Type

Parametric types are functions that return Type objects.

Example

Node := (<Type> valueType) { return type { <valueType> v; Maybe<Node<valueType>> next; }; };

Constant folding evaluates some invocations of type functions at compile time. Functions that return Type objects (or another type function) should be called with the angle bracket syntax:

Example invoking List using angle bracket syntax

<List<Int>> myList;

Pattern matching decomposes values into unbound symbols. Patterns are tested sequentially in the order given.

Tail recursive function to print the last element of a list

<List<_> β Void> printLast := (_ & tail) { printLast(tail); } | (x) { print(x); }; myList := [ 5, 12, 8, 9 ]; printLast(myList);

Output

9

The prepend operator `&`

takes a value on the left, and a list on the right. In the examples above the first parameter to the function is being broken apart into two pieces.

Note the use of the underscore `_`

character. It's substituted for a variable when the code doesn't care about the value. In the first line of the example above, we are unconcerned with the type of the elements the input list contains, and only need to ensure that the input is a list of something. In the second line, we don't need to know the value of the head element. The underscore keyword is called dont_care.

Another recursive function to filter values

filter := (head & tail, predicate) { return predicate(head) ? head & this_func(tail, predicate) : this_func(tail, predicate); } | ([], predicate) { return []; };

The inheriting keyword, used in conjunction with the type keyword, makes a new Type object inheriting the members of the specified base Types. See also: implementing

Example

// base Type Widget := type { <Void β Image> Paint; }; // derived Type TextBox := type inheriting Widget { <String> text β "Hello, world!"; // override the inherited Paint method Paint β (<Canvas> c) { return c.print(text); }; };

Types can be combined together to make algebraic Types using the compound operator `|`

.

Example

Some := (t) { return type { <t> value; }; }; None := type {}; Maybe := (t) { return Some<t> | None }; <Void β Maybe<Int>> get_age := { return coerce(input("What's your age? You don't have to tell me.")); }; print(get_age());

Many interesting problems may be constructed as one or more constraints using operators and functions. When an appropriate normal form or canonical form is given, constant folding, satisfiability solving, and symbolic manipulation yield equality constraints upon termination. See the computer algebra systems page. All the results in this section have been computed manually for demonstrative purposes.

Example

children := {| abe, dan, mary, sue |}; ages := {| 3, 5, 6, 9 |}; children β ages; // One child per one age (bijection operator) abe > dan; //abe is older than dan sue < mary; //sue is younger than mary sue = dan + 3; //sue's age is dan's age plus 3 years mary > abe; //mary is older than abe print(abe); print(dan); print(mary); print(sue);

The output when ran:

Output

5 3 9 6

Example of a linear system

<Real * Real * Real β Real> linear_interpolation := (min, max, x) { min * (1 - x) + max * x } ; inverted_linear_interpolation := (min, max, interpolated) { interpolated = linear_interpolation(min, max, x); // solve for free variable x return x; } linear_map := (minIn, maxIn, v, minOut, maxOut) { v = linear_interpolation(minIn, maxIn, x); // solve for free variable x return linear_interpolation(minOut, maxOut, x); } assert(x = inverted_linear_interpolation(y, z, linear_interpolation(y, z, x));

One well studied domain is initial value problems. An ordinary differential equation is given with boundary conditions on free variables:

Example: Aerodynamic Drag On A Projectile

projectilePosition := ( <Vector3D> initialPos, <Vector3D> initialVel, <Real> mass, <Real> drag, <Vector3D> gravity, <Real> t ) { // declare the position function, x <Real β Vector3> x; // model x as a differential equation mass * Ξ^2x(t)/Ξt^2 = -drag * Ξx(t)/Ξt + mass * gravity; // with boundary conditions x(0) = initialPos; Ξx(0)/Ξt = initialVel; // solve, substitute, evaluate return x(delta_t); };

ODE solving gives a symbolic solution for x such that the following program is functionally equivalent. This constant folding is performed and cached at compile time.

Example (continued)

projectilePosition := ( <Vector3D> initialPos, <Vector3D> initialVel, <Real> mass, <Real> drag, <Vector3D> gravity, <Real> t ) { a := π^(drag*t/mass); return ( gravity * (mass-(mass*a + drag*t)) + initialPos*a*drag^2 + drag*mass*initialVel*(a-1) ) / (a*drag^2); };

Constraint solving is intractible in the general case. Users must familiarize themselves with the capabilities of the language, which are expected to expand. A demonstration of a semantically correct but nonfunctional program is in order.

Counter Example

<Collection * BinaryRelation β Collection> sort := (items, ordering) { result β items; // result and items make a bijection β { ordering(result[i - 1], result[i]) | i β (0...|result|) }; //the result has to be sorted return result; // solve, substitute, and return };

The above function, sort, is functionaly equivalent to the sorting functions. However, this constraint based problem is not yet solvable using available techniques.

copyright Β© Brent Lewis 2013-2018