Shift is an esoteric functional programming language. It is stack-based, but also has automatic currying like Haskell.
There are two datatypes in Shift:
- Functions, which can have an arbitrary positive arity (number of inputs), and which return a list of outputs. For example, a function that duplicates its only input has arity 1, and a function that swaps its two inputs has arity 2.
- Blanks, which are all identical and have no other purpose than not being functions.
A Shift program consists of zero or more commands, each of which is a single ASCII character. There are 8 commands in total:
!
(apply) pops a functionf
and a valuex
from the stack, and appliesf
tox
. Iff
has arity 1, the listf(x)
is appended to the stack. If it has arityn > 1
, a new(n-1)
-ary functiong
is pushed to the stack. It takes inputsx1,x2,...,xn-1
and returnsf(x,x1,x2,...,xn-1)
.?
(blank) pushes a blank to the stack.+
(clone) pushes to the stack a unary function that duplicates its input: any valuex
is mapped to[x,x]
.>
(shift) pushes to the stack a unary function that takes in ann
-ary functionf
, and returns an(n+1)
-ary functiong
that ignores its first argumentx
, callsf
on the remaining ones, and tacksx
in front of the result. For example,shift(clone)
is a binary function that takes inputsa,b
and returns[a,b,b]
./
(fork) pushes to the stack a ternary function that takes three inputsa,b,c
, and returns[b]
ifa
is a blank, and[c]
otherwise.$
(call) pushes to the stack a binary function that pops a functionf
and a valuex
, and appliesf
tox
exactly as!
does..
(chain) pushes to the stack a binary function that pops two functionsf
andg
, and returns their composition: a functionh
that has the same arity asf
, and which takes its inputs normally, appliesf
to them, and then fully appliesg
to the result.@
(say) pushes a unary function that simply returns its input, and prints0
if it was a blank, and1
if it was a function.
Note that all commands except !
simply push a value to the stack, there is no way to perform input, and the only way to output anything is to use @
.
A program is interpreted by evaluating the commands one by one, printing 0
s or 1
s whenever "say" is called, and exiting.
Any behavior not described here (applying a blank, applying a stack of length 0 or 1, calling "chain" on a blank etc.) is undefined.
The reference interpreter, written in Haskell, has the following extra features:
- Informative error messages.
- All functions can be referred to by their lowercase names, instead of the symbols. They must be separated by symbols or whitespace.
- All whitespace is ignored, except between lowercase words, where it acts as a separator. An uppercase letter begins a comment that extends to the end of the line.
Invoke it like
runhaskell shift.hs hello.sft