Atoms are the smallest variable types.
Numbers are the most basic of variables. In programming terms, they a doubles. If a number is an integer(not decimals) they are commonly represented without the '.0' on the end.
Chars(aka. Characters) are very similar to C-style char ints. They are commonly represented by the ASCII-value they correlate with. They hold a value between 0 and 255. If a number not between 0 and 255 is casted to a char, it's value will be modulo wrapped from 255(%= 255
).
Bools(aka. Booleans) are very similar to C-style booleans. They are commonly represented by either true
or false
. They hold a value of either 0 or 1. Same as a char, if a number that is not between 0 and 1 is casted to a bool, it will be module wrapped.
Nulls(aka. nil, void, undefined) are value types given to variables that have absolutely no value.
Sets are ways of containing, sorting, and orginizing multiple values.
Dicts(aka. Dictionaries) are very similar to JavaScript objects, or Python dictionaries. They hold multiple values of either atoms or other sets. For each value in the dict, there is a key associated to it.
Lists(aka. Arrays) are very similar to JavaScript arrays in the case that they are simply dictionaries that only use numbers for their keys. They are commonly represented by it's corresponding array notation, instead of just a dict using keys as numbers.
Strings are very similar to Haskell strings, in the case that they are simply arrays consisting of only chars. They are commonly represented by it's corresponding string notation, instead of an actual array of chars.
1 # notational value of 1 but literal value of 1.0
1.0 # notational value of 1(because it is a .0) but literal value of 1.0
3.14 # notational and literal value of 3.14
Standard convention is that you should not use a .0
if you are not intending a float/double style variable.
'a' # notational value of 'a' but literal value of 97(ascii)
# syntax for casting number to char is a prefixed grave symbol
`97 # notational value of 'a'
Single quotes are for single chars, where as double quotes are for strings.
'a' # value of 'a'
"a" # value of ['a']
true # notational value of true but literal value of 1
false # notational value of false but literal value of 0
# syntax for casting number to bool is a prefixed double-grave symbol
``1 # notational value of true but literal value of 1
Sets have two types of notation: declaration and retrieval.
Declaration is similar to python and JavaScript.
{"key":"value" "foo":1}
Retrival is the same for every set.
# To get the value of "foo"
{"key":"value"}["key"] # represents "value"
Lists can be declared by simply making a dict that only uses numbers as it's keys. The conventional way of making an array automatically assign a number key of the position.
{1:"something" 2:"foo" 3:"var"} # the unconventional way of making an array.
["something" "foo" "var"] # the conventional way of making an array
["element 1" "element 2" "element 3"][2] # "element 2"
Strings are very simple and as seen previously.
{1:'a' 2:'b' 3:'c'}
# is the same as:
['a' 'b' 'c']
# which is the same as:
"abc"
# and retrieval works too
"abc"[2] # 'b'
Another type of variable is a is lambda. The syntax for a lambda uses the def
function which(when defining a lambda) takes two arguements: arg set, value. The arg set is just an array of undefined variables that uses '()'s instead of '[]'s. The value is just code that eventually returns a value, but that code gets re-avaluated everytime the lambda is called. A lambda looks like:
(def (x y) x + y) # first argument is considered 'x' and the second is 'y', then it returns the value of x + y
Variables are a very powerful thing to have. The syntax for defining a variable uses the def
function which(when defining a variable) takes two arguments: name, value. The name is a string that represents what the variables name will be, and the value will be the permanently stored value of it.
(def "foo" 10)
Now later in your code, whenever you reference foo
it will represent 10. Like:
(add foo 5) # returns 10 + 5, 15
Functions are basically variables containing lambdas. Syntax is as straight forward as:
(def "add" (x y) x + y)
The if
function is pretty standard. It takes three args, the first argument is evaluated to a bool, if the bool is true, it returns the 2nd arg, if the bool is false it returns the 3rd arg or nothing if there isn't a 3rd arg.
Example:
(if true "true is true" "true is false") # returns "true is true"
Because the developer is very lasy and didn't want to implement something better, the both
, either
, not
and equal
functions are similar to &&
, ||
, !
and !=
.
(either false (both (not false) true)) # true
The self
variable represents the current lambda definition. Also the parent
variable represents the parent lambda, and the parentof
function returns the parent of the first argument.
So...
(equal (parentof self) parent)
Streams are represented by a number, and allow for reading and writing to them.
To create duplex stream which is strictly for in-program use, use the canal
function like so:
(def mystream canal) # mystream is now some random number
To write to the stream, use the pump
function like so:
(pump mystream "foo")
To read from a stream, use the scoop
function like so:
(scoop mystream) # returns "foo"
And you can optionally close streams with the clog
function like so:
(clog mystream)
(pump mysream "foo") # raises error
Write a string to a file:
(puke "foobar.txt" "foo")
Append a string to a file:
(spit "foobar.txt" "bar")
Read entire file to string:
(slurp "foobar.txt") # returns "foobar"
Read x amount of chars from file(third argument can represent the current position in the file):
(sip "foobar.txt" 3) # returns "foo"
(sip "foobar.txt" 3 3) # returns "bar"
Additionally, you can use streams for file reading and writing with the well
function like so:
(def myfile (well "foobar.txt"))
(scoop myfile) # returns "foobar"
(pump myfile "\nfoobar on line 2")
(clog myfile) # closes and writes file
Let's say you need enviroment variables or maybe command line arguments? Well the enviro
and lure
variables are at your service.
The enviro
variable is a dictionary of the current shell enviroment variables.
(enviro["SHELL"]) # /bin/bash or something of the sort...
The lure variable is an array of the command line arguments given at runtime. The array starts with the source file regarless of whether it was run like whip script.whp
or ./script.whp
. Remember: lists start at 1.
# ./script.whp foo
(lure[1]) # "./script.whp"
(lure[2]) # "foo"
Although you can just use the /dev/std*
files the same, the stdout
, stdin
, and stderror
are addresses for file reading and writing. But you should use them if you want platform independant code(They can be changed depending on your OS/Enviroment).
A simple unix-like concatenation program.
(puke stdout (slurp stdin))
Something not in the standard library? That's fine. Just use the require
or import
functions.
The require
function imports an imported library for confining to a variable, whereas the import
function will import a library into the namespace(use import sparingly to avoid flooding). For example:
(def "JSON" (require "json"))
(JSON::parse "{\"name\":\"bob\", \"age\":20") # {"name":"bob" "age":20}
(import "json")
(parse "{\"amount\":10}") # {"amount":10"}
Sockets are just like duplex streams, but with networks and all that. To create a socket, use the plug
function like so:
(def mysock (plug "www.google.com" 80)) # mysock is now some random number
Sending stuff through the socket with stream pumps and scoops.
(pump mysock "GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n")
(scoop mysock) # returns string of the entire HTTP query with the HTML and everything.
(clog mysock)
Luckily, there is an HTTP function set that automates the whole thingy above.
The tour
function takes a URI string as it's first argument and returns a string of the recieved HTTP file:
(tour "http://jsonip.com")
# returns "{"ip":"255.255.255.255",about":"/about","Pro!":"http://getjsonip.com"}"
I chose to use jsonip.com since it had an index page that is really simple.