An introduction to Hyperlambda

17 Feb 2022 - thomas

The purpose of Hyperlambda is to have a programming language that allows your computer to generate most of your code. However, sometimes you need to manually modify the generated code yourself, and/or create Hyperlambda code manually, at which point you’ll need to understand Hyperlambda. Hyperlambda is extremely easy to learn though, and you can probably teach yourself most of its basics in less than 10 minutes. This article is an introduction to Hyperlambda and gives you an overview of Hyperlambda from a bird’s perspective.

Structure

Syntactically Hyperlambda resembles YAML, however it has its own unique syntax, and even though it has the same readability traits as YAML, it is not the same. In theory we could have used YAML, JSON, or XML to create Hyperlambda, but that would increase its verbosity, resulting in more difficult to read code. However, its structure is easily understood in 5 minutes, since it is literally just a text representation of a tree structure. Hyperlambda’s structure is based upon “nodes”, and each node has 3 properties.

To illustrate imagine the following Hyperlambda.

.data
   foo1:bar1
   foo2:bar2

The above Hyperlambda consists of 3 nodes. The first node is called [.data]. This node has two children called [foo1] and [foo2]. Both of these nodes have a value each being “bar1” and “bar2”. The colon separates the node’s name and its value, and 3 spaces opens up the children collection. To play around with Hyperlambda you can use Magic’s “Evaluator” component. Below is a screenshot of a slightly more complex example.

The Hyperlambda evaluator

The above example is of course more complex than our first code snippet, but it still follows the exact same structure being name/value/children. In the above example the [while] node is given two children arguments; One condition node being its [lt] parts, and another lambda object being its [.lambda] parts. The [while] loop will execute its [.lambda] object for as long as its condition is true. The condition again is an [lt] condition, implying “less than”. Translating the above Hyperlambda to English hence becomes the following.

Execute .lambda while .no has a value less than 20

Inside [.lambda] we’re creating a log entry before we increment the [.no] value. Magic contains many similar conditions, such as “more than”, “equal”, etc - In addition to a lot of additional “keywords”. Refer to the magic.lambda documentation for an exhaustive list.

Code is data

The reasons why Hyperlambda is so good at creating and modifying code, is because there is no difference in Hyperlambda between “code” and “data” - Implying code is data. The same way we can modify data structures such as XML, YAML, or the HTML DOM for that matter, Hyperlambda allows for modifying its code. If you want to change the invocation to [log.info] in the above screenshot to [log.error] this is as easy as adding the following Hyperlambda to the top of your code.

set-name:x:../**/log.info
   .:log.error

This trait of Hyperlambda makes it very easy to create “self evolving code”, that somehow changes an existing snippet of code to do something completely different. This trait of Hyperlambda is crucial for its ability to automatically generate code and is the reason why it can with such ease create and generate code 100% automatically for you. When you think about “how weird” Hyperlambda is, please understand its reasons for being weird. Hyperlambda’s “weirdness” allows us to easily create templated snippets of code that we use as the foundation for some process automatically generating custom code, by parametrising our code dynamically, such that we can modify it according to our specific needs.

Everything is code

The natural realisation of the above is that all data is also code. This creates a problem for us since we might want to create nodes we don’t want to “execute”. This is achieved by starting a node’s name with a ”.”. This instructs the Hyperlambda execution engine that this node should not be executed but simply ignored by the Hyperlambda execution engine. This is what allows us to create “variables”, and/or nodes containing “data”. You can see examples of such nodes in our previous code snippets and screenshots.

Expressions

Hyperlambda doesn’t have “variables”. This is because everything is a variable in Hyperlambda, including function invocations, arguments to functions, etc. This creates a problem for us, being that we need some mechanism to modify node names, node values, and their children collection. This is where expressions comes into the picture. An expression allows us to reference any node in our Hyperlambda object. An example of such an expression can be seen in the above screenshot where our invocation to [get-value] has the value of :x:@.no. Its :x: part declares it as an expression type, while the @.no part is the actual expression. An expression is similar to chained LINQ statements in that it is a list of IEnumerable objects, that reacts upon each other in a chain. Below is a slightly more complex expression to illustrate the point.

.data
   foo1:bar1
   foo2:bar2
   foo3:bar3

set-value:x:../*/.data/*/foo2
   .:Hyperlambda was here

If you execute the above Hyperlambda in your Magic’s “Evaluator” component, you will see it changes the value “bar2” to “Hyperlambda was here”. This is because the expression we give our [set-value] invocation basically says the following.

Give me the root node, then all its children, then filter away everything not having the name of ‘.data’, find its children again, and filter away everything not having the name of ‘foo2’

When the expression is done filtering our nodes, we’re left with only the [foo2] node, at which point [set-value] changes its value. To understand expressions and type declarations in Hyperlambda you might benefit from reading about magic.node diving deeper into both expressions, iterators, and Hyperlambda’s typing system. However, think of expressions as “pointers into your Hyperlambda object”, where each pointer is composed from a chain of “iterators”, where each iterator is separated by a slash (/), and your expression as a whole can point to zero or more nodes.

Slots

Hyperlambda doesn’t really understand the idea of function invocations. Instead everything is a “slot” in Hyperlambda. However, for all practical concerns a “slot” is similar to a function invocation in a traditional programming language. Magic contains hundreds of slots for all sorts of scenarios, and in the documentation for Magic we often refer to these using square brackets in bold text. To modify parts of your Hyperlambda the following slots are your most important friends.

By combining the above slots you have everything you need to be able to change your Hyperlambda objects as they are executing, resulting in a Turing complete programming language, even though it technically doesn’t have neither functions nor variables. To see which slots are available you can click CTRL+SPACE on Windows or FN+CONTROL+SPACE on a Mac while your code editor has focus to open the autocomplete drop down. Below is a screenshot of the autocomplete drop down from Hyper IDE.

Hyperlambda autocomplete

Snippets

Magic’s “Evaluator” component contains a lot of Hyperlambda snippets illustrating some aspect of Hyperlambda. If you click the “Load” button you can load existing Hyperlambda snippets demonstrating some aspect of the programming language for you. The easiest way to start learning Hyperlambda is probably to go through these snippets, understand what they do, for then to apply similar constructs in your own Hyperlambda. The [while] loop in the first screenshot in this article is one example of such a snippet. You can also save your own snippets as you’re playing around with Hyperlambda. Below is a video where I demonstrate Hyperlambda and what you can achieve with it.

Orchestration

Hyperlambda is an “orchestration programming language”. This implies that it is a super high level language, intended for “orchestrating” programming building blocks. For these reasons some would argue it’s not a “real” programming language, which would be a correct assessment. If you’re creating quick sort functionality or polygon rendering algorithms in Hyperlambda, you’re doing something wrong. Hyperlambda is not intended for algorithm heavy snippets, even though technically it is possible to implement anything in it. To understand Hyperlambda’s position, realise its purpose is to sit between your algorithm heavy low level code, and the client, “orchestrating” your low level building blocks, giving you dynamic capabilities on your code as a whole.

Hyperlambda is for your code the same as YAML is for your pipelines, configurations, and Kubernetes cluster, in that it allows you to “configure” your code together, using declarative concepts, from a high level abstraction layer, where you don’t have to think about the internal details of your executing code. However, where YAML allows you to configure deployment of your applications, Hyperlambda allows you to “configure” your application instead of manually coding it using low level programming languages such as C# or Java. However, when that’s said, the entirety of Magic is actually created in Hyperlambda, implying its middleware, the IDE, the SQL editor, everything in fact, including the crudifier - And you can actually find this code inside your “system” folder if you’re using Hyper IDE to check out its code. If you’re using heavy recursion, lots of nested while loops, and dozens of temporary variables in your Hyperlambda code, you would probably be better creating this part of your code in C# and expose your C# code as a “high level slot” to your middleware instead.