SkillAgentSearch skills...

Sapf

Sound As Pure Form - a Forth-like language for audio synthesis using lazy lists and APL-like auto-mapping.

Install / Use

/learn @lfnoise/Sapf
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

WHAT

This program is called:

"A tool for exploring sound as pure form." or "sound as pure form" or "sapf"

It is an interpreter for a language for creating and transforming sound. The language is mostly functional, stack based and uses postfix notation similar to FORTH. It represents audio and control events using lazy, possibly infinite sequences. It intends to do for lazy sequences what APL does for arrays: provide very high level functions with pervasive automatic mapping, scanning, and reduction operators. This makes for a language where short programs can achieve results out of proportion to their size. Because nearly all of the programmer accessible data types are immutable, the language can easily run multiple threads without deadlock or corruption.

WHY

Other languages that inspired this one: APL, Joy[1], Haskell, Piccola[2], Nyquist[3], SuperCollider[4].

APL and FORTH (from which Joy derives) are both widely derided for being write-only languages. Nevertheless, there has yet to be a language of such concise expressive power as APL or its descendants. APL is powerful not because of its bizarre symbols or syntax, but due to the way it automatically maps operations over arrays and allows iterations at depth within arrays. This means one almost never needs to write a loop or think about operations one-at-a-time. Instead one can think about operations on whole structures.

Here is a great quote from Alan Perlis[5] on APL, which that also reflects my interest in this way of programming :

"What attracted me, then, to APL was a feeling that perhaps through APL one might begin to acquire some of the dimensions in programming that we revere in natural language — some of the pleasures of composition; of saying things elegantly; of being brief, poetic, artistic, that makes our natural languages so precious to us."

The Joy language introduced concatenative functional programming. This generally means a stack based virtual machine, and a program consisting of words which are functions taking an input stack and returning an output stack. The natural syntax that results is postfix. Over a very long time I have come to feel that syntax gets in between me and the power in a language. Postfix is the least syntax possible.

There are several reasons I like the concatenative style of programming: Function composition is concatenation. Pipelining values through functions to get new values is the most natural idiom. Functions are applied from left to right instead of inside out. Support for multiple return values comes for free. No need for operator precedence. Fewer delimiters are required: Parentheses are not needed to control operator precedence. Semicolons are not needed to separate statements. Commas are not needed to separate arguments.

(Note: Sapf is inspired by, but is not purely a concatenative language because it has lexical variables.)

When I am programming interactively, I most often find myself in the situation where I have a value and I want to transform it to something else. The thing to do is apply a function with some parameters. With concatenative programming this is very natural. You string along several words and get a new value.

QUICK SET-UP

Put the sapf program into ~/bin or wherever you keep commands. Since this binary is unsigned you will need to remove Apple's quarantine attribute:

xattr -r -d com.apple.quarantine <path-to-sapf-binary>

Set up the environment variables in Terminal or your shell profile. For example:

export SAPF_HISTORY="$HOME/sapf-files/sapf-history.txt" export SAPF_LOG="$HOME/sapf-files/sapf-log.txt" export SAPF_PRELUDE="$HOME/sapf-files/sapf-prelude.txt" export SAPF_EXAMPLES="$HOME/sapf-files/sapf-examples.txt" export SAPF_README="$HOME/sapf-files/README.txt" export SAPF_RECORDINGS="$HOME/sapf-files/recordings" export SAPF_SPECTROGRAMS="$HOME/sapf-files/spectrograms"

read this README.

check out some examples: start sapf in Terminal. open sapf-examples.txt in a text editor copy an example onto the command line.

COMMAND LINE

sapf [-r sample-rate][-p prelude-file]

sapf [-h] print this help

OPTIONS -r sample-rate Sets the session sample rate. The default sample rate is 96000 Hz.

-p prelude-file
    The path to a file of code to load before entering the read-eval-print
    loop. If this argument is not supplied, the prelude file is loaded from
    the path stored in the environment variable SAPF_PRELUDE.
    
-h
    print help for the command line options.

ENVIRONMENT VARIABLES

SAPF_PRELUDE 
    the path for a code file to be loaded before entering the
    read-eval-print loop.
    
SAPF_RECORDINGS
    the path where output sound files should be written.
    
SAPF_SPECTROGRAMS
    the path where spectrogram images should be written.
    
SAPF_HISTORY
    the path where the command line history is stored for recall at runtime.
    
SAPF_LOG
    the path where a log of command line inputs are stored for posterity.
    
SAPF_EXAMPLES
    the path to a file of examples. 

SAPF_README
    the path to this README file.

BUILT IN FUNCTION HELP You can get a list of all defined functions by typing "helpall". You can get help for a particular built-in function by typing "`someword help" (note the backquote).

A VERY FEW EXAMPLES

;; type 'stop' to stop sound playback

;; play a sine wave at 800 Hz and initial phase of zero.
800 0 sinosc .3 * play   

;; the analog bubbles example from SuperCollider:
.4 0 lfsaw 2 * [8 7.23] 0 lfsaw .25 * 5/3 + + ohz 0 sinosc .04 * .2 0 4 combn play

See the "examples" file for more.

TYPES "It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures." -- Alan Perlis

The language has a bare minimum of data types:
    Real - a 64 bit double precision floating point number for quantifying
        things.
    String - a string of characters for naming things.
    List - Ordered lists of values that function as both arrays and lazy
        potentially infinitely long sequences.
    Form - An object that maps symbolic names to values. A form is a
        dictionary with inheritance.
    Function - Functions are values that when applied take values from the
        stack and evaluate an expression.
    Ref - A mutable container for a value. This is the only mutable data
        type.
    
    

SYNTAX Expressions are sequences of words (or other syntactic elements) written in postfix form. All words are executed in left to right order as they are encountered. When a word is executed it looks up the value bound to that word. If the value is a function, then the function is applied and any arguments of the function are taken from the stack. If the value is not a function then the value itself is pushed onto the stack.

2 3 *  -->  6

"-->" means "evaluates to" throughout this document.

COMMENTS Comments begin with a semicolon character and continue to the end of a line.

; this is a comment line

NUMBERS

Numbers are written in the usual ways.
    1  2.3 .5 7.
    
Scientific notation
    3.4e-3 1.7e4
    
Suffixes. There are several suffixes that scale the value.

    pi - scales the number by 3.141592653589793238...
         pi can stand alone as well as being a suffix.
         pi  2pi  .5pi  .25pi
        
    M - mega. scales the number by one million 1000000.
        1M .5M
        
    k - kilo. scales the number by 1000.
        4k 1.5k
    
    h - hecto. scales the number by 100.
        8h
        
    c - centi. scales the number by .01
        386c 702c
        
    m - milli. scales the number by .001
        53m 125m
        
    u - micro. scales the number by .000001
        20u

Infix fractions with no intervening spaces are interpreted as literal real 
numbers. Both the numerator and denominator can be floating point as 
described above.

    5/4  9/7  15/11  pi/4  7.5/4  1k/3

STRINGS Strings are enclosed in double quotes.

"This is a string"

\\n is newline and \\t is tab.

"\\tThis string begins with a tab and ends with a newline.\\n"

WORDS Words are sequences of characters delimited by spaces, square brackets, curly brackets, parentheses, or one of the quote characters described below (except for the equals sign, which can occur in a word).

these are words

When a word is executed it looks up the value bound to that word. If the
value is a function, then the function is applied and any arguments of the
function are taken from the stack. If the value is not a function then the
value itself is pushed onto the stack. 

QUOTES There are certain symbols that, when immediately preceding a word, change the normal evaluation behavior. Normally when a word occurs in an expression, the value bound to the word symbol is looked up and applied. For example, when a word appears by itself like this: sin The interpreter looks up the value bound to the 'sin symbol, which is a function, and applies it.

If a word is preceded by backquote, the interpreter looks up the value bound
to the symbol without applying it.
    `sin
The function bound to the 'sin symbol will be pushed onto the stack instead
of being applied.

If a word is preceded by single quote, the interpreter pushes the symbol
itself onto the stack.
    'sin

If a word is preceded by comma, the interpreter pops the object on top of the
stack, looks up the 

Related Skills

View on GitHub
GitHub Stars936
CategoryDevelopment
Updated2d ago
Forks49

Languages

C++

Security Score

95/100

Audited on Mar 31, 2026

No findings