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*.
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!
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 and xout are the result of
sig => proc
and xout.flat and xout are the result of
sig => proc assuming there are at least two processes
a * is born
Finding all the results in x that are processed sig is easy, it's just xout
Want to find all results in x that are processed by proc?
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[*] is the same as xout in this simple 2D example. For higher dimensions this is not the case.
Only want to process a subset?
sig => proc > xout
sig => proc
there's also .first and .last access methods for collections so
sig 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).
xout = proc <= x
xout < proc <= x
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:
- dynamic language to encourage meta programming. I desire a very flexible language
- dynamically typed objects, where just about everything is an object (like Ruby)
- syntactical brevity and clarity. It will be a pleasure to develop with and read later
- easy to begin using, but flexible and complex enough for experience hackers
- high level functionality and a rich default utility library
- has competitive implementation benchmarks with other "fast" programming languages (c, c++, java)
- asynchronous flexibility with optional high priority blocking synchronous code sections. Ideally the language can identify the optimal allocated level of concurrency and use threaded resources
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.
*= Not enough severe negative reinforcement is used in programming. Probe seeks to rapidly enhance best coding practices ;).