2. What is the Bund ?
the Bund, is a simple, stack-based functional programming language. It’s written and tightly
integrated with Python 3. Bund is extendable with Python 3 and can be embedded into any
Python-based libraries and applications, providing ability to have a scriptable interface for
your code. You can custom tailor the codewords of the language to your taste. So, take a
look at some of the basic features supported by the Bund :
❏ Functions are the “first-class citizens”.
❏ Application-wide namespaces, instead of modules.
❏ Separation of the mutable (runtime) and immutable (default) data.
❏ Delayed execution.
❏ Direct (with help of delayed execution) and reverse (default) stack coding.
❏ User predefined input/output channels, called “pipes”
❏ Partial application
❏ “Code as a data”
❏ Capability to create a self-modifiable application with dynamically created functions
and a scripts.
❏ and much more ….
3. About me
My name is Vladimir Ulogov and I’ve been around awhile. Been a Linux user before
a RedHat. Started to use a Python, when it was version 0.95. I am having an “Erlang OTP
documentation” printed in 1999, because I was an Erlang developer at the time and
actually still like to do an Erlang code once in a while. I do know, what is the CLIPS, and
spent a great deal of time, programming an expert systems using CLIPS and Lisp. I’m
beginning to get interested in DSL languages a long time ago. My first “handwritten”
parser was for a Snobol language, crafted for RSX-11 operating system running on PDP-11,
with a help of the MACRO-11. I’ve created a DSL, for a Zabbix with some help from Hy
language and it’s threading macros.
But I’ve been interested to a possibilities provided by a PEG parsers for a while. So,
in order to contradict the saying “You can't teach an old dog new tricks”, I’ve decided to
teach myself to how to use a PEG parser and create a language with it help. So, here is
what I achieve so far. So, be with me, there will be more. If you would like to get in touch
with me, here is an e-mail address: vladimir.ulogov@me.com And my LinkedIn profile, also
can be used to communicate with me.
https://www.linkedin.com/in/vladimirulogov/
4. Show me ...
The first, foremost and classic. Exists in any language. The all time famous and everyone’s
favorite, the most loved “Hello world” program.
5. Explain to me ...
Before we continue, let us review what we can deduce about the language from the “Hello
World” program.
Here is the definition of the
namespace called “/Main”. We will
talk about namespaces later on.
Here is the end of the /Main
namespace
Inside namespace /Main, we
defining a variable, called “Main”
This is literally says: That assigned
to that variable. And this is the
lambda function, that we can refer to
using that variable.
We will delay-execute function
println with a string argument
containing “Hello world!”
6. I know, you are bit confused, but ...
This, very simple program introduces some very important concepts of the Bund.
“Delayed execution”, “namespace”, lambda and “where is my equal sign” for an
assignment. Have no fear, underneath, the Bund is a very simple language. It’s bears some
similarities to a several languages of the past.
From the LISP, it takes a lambda calculus, “code as a data” and many other important
aspects of functional programming. Forth gave to the Bund it’s stack-based virtual machine
and coding paradigm. Haskell gave a partial application, immutable objects and
abstractions. And a Python, gave it’s runtime and a PEG parser.
7. Namespace
First, very important feature of the Bund is a “Namespace”. Namespace is an element
of the hierarchical tree, which can contain a references to a values or another namespaces.
Think about Namespace, as a directory, referred by it’s name and refereeing another files
or directories. Namespace referred by the name which looks like this: /This/Is/a/Namespace
❏ Namespaces can be declared inside another namespaces, but it doesn’t make them
nested. Namespace are strictly defined by it’s id.
❏ Namespaces refer another namespace or variables.
❏ Namespaces are implemented as nested dictionaries.
❏ When the Bund virtual machine creates, there are several predefined namespaces
are created:
❏ __main__ - default main namespace
❏ __script__ - namespace for the script executed outside namespace
system
❏ /sys - this is system namespace. Do not mess with it, unless you know
what you are doing
…. And several others
8. Namespace
Here is an example of the Bund Namespace creation.
Defining Namespace /Main
Even if we are defining Namespace
inside another namespace, the
hierarchy is defined by the
Namespace ID. In this example,
Namespace /config is not nested to
the /Main. It is very separate
Namespace.
But /Main/Program namespace are
nested to /Main, although you can
declare it inside or outside /Main
9. Namespace
Here is an example of the Bund Namespace creation.
Defining nested Namespace
/Main/Program
You can declare the same
namespace multiple times. They will
be merged.
This immutable data is local for the
/Main/Program
This immutable data is local for the
/Main/Program too
This immutable data is local for the
/Main Namespace
10. Assignments
You already saw in a previous examples that assignment of the data as an
immutable elements of the Namespaces, doesn’t looks like what you are accustomed to
see in Python or in many other languages. It is more look like a definition of “what goes
where”
Here in the Namespace /Main we
are declaring that the name /Main/Pi
will be forever 3.14
and in the same Namespace, we
are declaring that’s 42 will be
assigned to an Answer. And the
/Main/Answer will be forever 42
If <- does not aesthetically pleasing
you, the word “is” also could be
used. /Main/Hello forever is “World”
you can use “as” to reverse
directionality of the assignment,
instead of -> . Here 0.0 will be
known as /Main/Zero perpetually.
11. Assignments
The Bund is a dynamically-typed language. Although, it treats an assignment of
the type as permanent. Once you are assign a Name in a Namespace, you can not change
it’s value. Once you assigned a Run-Time Variable (RTV will be introduced in 0.0.2), you
can not change it’s type. Language supports three basic data types, as you might expect.
Integers
Floats
String
12. Assignments
You can also use a special, Key-Value data type, defined as a pair of the
elements, where the first one is Key and a second one is a Value. This pair is enclosed in “{“
“}”
As usual, in the Namespace /Main ...
we are defining the Key-Value pair
under local name “theKV” and also
visible as /Main/theKV
13. Assignments
The next data type available to you, is called “List”, but it is different from the
common list types that you’ve may been exposed before. This data structure in fact , closer
to the hash tables (and implemented as the dictionary at the lower level). The elements of
the list could be either positional, or keyed. Positional arguments, referred by the number of
it’s position in the list. The keyed elements are referred by the the key, from the Key-Value
pair.
The “List” data is defined as the sequence of the comma-separated data elements,
enclosed in “|“ “|”. The List could be empty.
14. Assignments
Here is some examples of the List structure
This is an example of the positional list,
where theList[0]=1, theList[1]=2,
theList[2]=3, theList[3]=4 This is also an example of the List with
positional elements, and yes, you can
mix the types of the elements in the list.
This is an example of the List, mixing both, keyed and a positional elements:
alsoList[1]=41, alsoList[2]=40, alsoList[“Answer”]=42
15. Stack
In order to understand of how the Bund Virtual Machine works, I do need to
introduce you to the “Stack”, which is the heat of the Bund VM, and the VM Arguments -
the detail of how the Language processing parameters to the functions and lambdas.
The stack is a common unlimited LIFO (Last In First Out) data structure, where
“unlimited” means that you can store as much data in the stack, as your memory permits.
There is no limitations in the data types that you can place on Stack. There are only two
exceptions, that changes behavior of the Stack-based VM: semicolon “;” and a percent
“%”. We will talk about them later on.
Naturally, the Bund supports RPN (Reverse Polish Notation), such as
argumentN … argument2 argument1 function
But you can change that behavior with the help of “delayed execution”, which
we will discuss later.
16. Lambda
Lambda function, is a function definition that is not bound to an identifier. The
Bund, defines a lambda expression as a list of the instructions for the Stack VM enclosed
between “(“ and “)”
This is a lambda expression, containing two integers that will be pushed to a
Stack and then passed to an execution to a function + as parameters.
17. Functions
Function which become a bound to a identifier in a Namespace, ceased to be a
Lambda, but retain a possibility to have a lambda functions, as a stack values.
We are defining a Lambda
function within /Main
namespace and binding it
with identifier /Main/Main
Placing two numbers on the
Stack and execute function
+ Result of the computation
will be placed on the Stack.
Here is our Lambda function
defined inside another
function which will do the
following math operation:
6 x 7
We are performing a -
(minus) operation with the
values stored in the Stack.
Result of the operation are
stored in Stack
Comparing two numbers
and placing result on the
Stack
18. Arguments separation
All data elements on the Stack are treated as the argument values for the
functions. But oftentimes, we may have a need to separate computation in groups. In the
Bund language, there is no brackets to enclose specific arguments, but you can use either
of the two approaches to achieve an argument and computation separation.
One method, is to use lambda function declared inside function. In the previous
example, computations of the 41 1 + and 6 7 * are completely independent from each other.
Performing calculation inside lambda function effectively placing “Arguments separator” by
default on the stack, so, inside a one lambda, there will be a limit, on how much data that
you can acquire from the stack.
Another method is to place an “Argument separator” on the stack. What is an
“Argument separator” ? As a token, it is looks like a percent sign - “%”
19. Arguments separation
To better understand, on how Argument separator works and how we can split
Stack into a parts, we do need to learn how the Bund forming the list of the arguments for
the function.
2Empty 3 +
Three numbers will be stored
in the stack in a reverse
order.
4
Once, we detect that we have a reference
to a function (or the function itself) in the
Stack, first, we are forming the list of the
arguments for that function.
For that, VM start to read a values from the Stack, until it meet certain condition.
In this example, until Stack is empty. Then normalize the Arguments from RPN and passes
them to a function.
20. Arguments separation
Like that ….
23+ 4
The outcome (if any) will be pushed by the function back to the Stack. But what
if we wanted to perform an operation + only with 4 and 3 and perform multiplication with
the result of the previous computation and number 2. When you are working with the
languages descended from an Algol, you are using braces to surround the computations.
Just like that ( 4 + 3 ) * 2 . Stack-based VM dictates you a different approaches. You do
need to somehow limit the forming of the list of the Arguments.
21. Arguments separation
We can use the Lambda functions, like that: ( 3 4 +) 2 *
3+ 4
2Empty 3
+
4
*
In our Stack-based VM, this will be
converted to a following functions call
To
Stack
* 2
From
Stack
To
Stack
22. Arguments separation
Or the arguments separator, like that: 2 % 3 4 + *
3+ 4
2Empty 3 +4 *
To
Stack
* 2
From
Stack
To
Stack
%
Once we start to form the Arguments list for a
function +, we will stop once we get to the % and the result
of the previous computation along with the rest of the Stack
will be an Arguments for a second function - *
You can use ether method for an argument
separation, pick the one which fits your coding style.
23. Global Lambda
But what if we do not want to limit us from restriction placed on the stack, when
we decent into a Lambda function from another Lambda function ? For that, we have to
place “the Globetrotter” function as the first call in the Lambda. What this function is doing,
it’s removes an Arguments separator from the stack, placed there by VM before it enters
into a Lambda.
This, “Globetrotter” function
call removes separator from
the Stack and makes 2
available inside the Lambda
function.
23+ 4
So, this is essentially the same as to call a
function + with parameters picked up
from within and outside of the current
Lambda function
24. Delayed execution
There are an alternative to a RPN syntax that we discussed before, will be a
“delayed execution”. The idea behind “Delayed Execution” is very simple. When VM detects
a Delayed Execution Function reference, inside the Lambda function, it does not trying to
execute it, but placed this instruction to the stack. “Delayed Execution Function reference
looks like a column “:”, followed by reference to a function. All consecutive elements in the
Lambda, will be treated as usual. There will be no difference from the RPN, until we meet
“Delayed Execution Command”, represented by semicolon “;”. When VM detects this
command, it is start to form an Argument, until it hits the reference to an actual function,
placed in the stack before. Then function is called with passing a formed arguments. If you
do not discover “Delayed Execution Function reference”, the Error will be placed on the
Stack and execution will be considered unsuccessful.
25. Delayed execution
Look at the example ….
We are declaring, that the function +
will be a “delay executed” with an
arguments as 4 3 2 . Please note,
unlike RPN, arguments are passed in
there “natural order”.
When we reach the “Delay Execution
Command, only then the list of the
arguments will be formed and function
will be discovered and executed.
26. Delayed execution
Delayed execution provide you a very interesting coding opportunities. One of
them, that the function you are planning to execute, may or may not exists. You may
dynamically create a function that you will execute inside your “Delay Execution statement”.
Of course, you can also freely use the Lambda Function for your “Delay Execution”. When
we declare that the Bund treats Code as a Data, we are really mean it.
27. Delayed execution
Let me show you an interesting example of how you can use an anonymous
function, or Lambda in the delay execution. This is almost looks like a Partial Application
(which we will discuss next). And of course, you can “Delay Execute” in all cases, where the
Partial Application is most optimal.
Here, we are using a Lambda function
for a Delay Execution. Since the first
“word” in the Lambda is “Globetrotter”
call, then Stack will be open for that
Lambda
This is an essential equivalent
of executing function + with a
following Arguments
(+) ( 1 2 3 10)
28. Partial Application
Partial Application is a process of fixing a number of arguments to a function,
producing another function of smaller arity. Partial Application is very similar to a Curry
Functions. In the Bund, I’ve implemented something, which is kind of between. Like a Curry
Function, Curry data type is a reference to a function, bound with one parameter. Unlike
Curry Function it will not produce a new lambda function. It will create a special structure,
instructing VM to interpret it as something, which is calling a function with incomplete
parameters. Like a Partial Application, the Bund does not create a new function. And unlike
a Partial Application, if you are using a Curry data type, you are restricted to a single
Argument only. Just like Curry function, without actually create a new function. If you are
looking for a Partial Application with multiple arguments, then you are looking for the use of
the Lambda function in “Delayed execution”, discussed earlier.
29. Partial Application
Cutty data type defined by the “dot” (“.”) separated pair, the first element of the
pair is a reference to a function, the second one is a first parameter passed to that function.
The pari enclosed by the brackets. Remember, no matter what, this will be the first
parameter passed.
Here, we are “Delay Execute” a Partial
Application of function + with the first
Argument defined as number 10 and
consecutive arguments 1 2 3
This is an essential equivalent
of executing function + with a
following Arguments
(+) ( 10 1 2 3)
As you see, the principal
difference between Curry and
the Lambda is location and
application of the first
Arguments. Also, all Curry are
Global (i.e. have an access to a
full Stack) by default.
30. Conclusion
The Bund at this present time is not very useful. It is remaining to be my
experiment and training ground on how to use PEG parsers and which features of the
functional programming are easy to understand and if they are practical. I will add more
features to a language as I will see fit and will try to find a good use of the Bund in my future
projects.
The syntax of the language also will experience a changes. There are many new
features that I can think of and yes, life is short and ideas are plenty. If you like to participate
in the development of the Bund, feel free to check out the code and propose your additions.
Cheers !
https://github.com/vulogov/py-bund
Vladimir Ulogov