Forwards
Paradigm(s)  Functional 

Designed by  User:Sλλλ1(210) 
Appeared in  2021 
Computational class  Turing complete 
Reference implementation  Unimplemented 
File extension(s)  .txt 
Forwards is an esolang created by Andrew Phillips in 2021. Forwards is loosely inspired by Haskell, F#, and Dyalog APL, and is an attempt to write a functional language that is both short and expressive.
Feature Specification
Overview
In Forwards, everything is a function. 8
is the constant 8 function. * 2
is the multiply by 2 function. To define different sorts of things in Forwards, we first need to understand how Forwards understands functions.
To write a function, the coder uses the let
clause. the syntax can take four variations, depending on whether the argument is marked, and whether the body is in an indented block.
// with argument, no block let (x) foo <operands>: <expression> // no argument, no block let foo <operands>: <expression> // with argument, block let (x) foo <operands>: <expression> <subclauses> // no argument, block let foo <operands>: <expression> <subclauses>
 The
<expression>
is the return value, in terms of the operands and argument (if bound).  The
<operands>
is a spaceseparated list of alphanumeric identifiers. <subclauses>
contain any amount oflet
clauses which further define terms in the<expression>
. The part of the definition from
let
to the colon is called the signature.  The expression after the colon is called the body.
There is also an inline version of the let
clause, which is fn
. This can be used to represent complicated anonymous functions. the syntax is much simpler, being inline only and requiring the argument specified:
fn arg <operands>: <expr>
In either structure, an input which is a list may be unpacked to a list in the argument.
[3, 4, 5] sum_three let ([a, b, c]) sum_three: a + b + c // or [3, 4, 5] (fn [a, b, c]: a + b + c)
Rank
The rank of a function is a list (in braces) of the ranks of its operands. a constant function such as 4
takes no operands, so has rank {}
, or rank 0. Every completed function (or lambda) has rank 0, including complicated ones such as + 2
, ~ / 3
, or !! 0 +
.
If rank 0 is {}
, denoting a function without operands, then a rank n function is one of rank {n1}
, taking one operand of lower rank. For example, !
has rank 2, or equivalently rank {{{}}}
.
tokens are grouped and evaluated using ranks. In order for a function to accept the token on its right as an operand, the token's rank must match the first item of the function's rank. consider the different combinations (where square brackets denote the rank):
+[1] 7[0] => (+7)[0] just the ranks: 1 0 == {0} 0 => {} == 0 ~[1] *[1] 7[0] => ~[1] (*7)[0] => (~*3)[0] 1 1 0 == 1 {0} 0 => 1 {} == {0} 0 => {} == 0 ![2] +[1] => (!+)[0] 2 1 == {1} 1 => {} == 0 ?[{1 0}] <[1] => (?<)[{0}] == (?<)[1] {1 0} 1 => {0} == 1 ![2] ?[{1 0}] <[1] => ![2] (?<)[1] => (!?<)[0] 2 {1 0} 1 => 2 {0} == {1} 1 => {} == 0
If the compiler is unable to reduce the program to a rank 0 function, it will raise a RANK ERROR
(as in the case of ?<
).
When writing functions, the rank can be specified on the function name, or any operand. The function below will throw a RANK ERROR
if the first operand is not rank 0 (a lambda).
let getconst x{}: ...
Similarly, the below functions will throw RANK ERROR
if the rank of any operands are wrong, or if the amount of operands is wrong.
let foo{1 2} x y: x y // does not compile: x (rank 1) does not accept operand y (rank 2). let bar{} a: 3 // does not compile: bar is not rank 0. the marking is improper. fn{1 0} f x: f x // on an anonymous function, put the rank after 'fn'.
One special function to note is \
, called Elevate. It turns a rank 0 function into a rank 1 function, for use as an operator in another function. Its definition is approximately:
let \ x _ : x
?=4 2 // compiles, but does not work as intended: // will check if input is 4, and if not, changes the input to 4, // but in any case the value is immediately replaced by a constant 2. ?\=4 2 // works as intended: // check if input is 4, and if not, returns 2 (otherwise returns the input (4)).
Composition of Lambdas
Rank 0 functions, that is, functions without operators, go by many names in this document, including lambda. It is called so because it takes a lambda as argument and returns a lambda as output. A special case of lambda is the value: a constant lambda which returns itself, and which represents a value such as 5.
A Forwards program consists entirely of lambdas. The program ?>
will not compile, as it is not a lambda (rather, it is rank 1). When a program is a string of lambdas, it performs each operation at a time, lefttoright (or forwards). In other words, a length of lambdas reduces to a single lambda which is the composition of its components.
The arithmetic functions are functions that act deterministically on values. If a program consists of a value followed by a length of arithmetic functions, it is always equivalent to a single value. (These are as opposed to nonarithmetic functions, which include sideeffecting code, like IO, or nondeterministic functions, like RNG.)
5 + 3 / 2 ** 2 is the same thing as 16 because for all values of x, x (5 + 3 / 2 ** 2) gives the same result as x 16.
This reasoning allows compilers to reduce arithmetic compositions into shorter forms.
Abstract Values
Arithmetic functions expect their input and argument in specific forms. The program + "hello"
will produce an ARITHMETIC ERROR
thrown by +
, because it expects a number for its operand. However, some lambdas that are not strictly values can be accepted as abstract values. Because values and lambdas are both rank 0, there is no compiler error thrown by the code * + 2
. The diagram below shows how this function can be applied to an argument:
3 * + 2 => 3 * (3 + 2) => 15 4 * + 2 => 4 * (4 + 2) => 24 5 * + 2 => 5 * (5 + 2) => 35
The abstract value derives its value from modification of the argument. In reality, the same approach works for arithmetic values, if one recalls that these values are also constant lambdas:
3 * 6 => 3 * (3 6) => 3 * (6) => 18
The idea of abstraction is easy to apply to function definitions. One could write:
let (q) foo x_: <expression containing x> let x: q x_
And the operand x_
represents an abstract value, which resolves to x
. There is a shortcut, however, using asterisks. The below code is identical to the above:
let foo *x: <expression containing x>
In the reference, there are many arithmetic functions whose signatures contain abstract values. These indicate that while the functions expect arithmetic values such as 5, they will also allow abstract values that return the proper type.
One special function to note is $
, called Fork. Fork uses two abstract values to distribute the argument across. It is useful for writing pointfree style functions.
let (x) square_minus_half: x ** 2  (x / 2) let square_minus_half_fork: $ ** 2  / 2
Reference
Arithmetic Builtins
This list only contains the builtins from the main module of Forwards. More casespecific functions can be imported using use <module name>
in the head of the program.
Name  Signature and Description 

Add (+ ) 
let (x) + *y:

Subtract ( ) 
let (x)  *y:

Multiply (* ) 
let (x) * *y:

Divide (/ ) 
let (x) / *y:

Power (** ) 
let (x) ** *y:

Modulus (% ) 
let (x) % *y:

Returns the sum/difference/product/quotient/power/remainder of x and y .
 
GT (> ) 
let (x) > *y:

LT (< ) 
let (x) < *y:

GTE (>= ) 
let (x) >= *y:

LTE (<= ) 
let (x) <= *y:

EQ (= ) 
let (x) = *y:

neq 
let (x) neq *y:

Compares x and y . Returns 1 for true, 0 for false.
 
not 
let (b) not:

Returns the negation of the logical value.  
and 
let (a) and *b:

or 
let (a) or *b:

nand 
let (a) nand *b:

nor 
let (a) nor *b:

xor 
let (a) xor *b:

Performs the specified logical operation.  
Index (. ) 
let (ls) . *i:

of 
let (i) of *ls:

Extracts the i th value of ls
 
Catenate (++ ) 
let (ls) ++ *ls2:

Joins two lists by catenation.  
zip 
let (ls) zip *ls2:

Zips two lists into a list of length2 lists. Discards unmatched values if either list is too long.  
zipwith 
let (ls) zipwith{1 0} f *ls2:

Creates a list where the nth element is the application of f on the nth values of ls and ls2 .
 
Map (~ ) 
let (ls) map{1} f:

Applies f to each element of ls .
 
Accumulate (!! ) 
let (ls) !!{0 1} *c f:

Take a list of form [x1, x2, x3, ...] and return c f x1 f x2 f x3 ... ; that is, modify the accumulator c by each term in the list.
 
Reduce (! ) 
let (ls) !{1} f:

Insert the function f "between" each value in ls . A special form of Accumulate where the accumulator starts as the first list item.
 
filter 
let (ls) filter test:

Returns a list of the elements of ls that pass the test .
 
reverse 
let (ls) reverse:

Reverses the order of list elements  
Choose (? ) 
let (x) ?{1 0} test *y:

If x test y is true (nonzero) return x . Otherwise return y .
 
Elevate (\ ) 
let \{0 0} f _ :

\f represents a rank 1 function which ignores its operand and calls f on its argument.
 
Commute (^ ) 
let (y) ^{1 0} f *x:

Takes an argument and an operand and calls the function with the order switched (x f y ).
 
Fork ($ ) 
let ${0 1 0} *x f *y:

Distributes the argument into two abstract values, then combines them with f .

Nonarithmetic Builtins
This list only contains the builtins from the main module of Forwards. More casespecific functions can be imported using use <module name>
in the head of the program.
Name  Signature and Description 

print 
let (x) print:

println 
let (x) println:

Prints the value to stdout with/without a newline  
input 
let input:

Returns the next character from stdin  
random 
let random:

Returns a random float in the interval [0, 1)
 
randint 
let randint *x:

Returns a random int between 0 and x , inclusive
 
randelem 
let (ls) randelem:

Returns a random element of ls

Areas for Expansion
There are many weird aspects of Forwards, some of which might be able to be remedied without breaking the language concept. Two of these are its lack of truly higher order functions and its lack of a type system.
Without changing the language, however, there are several modules which should be created if Forwards were to be of any practical use.
 A
Math
module containing a plethora of mathematical functions and constants  A
String
module which allows for ease of string formatting  A
List
module which contains more list functions such assort
 A
File
orSys
module which allows manipulation of the filesystem