Function EXpression Language
Author: Patrick Chkoreff    PGP Key
Email: 0ca355c7@fexl.com

Clump

The Clump program is a handy tool for C programmers who are tired of makefiles. I wrote it way back in 2003, and a lot of people really appreciate it. Give it a try, and let me know what you think.

— 2010-07-24 16:59:40 UTC Patrick Chkoreff

Loom

A new concept in digital money. See loom.cc/faq for more information.

Fexl

Fexl™ is a brand new programming language based on pure functions.

The name "Fexl" (pronounced Fex' - il) stands for Function EXpression Language. It's an accurate name, because Fexl is indeed a language for expressing functions. As you will see, everything in Fexl is a function.

Fexl may one day become a powerful and secure basis for executable content on the internet. Java is a nice try, but Fexl is cleaner and more expressive.

For a quick overview of the features see the Fexl Summary.

Status update 2010-08-26

The Basics

RULE 1: EVERYTHING is a function.

This means everything, including data. A list is a function. A boolean value is a function. A number is a function. A string is a function. An entire program is a function. You name it, it's a function.

Exactly how data are represented as functions is the subject of another discussion (see below). Fexl uses the method described in the Jan 1989 issue of ACM Transactions on Programming Languages and Systems, in an article titled "Typed Representation of Objects by Functions," by J. Steensgaard-Madsen. Suffice it to say it's incredibly elegant.

RULE 2: Side effects happen.

Ultimately the entire purpose of a computer program is to interact with its environment -- in other words, to create side effects. This includes drawing on a screen, reading a keyboard and mouse, sending messages to other computers, moving robot arms, etc.

In spite of this, a Fexl interpreter has no side effects. Fexl only does symbol manipulation, reducing a function to a final normal form. At that point it is up to the programming environment to look at that normal form and see if it needs to draw something on the screen, get some input, play a sound, or whatever. So you can write code which looks very ordinary and procedural, like this:

        print "Hello, world\n";
        beep; beep;    # beep the speaker twice
        getline \line
        print "You typed: "; print line;
        exit;
      

However, the beauty of it is that functions such as "print" and "beep" and "getline" do not have hard-coded meanings, so that program fragment can run inside any kind of safe "sandbox" you like. The programming environment surrounding the Fexl interpreter could even use different sorts of definitions for print, getline, beep, and exit. This is very useful for creating test environments, or environments that limit what certain users can do.

With this kind of thing, it is actually possible to download a function from the internet and run it in a protected way that is guaranteed to be safe. Or, if that function is from a highly trusted source, you can run it directly "on the iron" so to speak, calling the operating system and hardware directly.

What Does Fexl Really Do?

Fexl is just a bunch of functions being applied to other functions to produce more functions. In that sense, a Fexl program is just "swimming on air." However, what it's really doing is serving as a general-purpose "sequencer" for an arbitrary set of primitive routines and data types, perhaps written in C or any other language. For some tasks I actually use a Fexl interpreter written in Perl!

The general idea of course is to do only the low-level stuff in the language of your choice, and migrate more and more of the serious application logic into your Fexl sequencer.

Why Isn't Fexl More Conventional, Like C, Perl, Ruby, Python, or even Lisp?

What would be the point? Those languages already rule at what they do. Fexl has to be different. But Fexl does not compete with other languages, it complements them. You can use any language you like and embed a little Fexl interpreter inside it. You can fold more and more of your code into Fexl if you like, completely at your leisure and discretion. Perhaps you will end up loving Fexl so much that none of your original code remains, except the low-level system functions and the Fexl interpreter itself. You will have achieved full functional enlightenment.

How Do Functions Work?

A function is something that is applied to an input value to produce an output value. In general you take a function f, apply it to an input x, and it produces an output y.

        f x = y
      

For example, you might apply a function called "twice" to the input 7, producing the output 14:

        twice 7 = 14
      

You can also have functions that take more than one input value:

        add 3 5 = 8
      

Actually, all functions take only one input. The add function only appears to take two inputs. In reality, when you apply add to 3, you get the function (add 3). The (add 3) function adds 3 to whatever input you give it. So the next step is to apply (add 3) to the input 5. This yields the final output 8.

The expression (add 3 5) is actually a shorthand for the fully parenthesized expression ((add 3) 5). The longer form makes it clear that we are applying the function (add 3) to the value 5.

Because function application is left-associative, we can dispense with any extra parentheses that group things toward the left. So these expressions are all equivalent:

        (((a b) c) d)
        ((a b) c d)
        (a b c d)
        a b c d
      

Of course, when you have grouping toward the right you cannot simply drop the parentheses. For example, you could not drop the parentheses in this expression:

        a (b c) d (e f (g h i))
      

However, I have devised a neat syntactical trick that lets you express right-grouping without parentheses. If you have something grouped off to the right as in (e f (g h i)), you can rewrite this as (e f; g h i). The semicolon acts as a "fulcrum" pivot point meaning "wrap parentheses around everything that follows." So all of these expressions are equivalent:

        a (b c) d (e f (g h i))
        a (b c) d (e f; g h i)       # right-pivot after the f
        a (b c) d; e f; g h i        # right-pivot after the d
      

The pivoting semicolon neatly expresses the notion of sequentiality in procedural programming. For example:

        print "hello"; print "how are you?"; exit 0
      

If we removed the pivoting semicolons from this function, we would have:

        print "hello" (print "how are you?" (exit 0))
      

That expression has the form (print string next), which prints the string as a side-effect and then returns the value next.

So here's what happens when this function is run. We demonstrate the behavior of Fexl by showing the reduction sequence starting from an initial expression marked with ':', and continuing to each subsequent equivalent expression marked with '='.

        :   print "hello" (print "how are you?" (exit 0))  # (prints "hello")
        =   print "how are you?" (exit 0)                  # (prints "how are you?")
        =   exit 0                                         # (exits with code 0)
      

Of course, it's more natural just to preserve the semicolons and view it as a simple procedural computation sequence:

        :   print "hello"; print "how are you?"; exit 0    # (prints "hello")
        =   print "how are you?"; exit 0                   # (prints "how are you?")
        =   exit 0                                         # (exits with code 0)
      

How are Functions Evaluated?

When you apply a function to an input, the result is a new function with the input "absorbed" or "incorporated" into it. You can think of the input value as being "substituted" into the function wherever the input name appears. Fexl doesn't actually use substitution, it uses combinatorics instead. Nevertheless, substitution is a very handy way of thinking about how functions are evaluated.

Let's say we have a function f defined as follows. Nevermind what it means; it's just a random example.

        \f=(\x\y x 3 (y x))
      

That function f takes two inputs x and y and produces the result (x 3 (y x)), where the x and y are replaced with whatever you supplied as inputs.

Let's say you apply f to the inputs A and B, where A and B are any arbitrary values. Here's the reduction sequence:

        :   f A B
        =   (\x\y x 3 (y x)) A B     # expand f
        =   (\y A 3 (y A)) B         # substitute A for x
        =   (A 3 (B A))              # substitute B for y
      

This sequence establishes that (f A B) equals (A 3 (B A)). Of course, A itself will also be a function, and the reduction sequence will continue from there.

For example, let's say A is the function (\p\q\r q r p). Then we have the sequence:

        :   A 3 (B A)
        =   (\p\q\r q r p) 3 (B A)   # expand A
        =   (\q\r q r 3) (B A)       # substitute 3 for p
        =   (\r (B A) r 3)           # substitute (B A) for q
        =   (\r B A r 3)             # drop left-associative parens
      

Note that at this point we cannot go any farther. We have reached an expression that insists on receiving an input called r. You can't go any further than this, unless you go ahead and supply a value for r. An expression that starts with an input name like this is called a normal form. For historical reasons, it is also known as a lambda expression.

Combinatorics

The core Fexl engine doesn't actually use substitution. Instead, it uses the principle of combinatorics first devised by a hero of mine Moses Schönfinkel in a landmark 1924 paper. But you can think of Fexl as using substitution.

But since we're on the subject of combinatory logic, here it is in a nutshell. All computable functions can be defined in terms of just two primitive functions C and S, defined by the following equations:

        C x y = x
        S x y z = (x z) (y z)
      

What's amazing is that this can be done without the use of variables. Any computable function you can possibly imagine can be built up by applying the functions C and S to each other in some combination.

One tiny little example of this is the identity function I which we might define in Fexl as:

        \I = (\x x)
      

But in terms of C and S we can define it without using any variables as:

        \I = (S C C)
      

The core evaluation engine does not have to reduce everything down to C and S. It can use higher level functions which theoretically can be reduced to C and S but in practice can be evaluated in a single large step, without going through all the unnecessary motions.

So If Everything is a Function, Then What is Data?

Always remember Rule 1: Everything is a function. This means everything, including data. You name it, it's a function.

To represent all data as functions, Fexl uses the method described in the 1989 article "Typed Representation of Objects by Functions," by J. Steensgaard-Madsen. The concept is simple once you get the hang of it, but subtle when you first see it. It is best illustrated by example. Here's a simple grocery list.

        item "apples"; item "cereal"; item "toothpaste"; end
      

Without the semicolon feature we would would have to express the list like this:

        item "apples" (item "cereal" (item "toothpaste" end))
      

In that expression you'll notice we use two functions called "item" and "end". The item function is defined as:

        \item = (\head\tail \end\item item head tail)
      

The end function is defined as:

        \end  = (\end\item end)
      

You are free to use names other than "item" and "end" if you like. Usted puede usar Español si le gusta. Una rosa por otro nombre es una rosa.

Here's how the function (item H T) is reduced:

        :   item H T
        =   (\head\tail\end\item item head tail) H T    # expand item
        =   (\tail\end\item item H tail) T              # subst for head
        =   \end\item item H T                          # subst for tail
      

Examining the List

To look at a list, you apply it to two "handler" functions, one to handle the end case and another to handle the item case:

        list do_end do_item
      

Of course, you don't actually have to define named functions do_end and do_item. You can put the handler functions directly inline after the list, as parenthesized expressions. But named functions are handy for the purpose of this discussion.

The end case

So here is how (list do_end do_item) reduces when the list is empty:

        :   list do_end do_item
        =   end do_end do_item                # expand list
        =   (\end\item end) do_end do_item    # expand end
        =   (\item do_end) do_item            # subst for end
        =   do_end                            # subst for item (not used)
      

So now computation proceeds with whatever do_end is.

The item case

Next, let's examine the case where the list is (item H T).

        :   list do_end do_item
        =   (\end\item item H T) do_end do_item    # expand list
        =   (\item item H T) do_item               # subst for end (not used)
        =   do_item H T                            # subst for item
      

Computation now proceeds with the do_item function applied to the head H and the tail T. You would write the do_item function with two inputs to grab the head and tail. For example, you might use something like this:

        \do_item=(\head\tail print_item head; print_list tail)
      

Now let's pick up where we left off above with (do_item H T):

        :   do_item H T
        =   (\head\tail print_item head; print_list tail) H T   # expand do_item
        =   (\tail print_item H; print_list tail) T             # subst for head
        =   print_item H; print_list T                          # subst for tail
      

We have now determined that (list do_end do_item) equals (print_item H; print_list T).

Example of a Function that Does Something with a List

Let's look at how the print_list function might be defined:

        \print_list=(\list\next list next \head\tail
            print_item head;
            print_list tail;
            next)
      

This function says that if the list is empty, simply proceed with whatever is next. But if the list has a head and tail, print the head item and then print the remaining list.

Here is how the print_list function might be used:

        print_list (item 1; item 2; item 3; end); exit 0
      

Let's look at how this function reduces:

        :   print_list (item 1; item 2; item 3; end); exit 0
        
        # First remove the ';' notation to make the functional structure very obvious.
        
        =   print_list
            (item 1 (item 2 (item 3 end))) (exit 0)
        
        # Now replace print_list with its definition.
        
        =   (\list\next list next
                \head\tail
                print_item head;
                print_list tail;
                next)
            (item 1 (item 2 (item 3 end))) (exit 0)
        
        # Substitute for the list input.
        
        =   (\next (item 1 (item 2 (item 3 end))) next
                 \head\tail
                 print_item head;
                 print_list tail;
                 next)
             (exit 0)
        
        # Substitute for the next input.
        
        =   (item 1 (item 2 (item 3 end))) (exit 0)
                 \head\tail
                 print_item head;
                 print_list tail;
                 exit 0
        
        # For clarity, let's put parentheses around the lambda expression (\head\tail ...).
        # We can do this because the scope of a lambda expression always extends as far
        # right as possible.
        
        =   (item 1 (item 2 (item 3 end))) (exit 0)
                 (\head\tail
                 print_item head;
                 print_list tail;
                 exit 0)
        
        # Remove unnecessary parentheses around the first function.
        
        =   item 1 (item 2 (item 3 end)) (exit 0)
                 (\head\tail
                 print_item head;
                 print_list tail;
                 exit 0)
        
        # Substitute the definition of the item function at the front, then
        # go ahead and reduce that to its normal form.
        
        =   (\end\item item 1 (item 2 (item 3 end))) (exit 0)
                 (\head\tail
                 print_item head;
                 print_list tail;
                 exit 0)
        
        # Substitute (exit 0) for the end input, which is not used.
        
        =   (\item item 1 (item 2 (item 3 end)))
                 (\head\tail
                 print_item head;
                 print_list tail;
                 exit 0)
        
        # Substitute the entire (\head\tail ...) for the item input.
        
        =   (\head\tail
            print_item head;
            print_list tail;
            exit 0)
                 1 (item 2 (item 3 end))
        
        # Substitute for the head input.
        
        =   (\tail
            print_item 1;
            print_list tail;
            exit 0)
                 (item 2 (item 3 end))
        
        # Substitute for the tail input.
        
        =   print_item 1;
            print_list (item 2 (item 3 end));
            exit 0
        
        # Now it's obvious we're on the right track.  We're going to print the first item 1,
        # then print the remainder of the list (item 2; item 3; end), and finally exit with
        # code 0.
      

Now that may seem like a lot of work to you, but it is incredibly fast when a computer does it.