The Heresy Programming Language
1 The Heresy Rules
2 Heresy Syntax and Conventions
3 Heresy Reference
3.1 Declarations
def
let
fn
3.2 Conditionals and Loops
if
select
for
do
break
carry
cry
3.3 Predicates and Logic
list?
null?
number?
zero?
one?
eq?
equal?
symbol?
string?
fn?
atom?
lat?
and
or
not
else
True
False
Null
3.4 Syntax and Evaluation
quote
quasiquote
unquote
unquote-splicing
error
run
rem
apply
3.5 Input and Output
print
?
input
using
file
3.6 Lists
list
join
head
tail
range
map
filter
len
foldr
foldl
reverse
index
index*
inlst
left
right
mid
slice
append1
append
assoc
subst
heads
sort
zip
zipwith
3.7 Strings
=$
&
list$
str$
empty$?
len$
list&
head$
tail$
left$
right$
mid$
slice$
instr
split
format$
3.8 Math
+
-
/
*
=
<
>
pi
e
mod
abs
even?
odd?
sgn
inc
dec
exp
sin
cos
tan
3.9 Random Numbers
randomize
rnd
timer
3.10 Things
describe
thing
send
Self
extends
inherit
3.11 Theory
Y
Y*
fnlet
partial
compose
3.12 Pipe/  Threading Operators
:  >
f>
l>
->
->>
3.13 State Notation
State
do>
:  =
:  _
return
6.3.90.900

The Heresy Programming Language

John S. Berry III

source code: https://github.com/jarcane/heresy

 #lang heresy package: heresy

The Heresy language is a functional Lisp/Scheme dialect implemented in Racket, with syntax inspired by the BASIC family of programming languages. Its principle goals are to provide a simple core language for BASIC and other programmers to experiment with and learn how to program functionally. This document will detail the general philosophy of the Heresy language, such as exists, as well as the language syntax and functions.

The Heresy language was created by John S. Berry III with additional contributions from many others in the Racket community.

Heresy and this documentation are Copyright (c) 2014 John S. Berry III and released under the terms of the GNU LGPL.

1 The Heresy Rules

The Heresy language is developed according to a few basic "ground rules," which the author and contributors attempt to follow in developing new features and functions for the language. These are as follows:

  1. Heresy is BASIC - Heresy is inspired by BASIC, and aims to be at least somewhat easy for BASIC programmers to learn. Mostly this means we prefer BASIC names for functions over the Lisp name, and naming conventions like the $ for string functions.

  2. Heresy is a Lisp - Heresy is still a Lisp, and loves simple syntax and s-expressions. While it makes use of some sugaring like literal keywords for certain common primitives, these are best used sparingly. Heresy is the Diet Coke of Evil, just one calorie, not quite evil enough.

  3. Heresy is functional - Functional, but not Haskell. It is not intended solely as a vehicle for absolute functional purity. I love Haskell. You love Haskell. We don’t need to write another Haskell. Think more in terms of a lower-calorie, more intelligible Clojure.

  4. Heresy is for learning - Heresy started as a learning project, a chance to learn how Lisp and functional programming really work on a practical level. I hope that, in time, it can be that for others as well, especially those who grew up with BASIC like myself and still sometimes struggle to get their head around the functional style. In particular, this means the Heresy-written portions of the source are generally written in as clear a manner as possible, as they are intended to be self-teaching.

  5. Heresy is an experiment - Heresy is an experimental language. It’s very DNA is as a mad idea that came to life, and it’s development should be ready and willing to embrace new mad ideas and run with them. This is where carry came from, and I hope to have more mad ideas in the future.

  6. Heresy is for everyone - As a statement of culture, the Heresy community welcomes the contribution of all people, who taste equally delicious to the jaws of mighty Cthulhu. No discrimination, harassment, or any other mistreatment of contributors on the basis of age, race, sexuality, or gender will ever be tolerated by myself or anyone else who wishes to be part of this project.

2 Heresy Syntax and Conventions

Generally speaking, Heresy follows standard s-expression syntax as expected from any Lisp, being composed of parenthesized sequences of terms in Polish notation. Each sequence thus begins with an operator or function, and any number of arguments or additional s-expressions as needed.

There are however a few exceptions to usual expectations in the case of certain special forms like for, def, and if. These make use of additional literal terms as part of their syntax, to provide more clarity and similarity to BASIC-style syntax.

In accordance with that goal, Heresy also follows certain naming conventions as a matter of style. Functions which produce a string value are appended with $, and in general where a naming conflict between two similar functions in Racket/Scheme and BASIC exists, prefer BASIC.

When borrowing BASIC syntax and naming for use in Heresy, the author has generally relied chiefly on QBASIC and ECMA BASIC for reference.

3 Heresy Reference

3.1 Declarations

syntax

(def name value)

Defines a new variable of name with the given value.

syntax

(def fn name args body ...+)

 
args = (arg ...)
  | (arg ... . rest-id)
  | args-id
     
arg = arg-id
  | [arg-id default-expr]
Defines a function of name, which when called evaluates its body expressions with the given list of arguments bound to local variables for use in the body of the function’s definition. Note that there are a number of additional options here for defining arguments. Default values can be ascribed to an argument by enclosing it in additional parentheses:

Examples:
> (def fn foo (x (y 1)) (+ x y))
> (foo 3 4)

7

> (foo 5)

6

Two patterns as well exist for taking an arbitrary number of values. The argument names list can be forgone entirely and substituted with a single name (generally args* by convention), which will then contain a list of any and all values passed to the function. The second method is the use of the dot (.) in the body of the arguments list followed by a single variable (usually called rest).

Examples:
> (def fn foo args* args*)
> (foo)

'()

> (foo 3 4 5)

'(3 4 5)

> (def fn bar (x y . rest) (join (+ x y) rest))
> (bar 3 4)

'(7)

> (bar 3 4 5 6 7 8)

'(7 5 6 7 8)

syntax

(def macro name (pattern ...) template)

Defines a new macro with name. A macro can best be thought of as a function which is not evaluated, but rather returns syntax to be evaluated in the form of a template. Each name described in the pattern defines a "pattern variable" which can then be used in the body of the template and will pass any syntax contained in that portion of the pattern in the appropriate location matched in the template. The elipsis ... can be used in a pattern to indicate repeatable values.

syntax

(def macroset name [(name pattern ...) template] ...)

Similar to def macro, except that multiple matching patterns can be defined allowing for macros with variable syntax. Like def macro, the ... symbol can be used to indicate repeating values.

syntax

(let ((name value) ...) body ...+)

Binds the given name-value pairs for use in the local context created by the body of the expression. This is used to define local variables, such as are needed within a function. Note that local functions can potentially be assigned this way by storing anonymous functions, but there is a built-in syntax for defining a single such function, like so:

syntax

(let proc-id ((name value) ...) body ...)

When let is called this way, it defines a local function proc (conventionally called recur), which can then be called from within the body of the let in order to perform local recursion; the name-value pairs thus act as arguments to the function proc.

syntax

(fn (arg ...) body ...)

Creates an anonymous function with the given arguments, that evaluates its body when called. This is the lambda expression from other Lisps and functional languages, and a given fn can be passed as a value (as can named functions, for that matter) wherever called for. An anonymous function can also be evaluated directly in place by using it as the operator in an expression, like so:

Example:
> ((fn (x y) (* x y)) 4 5)

20

3.2 Conditionals and Loops

syntax

(if test then texpr else fexpr)

Evalutes test and, if test is True, evaluates texpr, otherwise it evaluates fexpr. Note that only a single expression can be placed in each "slot" in the syntax; if you need to do multiple things, use a do block.

syntax

(select (test1 expr1) ...)

(select (test1 expr1) ... (else fexpr))
Given a list of test-expression pairs, evaluates the tests in order until it finds one which is True, and evaluates the matching expression. The else expression is always true: if an else is found at the end of the select statement, its matching fexpr will be evaluated. If no test in select is true, returns #<void>.

syntax

(select case texpr ((val ...) rexpr) ...)

(select case texpr ((val ...) rexpr) ... (else fexpr))
Evaluates texpr and compares it to each val in turn until it finds a value that is eq? to the result of texpr. If one is found, it evaluates the matching rexpr. Like with select, else is always considered True, and will therefore always evaluate its fexpr. If no matching val is found, select case evaluates to #<void>. Note also that the (val ...) is a list, and can contain as many values as is needed, such as in the following example:

Example:
> (select case (* 2 3)
    ((2 3 4) (print "Nope."))
    ((5 6 7) (print "Yup."))
    (else (print "something is horribly wrong.")))

Yup.

syntax

(for (var in list) body ...)

(for (var in list with cry) body ...)
Iterates over list evaluating its body with the head of list assigned to var, then recurs with the tail of list until it returns Null. for loops declare an implicit variable cry which can be passed a value with carry. They may also be interrupted with break. See below for more details.

syntax

(do body ...)

Evaluates its bodys in order, returning the result of the final body expression.

syntax

(do loop body ...)

(do loop with cry body ...)
Evaluates body repeatedly until a break statement is encountered. Declares the implicit variable cry, which can be reassigned with the carry operator.

syntax

(break)

(break value)
Breaks the continuation of a for or do loop evaluation. If provided a value, returns that value as the result of the loop.

syntax

(carry value)

When called in the body of a for or do loop expression, immediately begins the next iteration of the loop, and passes the given value to the implicit variable cry.

syntax

cry

Loops declare an internal variable called cry, which defaults to Null, and which is passed automatically to the next iteration of the loop, and is returned when the loop concludes. The value of cry can be specified at the beginning of the loop with the optional with parameter, and carry can be used to pass a new value of cry to the next iteration.

3.3 Predicates and Logic

procedure

(list? v)  boolean?

  v : any
Returns True if v is a list.

procedure

(null? v)  boolean?

  v : any
Returns True if v is Null, where Null is defined as the empty list '().

procedure

(number? v)  boolean?

  v : any
Returns True if v is a number.

procedure

(zero? v)  boolean?

  v : any
Returns True if v = 0.

procedure

(one? v)  boolean?

  v : any
Returns True if v = 1.

procedure

(eq? x y)  boolean?

  x : any
  y : any
Returns True if x and y are the same object.

procedure

(equal? x y)  boolean?

  x : any
  y : any
Returns True if x and y are equal.

procedure

(symbol? v)  boolean?

  v : any
Returns True if v is a symbol: ie. a quoted name such as 'foo. See quote in Syntax and Evaluation.

procedure

(string? v)  boolean?

  v : any
Returns True if v is a string.

procedure

(fn? v)  boolean?

  v : any
Returns True if v is a function.

procedure

(atom? v)  boolean?

  v : any
Returns True if v is an atom: ie. a number, symbol, or function, rather than a list or Null.

procedure

(lat? l)  boolean?

  l : any
Returns True if l is a list composed solely of atoms.

syntax

(and expr ...)

Returns True only if all given expressions are True.

syntax

(or expr ...)

Returns True if any given expression is True.

procedure

(not v)  boolean?

  v : any
Returns True if v is False, else returns False.

syntax

else

A special keyword for True, used as a literal in conditional statements.

value

True : boolean?

The boolean truth value. Actually an alias for #t in the Racket implementation. Note that, in Heresy, as in Racket, anything not explicitly False is considered True.

value

False : boolean?

The boolean false value. Actually an alias for #f in the Racket implementation.

value

Null : null?

An alias for the empty list '().

3.4 Syntax and Evaluation

syntax

(quote v)

'v
"Quotes" the given v, without evaluating its contents. A quoted list is passed merely as data, a quoted atom is a "symbol" as per symbol?. Can be shortened to '.

syntax

(quasiquote v)

`v
Same as quote, but can be "escaped" with the unquote and unquote-splicing syntax. Can be shortened to `.

syntax

(unquote v)

,v
When encountered within a a quasiquoted block, evaluates v and quotes its value instead. Can be shortened to ,.

syntax

(unquote-splicing v)

,@v
Similar to unquote, but splices the result of evaluating v in place. Can be shortened to ,@.

procedure

(error message)  nothing

  message : string?
(error symbol message)  nothing
  symbol : symbol?
  message : string?
Halts the program, returning an error of symbol: message where symbol is a quoted value (customarily the name of the current function) and message is a string.

procedure

(run form)  any

  form : any
Evaluates the given form. Usage is not recommended.

syntax

(rem any ...)

Ignores its arguments and returns void. Useful for block comments.

procedure

(apply fun v ... lst)  any

  fun : fn?
  v : any
  lst : list?
Applies fun to the given arguments, as if it had been called with (fun v ... x ...) where the xs are the elements in lst.

3.5 Input and Output

syntax

(print v)

(print & v)
(print lit v)
(print)
Prints the given v to the current output, or stdout if not otherwise specified, followed by a newline. (print & v) outputs without a newline, while (print lit v) outputs as a literal form that can be directly read back by (input stx ....) as code. A bare (print) merely prints a newline to the current output.

syntax

(? ....)

A shortened macro for print.

syntax

(input)

(input stx)
(input string)
(input stx string)
Reads a line of input from the current input, or stdin if not otherwise specified, and returns the value read as a string. (input stx ....) instead reads a value using the standard reader, thus providing syntax which can be evaluated with run. If additionally provided with a string, this will be output as a prompt to the current output.

syntax

(using io-port body ...)

Evaluates the body, with input/ouptut redirected to the given io-port. Only the file port is supported at this time.

syntax

(file name as mode)

Opens the file name as the new target for input or output, depending on the mode provided. mode is a symbol, of one of the following:

'output

 

Opens file as the current output port. Will fail if file already exists.

'rewrite

 

Opens file as the current output port, rewriting its contents if the file exists.

'input

 

Opens file as the current input port.

3.6 Lists

procedure

(list v ...)  list?

  v : any
Returns a list containing the given values.

procedure

(join a b)  pair?

  a : any
  b : any
Joins a and b into a pair. If b is Null, a list is created containing a. If b is a list, a is joined to the head of the list.

procedure

(head l)  any

  l : list?
Returns the head (first element) of the list l.

procedure

(tail l)  any

  l : list?
Returns the remainder of list l after the head. If the list has only one element, returns Null.

syntax

(range start to finish)

(range start to finish step n)
Generates a list of numbers, incrementing from start to finish by n. If no n is provided, defaults to 1. Note that, unlike BASIC’s for x = y to z, descending sequences where start is greater than finish can only be declared by including a negative n. Otherwise only Null will be returned.

procedure

(map fun l)  list?

  fun : fn?
  l : list?
Given a single-argument function fun, returns a list with fun applied to each item in l.

procedure

(filter fun l)  list?

  fun : fn?
  l : list?
Given a predicate fun, returns a new list containing only those elements of l for which fun returns True.

procedure

(len l)  number?

  l : list?
Returns the number of items in l.

procedure

(foldr fun base l)  any

  fun : fn?
  base : any
  l : list?
Given a function fun with two arguments, returns the cumulative result of fun being applied to consecutive pairs, starting from base and the rightmost element of l.

procedure

(foldl fun base l)  any

  fun : fn?
  base : any
  l : list?
Similar to foldr, except that it combines pairs from the left, starting with the head of l and base.

procedure

(reverse l)  list?

  l : list?
Returns a list with the order of l reversed.

procedure

(index n l)  any

  n : number?
  l : list?
Returns the nth entry of l, indexed from 1.

procedure

(index* l dims ...)  any

  l : list?
  dims : number?
Walks through nested lists according to the given dims, essentially finding index recursively for an arbitrary number of dimensions. For example, given a nested list three lists deep, (index* l 2 3 1) would return the 1st element of the third element of the 2nd lst, like so:

Examples:
> (def dave '(1 (2 3 (4 5)) 6))
> (index* dave 2 3 1)

4

Also, (l dims ...) can be used as a shorthand for index*:

Examples:
> (def dave '(1 (2 3 (4 5)) 6))
> (dave 2 3 1)

4

procedure

(inlst item l)  any

  item : any
  l : list?
Searches l for item, returning the index of item if found, or False if not.

procedure

(left l n)  list?

  l : list?
  n : number?
Returns a list of the leftmost n elements of l.

procedure

(right l n)  list?

  l : list?
  n : number?
Returns a list of the rightmost n elements of l.

procedure

(mid l idx n)  list?

  l : list?
  idx : number?
  n : number?
Returns n entries of l starting from index idx.

procedure

(slice l first [last])  list?

  l : list?
  first : number?
  last : number? = (len l)
Returns a slice of l, starting at first and ending at last. If last is not provided, it defaults to the end of the list.

procedure

(append1 l1 l2)  list?

  l1 : list?
  l2 : list?
Returns a list with l2 appended to the end of l1.

procedure

(append l ...)  list?

  l : list?
Returns a list of the given ls appended together in order.

procedure

(assoc tgt l)  list-or-false?

  tgt : any
  l : list?
Searches the heads of a list of lists l and returns the first matching list or False.

procedure

(subst tgt new l)  list-or-false?

  tgt : any
  new : any
  l : list?
Searches the heads of a list of lists l, and if it finds tgt, returns a new list with the tail of tgt substituted for new. Otherwise, returns False.

procedure

(heads l)  list?

  l : list?
Returns a list of the heads of a list of lists.

procedure

(sort fun l)  list?

  fun : fn?
  l : list?
Sorts list l according to the comparison function fun.

procedure

(zip l1 l2)  list?

  l1 : list?
  l2 : list?
Returns a new list of lists combining l1 and l2. Excess length of either list is dropped.

procedure

(zipwith fun l1 l2)  list?

  fun : fn?
  l1 : list?
  l2 : list?
Returns a new list, combining the matching pairs of each list with fun. Excess length of either list is dropped.

3.7 Strings

procedure

(=$ x y)  boolean?

  x : string?
  y : string?
Returns True if the two strings are equal.

procedure

(& str ...)  string?

  str : string?
Concatenates its arguments into a single string.

procedure

(list$ str)  string?

  str : string?
Returns a list of one-character strings from the given string.

procedure

(str$ n)  string?

  n : any?
Converts a value n to a string.

procedure

(empty$? str)  boolean?

  str : string?
Returns True if the string is empty ("").

procedure

(len$ str)  number?

  str : string?
Returns the length of the string, indexed from 1.

procedure

(list& l)  string?

  l : list?
Given a list of strings, returns a single concatenated string.

procedure

(head$ str)  string?

  str : string?
Returns the head (first character) of the string.

procedure

(tail$ str)  string?

  str : string?
Returns the tail (remaining characters) of the string, unless str is empty, in which case it returns the empty string.

procedure

(left$ str n)  string?

  str : string?
  n : number?
Returns a string of the leftmost n characters of str.

procedure

(right$ str n)  string?

  str : string?
  n : number?
Returns a string of the rightmost n characters of str.

procedure

(mid$ str idx len)  string?

  str : string?
  idx : number?
  len : number?
Returns a section of str, len characters long, beginning at idx.

procedure

(slice$ str start [finish])  string?

  str : string?
  start : number?
  finish : number? = (len$ str)
Returns a slice of str beginning at start and ending at finish. If not specified, finish defaults to the length of the string.

procedure

(instr str search)  number-or-false?

  str : string?
  search : string?
Returns the index of the first instance of search in str, or False if not found.

procedure

(split str [delimiters])  list?

  str : string?
  delimiters : list? = '(" ")
Returns a list of string sections split at the given delimiters. If delimiters is not specified, defaults to space (" ").

procedure

(format$ template value ...)  string?

  template : string?
  value : any?
Given a string template, returns a new string with instances of glyph "#_" replaced in order, starting with the first value given following the string.

3.8 Math

procedure

(+ x ...)  number?

  x : number?
Adds the given numbers left to right and returns the result. If only one argument is given, returns the argument. If no arguments are provided, returns 0.

procedure

(- x y ...+)  number?

  x : number?
  y : nuber?
(- x)  number?
  x : number?
Subtracts the given numbers left to right and returns the result. If only one argument is given, returns (- 0 x).

procedure

(/ x y ...+)  number?

  x : number?
  y : number?
(/ x)  number?
  x : number?
Divides the numbers from left to right and returns the result. If only one argument is given, returns (/ 1 x).

procedure

(* x ...)  number?

  x : number?
Multiplies the numbers given from left to right and returns the result. If no argument is given, returns one. If one argument is given, returns the argument.

procedure

(= x y ...)  boolean?

  x : number?
  y : number?
Returns True if all the numbers are numerically equal.

procedure

(< x y ...)  boolean?

  x : number?
  y : number?
Returns True if all arguments are greater than the one previous going right (ie, x < y < z, etc.)

procedure

(> x y ...)  boolean?

  x : number?
  y : number?
Returns True if all arguments are less than the one previous going right (ie, x > y > z, etc.)

value

pi : number?

A bound variable containing the 64-bit floating point value of pi.

value

e : number?

A bound variable containing the 64-bit floating point value of Euler’s number.

procedure

(mod x y)  number?

  x : number?
  y : number?
Returns the modulus of x divided by y.

procedure

(abs n)  number?

  n : number?
Returns the absolute value of n.

procedure

(even? n)  boolean?

  n : number?
Returns True if n is even.

procedure

(odd? n)  boolean?

  n : number?
Returns True if n is odd.

procedure

(sgn n)  number?

  n : number?
Returns -1 if n is negative, 0 if n is zero, and 1 if n is positive.

procedure

(inc n)  number?

  n : number?
Returns the value of (+ n 1).

procedure

(dec n)  number?

  n : number?
Returns the value of (- n 1).

procedure

(exp x)  number?

  x : number?
Returns the value of ex.

procedure

(sin x)  number?

  x : number?
Returns the sine of x as a floating point value.

procedure

(cos x)  number?

  x : number?
Returns the cosine of x as a floating point value.

procedure

(tan x)  number?

  x : number?
Returns the tangent of x as a floating point value.

3.9 Random Numbers

Heresy’s random number generator operates slightly differently to traditional BASIC’s, in order to offer a more functional approach. Rather than defining a single global seed which the RND function then refers to, Heresy’s randomize returns a "generator" function with a given seed, allowing one to name and seed as many generators as one needs, though for practical purposes a default RND is still provided which is automatically created and seeded with a derivation of the current time in milliseconds. Heresy’s RNG employs a fairly strong 64ish-bit Xorshift* algorithm, though no guarantees are offered as to its cryptographic security.

procedure

(randomize [seed])  fn?

  seed : any/c = timer
Returns a new generator function initialized with seed, which is first passed through equal-hash-code. If no seed is provided, defaults to timer.

procedure

(rnd)  number?

A pre-defined generator which returns a random number between 0 and 1, exclusive, seeded by timer.

value

timer : number?

A special internal variable which contains the current time in milliseconds.

3.10 Things

Things are Heresy’s definable data structures. Unlike the objects of most object-oriented languages, which often exist to hold and carry mutable state and actions with which to change that state, Things are immutable. A Thing, once sprung to life, cannot itself be altered, but can be called with the correct syntax to return a new Thing with different internal values for its internal data fields.

Things are essentially functions, lambdas specifically, with predefined syntax arguments. They are first class values, and can be passed freely just as any other data, but can also be passed arguments to either return the values of their fields, return a new Thing, or to employ any functions contained within the thing.

syntax

(describe Name)

(describe Name (field value) ...)
(describe Name extends super-thing (field value) ...)
(describe Name extends super-thing inherit (id ...) (field value) ...)
Defines a new type of Thing, given Name. By convention, Things are generally named in uppercase, though this is not required by the syntax. Each field is an internal name and external symbol, which is mapped to the given value. Anonymous functions (fn) can be assigned as values to Thing fields, and those functions can access the fields of the Thing by name.

If the extends option is provided, the new Thing extends super-thing, inheriting it’s fields and methods (unless they are overridden). If the inherit option is provided with it, then the ids are available as bindings within the method expressions.

syntax

(thing)

(thing (field value) ...)
(thing extends super-thing (field value) ...)
(thing extends super-thing inherit (id ...) (field value) ...)
Just like fn produces an anonamous function, thing produces an anonamous Thing.

If there is a Thing defined as Name:

(Name)

(Name symbol)
(Name 'fields)
(Name pattern)
Once a Thing has been described or bound to a name by other means, that Name is bound locally as a function, and can thus be called with special syntax to return its contents or to return a new copied Thing. In more detail, these syntaxes are as follows:

(Name)

Returns an association list containing the contents of the Thing, ie. a list in the form of: '((field value) ...)

(Name 'fields)

Returns a list of symbols for the fields contained within the Thing. Note that the symbol 'fields takes precedent over the field names within, in order to prevent overwriting this syntax.

(Name symbol)

Returns the value of the field associated with symbol, the quoted form of the field name described when the Thing type was first declared. Will return an error if no such named field is found. If the value associated with symbol is a function, this expression can be used as the operating function of a further expression like so:

Examples:
> (describe Lord-Cthulhu (eat (fn (x) (print (& "Devours " x)))))
> ((Lord-Cthulhu 'eat) "Dave")

Devours Dave

(Name pattern)

 
pattern = `(sub-pat ...)
     
sub-pat = *
  | value
Returns a copy of the Thing, with new values according to the pattern passed to the original Thing. pattern must be a quoted list of either '*s or values, in order according to the fields of the Thing as originally defined (so the first sub-pat matches the first field, the second to the second field, and so on). A '* in a field indicates that the value is copied in-tact, while a value becomes the new value of the field in that position. For example:

Examples:
> (describe Santa
            (size 'fat)
            (sleigh 'ready)
            (sack 'full))
> (def Santa-after-Christmas (Santa `(* * empty)))
> (Santa-after-Christmas)

'((size fat) (sleigh ready) (sack empty))

procedure

(send Thing symbol arg ...)  any

  Thing : thing?
  symbol : symbol?
  arg : any
An alternate syntax for accessing functions within Things, send calls the function named by (Thing symbol) with the given arguments and returns the result.

syntax

(Self ....)

Self is the self-referring identifier for a Thing, allowing for functions within Things to call the Thing itself. Note that if it is only the values of the other fields, this is not necessary, as fields are defined as local names within the context of the Thing, and thus can be referred to simply by name.

syntax

extends

can only be used within a describe or thing form.

syntax

inherit

can only be used within a describe or thing form.

3.11 Theory

procedure

(Y fn)  fn?

  fn : fn?
The strict Y fixed-point combinator. Allows for recursion of anonymous functions. Given a fn1 which contains a single named argument, and within which is an additional single-argument fn2, the innermost fn2 can call the named argument of fn1 as if it were a function name in order to recur on itself. For example, the factorial function can be defined thusly, using the Y-combinator:

Example:
> (def Fact
    (Y
     (fn (fact)
       (fn (n)
         (if (zero? n)
             then 1
             else (* n (fact (- n 1))))))))

Note however that usage of the Y-combinator for recursion is not especially efficient, and the more traditional recursive approach is generally recommended whenever possible (which is most of the time).

procedure

(Y* fn)  fn?

  fn : fn?
A generalization of the Y-combinator that allows the function to take any number of arguments.

syntax

(fnlet name args body ...+)

Equivalent to (Y* (fn (name) (fn args body ...))). For example, to map the Fibonacci sequence without defining a named function to do it:

Example:
> (map (fnlet fib (n)
         (select
          ((zero? n) 0)
          ((one? n) 1)
          (else (+ (fib (- n 2)) (fib (- n 1))))))
       (range 0 to 20))

'(0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765)

procedure

(partial fun arg ...)  fn?

  fun : fn?
  arg : any
Returns a function with the args partially applied to fun, which can then be passed the remaining arguments, as many as needed to complete the calculation. For example:

Example:
> (map (partial + 2) (range 1 to 4))

'(3 4 5 6)

procedure

(compose fn1 fn2)  fn?

  fn1 : fn?
  fn2 : fn?
Returns a new function which is a composition of fn1 and fn2. This function evaluates fn2 with its arguments, and then applies fn1 to the result of fn2.

Examples:
> (def abs-sub (compose abs -))
> (abs-sub 4 5)

1

3.12 Pipe/Threading Operators

procedure

(:> initial-value fns ...)  any

  initial-value : any
  fns : fn?
The forward pipe operator. Given a value and a series of single-argument functions, applies them in order from left to right and returns the resulting value.

Example:
> (:> 5 inc dec sgn)

1

syntax

(f> fun args* ...)

A currying macro. Expands into an anonymous function that takes a single argument, and inserts it as the first argument of fun, followed by the remaining args*.

Example:
> (:> '(1 2) (f> append '(3 4)))

'(1 2 3 4)

syntax

(l> fun args* ...)

The inverse of f>. Returns a function whose argument is placed as the last argument to the given fun.

Example:
> (:> '(1 2) (l> append '(3 4)))

'(3 4 1 2)

syntax

(-> initial-value (fun args* ...) ...)

The first-argument threading macro. Works similarly to :>, except that it automatically applies f> to each listed form following the initial value.

Example:
> (-> '(1 2 3 4)
      (left 2)
      (append '(a b)))

'(1 2 a b)

syntax

(->> initial-value (fun args* ...) ...)

The last-argument (as in l>) version of ->.

Example:
> (->> '(1 2 3 4)
       (map (fn (x) (* x x)))
       (append '(a b)))

'(a b 1 4 9 16)

3.13 State Notation

Heresy by design contains no mutable values. Variables once set cannot be reset or altered, only shadowed by local scope. The following operators however provide a notation for performing functions with a limited local state that is mutable using a set of special operators.

Example:
> (do> (:= x 5)
       (:= y 4)
       (:= (x y) z (+ x y))
       (:_ (x y) print (format$ "#_ + #_ = " x y))
       (return z))

5 + 4 =

9

value

State : Thing

The empty State object. Used to store and retrieve values, essentially acting as a namespace for do>.

syntax

(do> (form ...) ...)

Beginning with a fresh State object, passes that State object through the subsequent forms, then returns it, unless return is called in the final position with a specific value from the state. Equivalent to (:> State ...).

syntax

(:= name value)

(:= (vars ...) name value)
The bind operator. Expands to a function that takes the current State and binds a value to name in the current State object. If the (vars ...) form is provided, the listed vars are lifted from the local state and bound for use in the value clause.

Example:
> (do> (:= a 3)
       (:= (a) b (+ a 3))
       (return b))

6

syntax

(:_ fun args* ...)

(:_ (vars ...) fun args ...)
The pass operator. Expands to a function that takes the current State, executes fun and ignores its result, then returns State unchanged. If the vars clause is provided with names, those names are available for use in fun’s arguments.

Example:
> (do> (:= x 4)
       (:_ (x) print x)
       (return x))

4

4

syntax

(return var)

The return operator. Expands to a function that takes the present State, and returns the value associated with var. Note that use of this will thus terminate State, so it is best used as the final form in a do> block.