The HaPyLi Programming Language

Section 1: Functions

Hello World

Let's start with a simple "hello world" application to illustrate the HaPyLi syntax.


    File: hello.hpl
    
        import "stdlib/base.hpl"
        
        def main() = (print-string "Hello, world!")

        ; Program comments are denoted by the semi-colon character,
        ; just as in LISP. 

All programs must define a "main" function (lower case) with zero parameters. The body of any function consists of just a single expression. In the above example, "print-string" is an imported function of one parameter.

Function names may contain any of the following characters:

  • ~ ! @ # $ % ^ & * \ - _ + \ | : , < . > / ?
  • any digit
  • any letter

Power

The next example defines a recursive function which accepts two arguments "x" and "y" and returns the value of "x" raised to the power of "y". (Yes, this is a bad example because 'power' goes into an infinite loop if "y" is less than 1). Notice that there are no commas between function parameters.


    File: power1.hpl
    
        import "stdlib/base.hpl"

        def power(x y) = 
            (if (== y 1)
                x
                (* x (power x (- y 1))))

        def main() = (print-number (power 2 10))

Local Variables / Let Forms

Any function can contain one let-form before the main body to define local variables.


    File: power2.hpl
    
        import "stdlib/base.hpl"

        def power(x y) = 
            (if (== y 1)
                x
                (* x (power x (- y 1))))

        def main() = 
            let 
                x = (read-number)
                y = (read-number)
            in
                (print-number (power x y))

Function Overloading

HaPyLi does support a limited form of function overloading. Two functions are considered unique if they have either different names or if they accept different numbers of parameters.


    File: params.hpl
    
        import "stdlib/base.hpl"
    
        def f (a b) = (print-string "Two Parameters")
    
        def f (a b c) = (print-string "Three Parameters")
    
        def main() = (f 1 2 3)

Inline Functions

Due to the nature of the Whitespace virtual machine, ordinary function calls are extremely inefficient. For each "call" instruction in your program, Whitespace will search through every instruction in the entire program to find the appropriate label to jump to. To avoid this overhead, you may define functions to be inlined. Calls to inlined functions do not emit a "call" instruction, but are instead replaced by a copy of the entire function's body. The only restriction is that inlined functions cannot be recursive.

I don't know of any method by which to determine whether a function should be inlined or not. Inlining a function will definitely increase the compiled program's size, so it may either increase or decrease overall performance.


    File: inline.hpl
    
        import "stdlib/base.hpl"

        ; Because "sum" is marked as inline, calling it 
        ; is no different than calling '+' directly. 
        ; They will compile exactly the same.

        def inline sum (a b) = (+ a b)

        def main () = (print-number (sum 1 2))

Function Parameters and Return Values

Arguments to a function are always passed by value and every function returns exactly one value. Furthermore, a function's parameters and local variables are read-only. The values stored within them cannot be changed during the function's execution. These limitations can be overcome, however, by defining global variables or allocating arrays on heap memory and passing/returning pointers to these arrays. These techniques are discussed later.