install factor and then rlwrap factor boil.factor
functions are postfix, i.e. xF
represents calling F with x as an argument.
3- ;; -3
4% ;; 1/4
4.2_ ;; 4 (floor)
5! ;; { 0 1 2 3 4 }
application is left associative, so you can chain functions easily
4.2-_- ;; 5
5!- ;; { 0 -1 -2 -3 -4 }
boil uses currying for functions with multiple arguments. 1+
is a function that adds one to the argument, and you can call it like this:
1 1+ ;; 2
3 4+ 5+ ;; 12
5 3+ 4-+ ;; 4
see that boil uses whitespace to separate expressions visually. we could also use parentheses, like 5(3+)(4-+)
. but that looks very ugly.
you can use ,
(concatenate) or !
(iota) to build lists. strings are lists of codepoints.
1 2, 3, 4, 5, ;; { 1 2 3 4 5 }
5! ;; { 0 1 2 3 4 }
"hello" ;; { 72 69 76 76 79 }
or use .
as special syntax, that takes all expressions in the same depth and puts them in a list
1 2 3 . ;; { 1 2 3 }
1 2 3 . 4 5 6 . . ;; { { 1 2 3 } { 4 5 6 } }
1 2, 3, ] 4, 5, 6, ] , ;; { { 1 2 3 } { 4 5 6 } }
mathematical operations pervade into lists:
5! 1+ ;; { 1 2 3 4 5 }
5! 1 1 0 2 1 . * ;; { 0 1 0 6 5 }
there is also x F/
fold, that builds up a value by calling the function F between every element in x.
4 5+ 6+ 2+ 1+ ;; 18
4 5 6 2 1 . +/ ;; 18
numbers are functions that index into lists. these indices are cylic, so 1-
gets the last element
0 1 4 9 16 25 . 4 ;; 16
3 9 . 5 4 . . 1 0 ;; 5
"bees" 4 ;; "b"0
"bees" 1- ;; "s"0
and when lists are called, they call each element with the same argument:
4 1+ - 2* . ;; { 5 -4 8 }
this means that rearranging items is the same as applying a permutation as a function:
"catnip" 4 5 6 1 2 3 . ;; "nipcat"
with this, we can implement many useful list operations. rotating a list is as easy as adding a number to the domain (x#
is length), and calling it with the original array:
"catnip" "catnip" # ! 2+ ;; "tnipca"
we can even use lambdas to turn this into a function. x. y
is a function that binds the argument to the name x
and calls y
.
"catnip" l. l l # ! 2+ ;; "tnipca"
because of currying, you can just have multiple x. y.
s to take multiple arguments.
"catnip" 2(n. l. l l # ! n+) ;; "tnipca"
still, boil has a bunch of combinators you can use to avoid writing lambdas. here's a list! they also have letters if you're that kind of nerd
name | usage | output | letter |
---|---|---|---|
id | x; |
x |
I |
swid* | F x[ |
xF |
T = CI |
swap | x y F` |
y xF |
C |
self | x F^ |
x xF |
W |
compose | x F G: |
xFG |
B |
const | x y@ |
y |
K |
*: real name pending
we can turn l # ! n+
into a single function call by composing :
all the functions together:
"catnip" 2(n. l. l l # !: n+:)
and remove the two mentions of l
using self ^
:
"catnip" 2(n. l. l # !: n+: ^)
a lambda that just consists of the argument being called with a function is the same as the function itself, so we can reduce this into:
"catnip" 2(n. # !: n+: ^)
we can still reduce this to not use any lambdas at all. if you play with the combinators for a bit, you might arrive to something like this. F G :`
is reverse compose, i think of it as adding a step to do before the function (get the domain Before we add)
"catnip" 2(n. n + # !: :` ^)
turning this into a function just means composing all functions together (because of boil's syntax, this means adding a :
to every function but the first one)
"catnip" 2(+ # !: :` : ^:) ;; "tnipca"
with these combinators you can build other combinators, if you feel like it. here is the infamous S combinator (x F GS => xF xG
, or λgfx.gx(fx)
)
G. F. x. xF xG ;; note how the args are listed backwards
G. F. x. x x F G` ;; x yF => y x F`
G. F. x. x x F G`: ;; xFG => x F G:
G. F. x. x F G`: ^ ;; x xF => x F^
G. F. F G`: ^ ;; η reduction
G. F. F G`: ^: ;; xFG => x F G:
G. G`: ^: ;; η reduction
G. G ` : ^: ;; (space things out)
G. G ` :: ^:: ;; xFGH => x F G: H:
` :: ^:: ;; yay
and we can use like this! the syntax for lambdas also works for assignment:
` :: ^:: S. 4 ! ,S ;; { 0 1 2 3 4 }
;; 4 ! ,S === 4! 4,
usage | name | example |
---|---|---|
x! |
iota | 5! ;; { 0 1 2 3 4 } 5-! ;; { -1 -2 -3 -4 -5 } "string" 4! ;; "stri" 2 3, ! ;; { { 0 1 } { 2 3 } { 4 5 } } |
x# |
length | 12 34 56 . # ;; 3 "string"# ;; 6 4# ;; 1 |
x$ |
reverse | "string"$ ;; "gnirts" 1$ ;; { 1 } |
x% |
reciprocal | 3% ;; 1/3 6 3%* ;; 2 |
x y& |
match | "meow" "meow"& ;; 1 "woof" "meow"& ;; 0 |
x f' |
each | 3 1 2 . !' ;; { { 0 1 2 } { 0 } { 0 1 } } |
x y* |
times | 1 2 3 . 4 5 6 . * ;; { 4 10 18 } |
x y+ |
add | 1 2 3 . 4 5 6 . + ;; { 5 7 9 } |
x y, |
concat | 1 2 3 . 4 5 6 . , ;; { 1 2 3 4 5 6 } "word" 32, ;; "word " 0 1 4 9 . , ;; { 0 1 4 9 } |
x- |
negate | 0 1 2 . - ;; { 0 -1 -2 } |
x f/ |
fold | 2 3 4 . +/ ;; 9 2 3 4 . */ ;; 24 1 2 . 3 4 . 5 6 . . ,/ ;; { 1 2 3 4 5 6 } |
x f g: |
compose | "cats" # !: ;; { 0 1 2 3 } 1 2 3 . 1(- +:) ;; { 0 1 2 } |
f g; |
id | 0; ;; 0 1 -; ;; -1 |
x y< |
less | 4 5 6 . 5< ;; { 1 0 0 } |
x y= |
equal | 4 5 6 . 5= ;; { 0 1 0 } |
x y> |
greater | 4 5 6 . 5> ;; { 0 0 1 } |
x? |
where | 0 0 0 1 0 0 1 0 1 1 0 . ? ;; { 3 6 8 9 } 0 1 3 0 2 0 4 . ? ;; { 1 2 2 2 4 4 6 6 6 6 } |
x y@ |
const | 1 2@ ;; 2 |
f x[ |
swid | 3 "hello"[ ;; 'l' + 2[ 3[ ;; 5 |
x f\ |
scan | 4 2 5 3 2- 3 . +\ ;; { 4 6 11 14 12 15 } |
x] |
wrap | 3 4 5 . ] ;; { { 3 4 5 } } |
x f^ |
self | 4 *^ ;; 16 |
x_ |
floor | 4.5_ ;; 4 |
x y f` |
swap | 1 2 3 . 4 ,` ;; { 4 1 2 3 } |
x y{ |
max | 4 5 6 . 5{ ;; { 5 5 6 } |
x y f| |
zip | 1 2 3 . 4 5 6 . ,| ;; { { 1 4 } { 2 5 } { 3 6 } } |
x y} |
min | 4 5 6 . 5} ;; { 4 5 5 } |
x~ |
not |
same as 1 2 0 4 0 5 0 6 . 0= ~ ;; { 1 1 0 1 0 1 0 1 } |
xSin
xCos
xTan
xAsin
xAcos
xAtan
xSqrt
xRound
xExp
xLn
do exactly what you expect them to dob nPow
is b to the power of npi
is pisWrite
writes a string to stdoutsPrint
writes a string to stdout with a trailing newlinexOut
prettyprints x
how do multiline files ?
- i want to do something smart with indentation, to go along with the spacing-based precedence, but i'm not sure exactly how that would work
how 2 sort?
- implementing one is definitely possible, but unwieldy
Grade^
would be very nice
unbounded recursion is non-trivial. fixed-point combinators?
identifiers with numbers would be cool i think
keep state in the repl
- set variables by having
name.
at the end ?