magic.lambda

magic.lambda is where you will find the “programming language keywords” of Hyperlambda. It is what makes Hyperlambda Turing complete, and contains slots such as [for-each], [if], and [while]. If you want to learn more, this is probably where you should start.

Notice, you do not need to master these slots to use Magic - the Hyperlambda Generator writes the code for you from plain English. This reference exists for when you want to read, understand, or hand-modify what the machine created.

Reference documentation

The slot reference is organised by concept, with one page per concept.

Hyperlambda internals

Everything is a slot in Hyperlambda. This allows you to evaluate and extend its conditional operators and logical operators the same way you would evaluate or create a function in a traditional programming language. This might at first seem a bit weird if you come from a traditional programming language, but has a lot of advantages, such as allowing the computer to look at the entirety of your function object as a hierarchical tree structure, parsing it as such, and executing your lambda object as an “execution tree”.

In a normal programming language, the equal operator must have a left hand side (lhs), and a right hand side (rhs). In Hyperlambda this is different, since the equal slot is the main invocation of a function, requiring two arguments, allowing you to think about it as a function. To compare this to the way a traditional programming might have implemented this, imagine the equal operator as a function, such as the following pseudo code illustrates.

equals(object lhs, object rhs)

The actual Hyperlambda code that would be the equivalent of the above pseudo code, can be found below, and this code actually executes successfully if you execute it as Hyperlambda.

eq
   .:lhs
   .:rhs

As you study Hyperlambda it might be beneficial to use the “Hyperlambda Playground” component that you can find in its frontend dashboard. This component allows you to play with Hyperlambda in “immediate mode”, experiment with Hyperlambda, execute it immediately from your browser, in a rich code editor, providing syntax highlighting for you, autocomplete on slots, etc. The “Hyperlambda Playground” component also allows you to save your snippets for later on your server.

If you put your cursor on an empty line and click CTRL+SPACE or FN+CONTROL+SPACE on a Mac, you will be given autocomplete, allowing you to easily see which slots are available for you.

Logically the Hyperlambda evaluator will signal each node in your Hyperlambda code sequentially, assuming all of your nodes are referencing an ISlot class, unless the node’s name starts with a ”.” or has an empty name. Most slots again are recursively executing their children slots, resulting in a recursively executed “execution tree”.

Hyperlambda structure

Hyperlambda is the textual representation of a node structure, where each node has a name, an optional value, and a collection of children nodes. Imagine the following Hyperlambda.

name:value
   child1

In the above Hyperlambda there is one root node. Its name is “name”, its value is “value”, and this node has one child node, with the name of “child1”. Its child node does not however have a value, which results in its value being “null”. The reason why the Hyperlambda parser understands “child1” as the child of the “name” node, is because it is prefixed by 3 spaces (SP) relative to the “name” node. This allows you to create graph objects (tree structures) with any depth you wish, by simply starting out with the number of spaces the node above has, add 3 additional spaces, and you have declared children nodes of the node above.

If you think of these nodes as a sequence of function invocations, from the top to bottom, where all of the nodes are assumed to be referencing slots, and all children nodes arguments to your slots - You can imagine how the tree structure resulting from parsing Hyperlambda into a graph object can easily be evaluated, due to its recursive nature, making it easy to express idioms such as “if”, “while”, “for-each”, etc. In fact logically this is similar to the way XSLT works, except there’s no XML, only Hyperlambda, lambda objects, and nodes.

Since each slot will be invoked with the node referencing the slot itself as the “input” Node, this makes the Hyperlambda evaluator recursive in nature, allowing a slot to evaluate all of its children, after executing its custom logic, etc. And yes, before you ask, Hyperlambda has been heavily influenced by LISP. In some ways Hyperlambda is Lisp for C#, only with a completely different syntax, and without S-Expressions.

Extending Hyperlambda with C#

To understand the relationship between C# and Hyperlambda, it might be beneficial for you to analyze the following code. The following code creates a new ISlot for you, implementing the interface found in the NuGet package called “magic.signals.contracts”.

using magic.node;
using magic.signals.contracts;

namespace acme.foo
{
    [Slot(Name = "acme.foo")]
    public class Foo : ISlot
    {
        public void Signal(ISignaler signaler, Node input)
        {
            var arg1 = input.Children.First().Get<int>();
            var arg2 = input.Children.Skip(1).First().Get<int>();
            input.Value = arg1 + arg2;
            input.Clear();
        }
    }
}

The above will result in a slot you can invoke from Hyperlambda using the following code.

acme.foo
   arg1:5
   arg2:7

Which of course will result in the following after having been executed.

acme.foo:int:12

Notice the relationship between the [Slot(Name = "acme.foo")] C# code and the way we invoke the [acme.foo] slot from Hyperlambda afterwards. It might help to imagine Hyperlambda as a simple string/type Dictionary, resolving an object from your IoC container using the name of the node as the key. And in fact, this is exactly how Hyperlambda is implemented - As a string/type dictionary, creating instances of your slot classes using your IoC container, for then to invoke its Signal method, passing in the identity node to your slot, where the identity node is the node invoking your signal from Hyperlambda. To create your own C# or F# slots, you can follow the following recipe.

  1. Reference the NuGet package magic.signals.contracts in your project.
  2. Create your class, and implement the ISlot interface.
  3. Mark your class with the Slot attribute, giving it an adequate Name property value.

Notice - You can also implement ISlotAsync if you want to support async invocations.

How Hyperlambda invokes slots

At the heart of Hyperlambda is the [eval] slot. This slot is responsible for executing your lambda object and follows a couple of simple rules. All nodes starting with a ”.” will be ignored, and [eval] will not try to raise these nodes as signals. This has two benefits.

  1. You can create “hidden” slots, that are only accessible from C#.
  2. You can use nodes starting with ”.” as data nodes, separating function invocations from data.

[eval] makes Hyperlambda “super functional” in nature. Below is an example of a Hyperlambda piece of code, that illustrates this, by adding a “callback” lambda object to its [while] invocation as a [.lambda] node, that will be invoked once for every iteration of your while loop.

.no:int:0

while
   lt
      get-value:x:@.no
      .:int:20
   .lambda

      // Your lambda goes here.
      log.info:Howdy from while
      math.increment:x:@.no

Hyperlambda tokens

The separating of a node’s name and its value is done by using a : character. To the left is the node’s name, and to the right is its value. The value of a node can also be a C# type of string, using double quotes, and even single quotes, or prefix your opening double quote with an “@” character, allowing you to use carriage returns in your strings the same way you can in for instance C#. Below are some examples.

.str1:"   This is a \r\n  string"
.str2:' This is also a string '
.str3:@"This
    is
  also a
      string"

Strings in Hyperlambda can be escaped with the exact same semantics as you would escape your C# strings, including referencing UNICODE characters in your strings. Hyperlambda is always serialized using UTF8, so you can add any UNICODE characters in your Hyperlambda you wish. Just make sure you save your files as UTF8 if you are using an external code editor to edit your Hyperlambda files.

How to comment your Hyperlambda code

Hyperlambda accepts comments the exact same way C# does, and you can use either multiline comments or single line comments, like the following example illustrates.

/*
 * Multiline comment.
 */

// Single line comment.

You cannot put comments on lines containing nodes, and comments must be indented the same amount of indentations as the nodes they are commenting, implying the nodes below them. Below is an example.

.data
   foo1:bar1
   foo2:bar2

for-each:x:@.data/*

   // This is correct indentation.
   set-value:x:@.dp/#
      .:Loop was here ...

Since Hyperlambda is using spaces (SP characters) to denote scope, indentation is important, also for comments. If you de-indent the above comment, you might get unpredictable results, in particular if you’re serializing and de-serializing your Hyperlambda preserving comments. Comments should as a general rule of thumb be applied with the same amount of indentation as the node below them.

Hyperlambda data segments

Hyperlambda does not separate between a “variable” and a “function invocation”. Hence, a node might serve as both at the same time. This allows you to dynamically modify your lambda structure, as you traverse it and execute it. But this creates another problem for you, which is that you will need a mechanism to store data. This is accomplished by prefixing a node’s name with a . character, at which point the Hyperlambda evaluator will ignore it, as it is traversing your tree, and not attempt to signal that particular node as a slot. Think of all nodes starting with a . character as “data segments”, or variables for that matter. Below is an example where [eval] will simply ignore the [.src] node and the [.dest] node, not attempting to invoke these as slots, but treat these as “data nodes”.

.src:foo
.dest
set-value:x:@.dest
   get-value:x:@.src

If you change name of the above [.src] node to simply [src], your code will raise an exception, with an error such as follows “No slot exists for [src]” since this slot doesn’t exist in your Hyperlambda vocabulary - Unless you for some reasons have an installation where this slot has been explicitly added to your vocabulary.

Hyperlambda documentation conventions

When we document Hyperlambda slots, and nodes, which are almost the same, we will document the node’s name with square brackets surrounding it, such as [this] illustrates, where “this” is referencing a node, and is the name of a node. We will also make such node references bold, to make them more easy to see.

Lambda expressions

Hyperlambda is heavily using “lambda expressions”. Think of these like XPath, except instead of referencing XML nodes they’re referencing Hyperlambda nodes. You can find the documentation for expressions in the magic.node project.