Victus Spiritus

home

Probe: A fictional programming language

26 May 2010

This morning's riff is a beginning for a new programming language which may end up being a dialect or library of an existing language after I take a look at what I've written. I'll begin with an example of how I'd like code to look and function, and then wrap up with some design priorities and benefits. I would like a language that makes the process of learning easier and more streamlined. Probe stands for anything you want. At the moment for me probe stands for PROgram By Essel, PROgram BE, Produce Relational Ontologies By Example, and/or Pattern Refined Organization Born Easily :).

The following program will access an array of numerical signals (images, sound clips, etc) and identify an array of signal parameters. The specific example will be a list of images from the web.


[url1, url2, url3] => Stats

The brackets suggest a collection of things to be handled by the Stats object. The => isn't quite a passing operator. What it means is map what's on the left into the inputs of what's needed on the right. I like to think of it as the handshake, I hold out my hand and you figure out a way to shake it. A default flow will take priority if there are multiple mappings possible (with a warning?) and if no mappings are possible an error is raised and a doomsday countdown is initiated*.

Implicit Loops

Lets keep inventing as we go by looking into the Stats code.


Stats
     init( sig, proc )
         sig => proc > xout
     end
 end

Note that sig and proc can both be collections. The interpreter/compiler will determine if sig or proc are collections and expand code as needed. When doing automated tasks with programs, we tend to work with sets of things and run them all. We also want it to be trivial to process subsets when desired. My motto: Get our hands dirty now with a little upfront work to provide elegant readability to ourselves time and again later on. Can reading code be fun? I think so!

So
sig => proc > xout
Is equivalent to:

 

sig.each x
     proc.each y
         x => y >> xout
     end
 end

Let's spend some time looking at collections of collections. xout should have easy single index or multiple index access. The trick is that the output collection structure is determined by the looping on the fly.
So both xout.flat[0] and xout[0][0] are the result of
sig[0] => proc[0]
and xout.flat[1] and xout[0][1] are the result of
sig[0] => proc[1] assuming there are at least two processes

a * is born

Finding all the results in x that are processed sig[0] is easy, it's just xout[0]
Want to find all results in x that are processed by proc[0]?
xout[*][0]
It's incredibly liberating writing a programming language based on how I want it to be used without any concern for the hell I'll have to go through later to implement it. In a moment of need, a wild card is brought into the probe language. And of course xout[0][*] is the same as xout[0] in this simple 2D example. For higher dimensions this is not the case.

Process Subsets

Only want to process a subset?
Try
sig[0] => proc > xout
Or
sig[0] => proc[0]
there's also .first and .last access methods for collections so
sig[0] is sig.first and
sig.last is sig[sig.dim-1]

I like the idea of allowing symmetry in the data/processing flow if it's not too tricky to parse. So that:
x => proc > xout
is equivalent to
xout < proc <= x
The assignment operator = is the equivalent of a <, but not > (equals only works by assigning left).
So:
xout = proc <= x
and:
xout < proc <= x
are equivalent.

Let's dig a layer deeper with the process object


Norm : Process
     init sig
         sig => go > xout
     end
     operator <= sig          
         sig.vector      
     end      
     go sig          
         sig => Std > xout
         sig => Sub(xout.mean) > temp
         temp => Div(xout.sigma) >> xout
     end
 end

init is a standard constructor name.
Add, Sub, Mult, Div, Std (Standard Deviation), and Norm (Sigma Normalization) are all standard library operators which can be overloaded and customized (all derive from Process, single inheritance as in Ruby is groovy with modules/mixins)

Benefits of the Language

There are many existent languages which share some or all of the aspects I'll outline below (i.e. Lisp is close), but there is a level of intimacy that comes from writing your own language that I wish to explore. The path forward is to imagine how it would look and work, and then back fill in the details as needed. Some high level language design priorities:

This is a top down approach to programming language design. None of the backend or implementation has been written nor have I have spent much time thinking over the advantages of JVM implementations, a C/Assembly backend, vs a dialect of Lisp.

Notes:
*= Not enough severe negative reinforcement is used in programming. Probe seeks to rapidly enhance best coding practices ;).