magic.lambda
10.0.14
See the version list below for details.
dotnet add package magic.lambda --version 10.0.14
NuGet\Install-Package magic.lambda -Version 10.0.14
<PackageReference Include="magic.lambda" Version="10.0.14" />
paket add magic.lambda --version 10.0.14
#r "nuget: magic.lambda, 10.0.14"
// Install magic.lambda as a Cake Addin #addin nuget:?package=magic.lambda&version=10.0.14 // Install magic.lambda as a Cake Tool #tool nuget:?package=magic.lambda&version=10.0.14
Magic Lambda
Magic lambda is where you will find the "keywords" of Hyperlambda. It is what makes Hyperlambda Turing complete, and contains slots such as [for-each] and [if].
Structure
Since everything is a slot in Hyperlambda, this allows you to evaluate its conditional operators and logical operators, the same way you would evaluate a function in a traditional programming language. This might at first seem a bit unintuitive 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 objects as hierarchical tree structures, parsing them as such, and imagining these as "execution trees".
For instance, 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 not true, 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.
=(arg1, arg1)
The actual Hyperlambda code that would be the equivalent of the above pseudo code, can be found below.
eq
.:arg1
.:arg2
As you study Hyperlambda it might be beneficial to use the "Evaluator" component that you can find in its frontend Angular dashboard website. This component allows you to play with Hyperlambda in "immediate mode", allowing you to experiment with it, execute it immediately from your browser, using a rich code editor, providing syntax highlighting, autocomplete on slots, and allows you to save your snippets for later on your server. Below is a screenshot of the "Evaluator" component to give you an idea of what you might expect.
Logically the Hyperlambda evaluator will signal each nodes 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.
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) relatively 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 can declare children nodes of the above node.
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 - 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.
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#.
Extending Hyperlambda
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,
which resolves an object from your IoC container, using the name of the node as the key.
To create your own slots, follow the recipe below.
- Reference the NuGet package
magic.signals.contracts
in your project. - Create your class, and implement the
ISlot
interface. - Mark your class with the
Slot
attribute, giving it an adequateName
property value.
Notice - You can also implement ISlotAsync
if you want to create an async
slot.
The gory details
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.
- You can create "hidden" slots, that are only accessible from C#.
- 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 POP3 fetch emails slot that will be invoked once for each available email on your POP3 server.
/*
* Example of how to retrieve emails form a POP3 server.
*/
mail.pop3.fetch
max:int:50
raw:bool:false
.lambda
/*
* Some lambda object invoked once for every email fetched.
* Given message as [.message] node structured as lambda.
*/
lambda2hyper:x:..
log.info:x:-
The ISlot
called [mail.pop3.fetch] will invoke the above [.lambda] object once for each email
it finds on the POP3 server it connects to. It will use the [eval] slot to do this.
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 would do in for instance C#. Below is an example
.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.
Comments
Hyperlambda accepts comments the exacts 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 however.
Lambda expressions
To understand Hyperlambda, and how to efficiently create your own Hyperlambda, you'll have to understand "lambda expressions". These are kind of like XPath expressions. However, instead of referencing XML nodes, lambda expressions are referencing lambda nodes. This allows you to retrieve node names, values, and their children collection - For either to manipulate these, or read their values and react accordingly.
Notice - 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.
Combining "data nodes" with expressions, allows you to use, modify, and reference these as "variables". Below is an example.
.src:foo
.dest
set-value:x:@.dest
get-value:x:@.src
What the above code basically translates into, is.
Set the value of the [.dest] node to the value of [.src]
Branching and conditional execution
Magic Lambda contains the following slots. Most of these slots have async overloads, which will be automatically used by Magic if possible.
[if]
This is the Hyperlambda equivalent of if
from other programming languages. It allows you to test for some condition,
and evaluate a lambda object, only if the condition evaluates to true. [if] must be given exactly two arguments.
The first argument can be anything, including a slot invocation - But its second argument must be its [.lambda]
argument. The [.lambda] node will be evaluated as a lambda object, only if the first argument to [if] evaluates
to boolean true. Below is an example.
.dest
if
.:bool:true
.lambda
set-value:x:@.dest
.:yup!
All conditional slots, including [if], optionally accepts slots as their first condition argument. This allows you to invoke slots, treating the return value of the slot as the condition deciding whether or not the [.lambda] object should be executed or not. Below is an example.
.arguments
foo:bool:true
.dest
if
get-value:x:@.arguments/*/foo
.lambda
set-value:x:@.dest
.:yup!
[else-if]
[else-if] is the younger sibling of [if], and must be preceeded by its older sibling, or other [else-if] nodes, and will only be evaluated if all of its previous conditional slots evaluates to false - At which point [else-if] is allowed to test its condition - And only if it evaluates to true, evaluate its lambda object. Semantically [else-if] is similar to [if], in that it requires exactly two arguments with the same structure as [if].
.dest
if
.:bool:false
.lambda
set-value:x:@.dest
.:yup!
else-if
.:bool:true
.lambda
set-value:x:@.dest
.:yup2.0!
[else]
[else] is the last of the "conditional siblings" that will only be evaluated as a last resort, only if none of its elder "siblings" evaluates to true. Notice, contrary to both [if] and [else-if], [else] contains its lambda object directly as children nodes, and not within a [.lambda] node. This is because [else] does not require any conditional arguments like [if] and [else-if] does. An example can be found below.
.src:int:3
.dest
if
eq
get-value:x:@.src
.:int:1
.lambda
set-value:x:@.dest
.:yup!
else-if
eq
get-value:x:@.src
.:int:2
.lambda
set-value:x:@.dest
.:yup2.0!
else
set-value:x:@.dest
.:nope
[switch]
[switch] works similarly as a switch/case block in a traditional programming language, and will find the first [case] node with a value matching the evaluated value of the [switch] node, and execute that [case] node as a lambda object.
.val:foo
.result
switch:x:@.val
case:bar
set-value:x:@.result
.:Oops
case:foo
set-value:x:@.result
.:Success!
The [switch] slot can only contain two children nodes, [case] and [default]. The [default] node will be evaluated if none of the [case] node's values are matching the evaluated value of your [switch]. Try evaluating the following in your "Evaluator" to understand what I mean.
.val:fooXX
.result
switch:x:@.val
case:bar
set-value:x:@.result
.:Oops
case:foo
set-value:x:@.result
.:Oops2.0
default
set-value:x:@.result
.:Success!
In the above, the expression evaluated in the switch, which is @.val
will become "fooXX" after evaluating it.
None of its children [case] nodes contains this as an option, hence the [default] node will be evaluated,
and this results in setting the [.result] node's value to "Success!".
[default] cannot have a value, and all your [case] nodes must have a constant value, meaning not an expression. However, any types can be used as values for your [case] nodes. And your [switch] node must at the very least have minimum one [case] node. The [default] node is optional though.
Comparisons
[eq]
[eq] is the equality "operator" in Magic, and it requires two arguments, both of which will be evaluated as potential signals - And the result of evaluating [eq] will only be true if the values of these two arguments are exactly the same. Notice, the comparison operator will consider types, which implies that boolean true will not be considered equal to the string value of "true", etc.
.src:int:5
eq
get-value:x:@.src
.:int:5
[lt]
[lt] will do a comparison between its two arguments, and only return true if its first argument is "less than" its seconds argument. Consider the following.
.src1:int:4
lt
get-value:x:@.src1
.:int:5
[lte]
[lte] will do a comparison between its two arguments, and only return true if its first argument is "less than or equal" to its seconds argument. Consider the following.
.src1:int:4
lte
get-value:x:@.src1
.:int:4
[mt]
[mt] will do a comparison between its two arguments, and only return true if its first argument is "more than" its seconds argument. Consider the following.
.src1:int:7
mt
get-value:x:@.src1
.:int:5
[mte]
[mte] will do a comparison between its two arguments, and only return true if its first argument is "more than or equal" to its seconds argument. Consider the following.
.src1:int:7
mte
get-value:x:@.src1
.:int:5
Boolean logical conditions
[exists]
[exists] will evaluate to true if its specified expression yields one or more results. If not, it will return false.
.src1
foo
.src2
exists:x:@.src1/*
exists:x:@.src2/*
[and]
[and] requires two or more arguments, and will only evaluate to true, if all of its arguments evaluates to true. Consider the following.
and
.:bool:true
.:bool:false
and
.:bool:true
.:bool:true
And will (of course) evaluate its arguments before checking if they evaluate to true, allowing you to use it as a part of richer comparison trees, such as the following illustrates.
.s1:bool:true
.s2:bool:true
.res
if
and
get-value:x:@.s1
get-value:x:@.s2
.lambda
set-value:x:@.res
.:OK
[or]
[or] is similar to [and], except it will evaluate to true if any of its arguments evaluates to true, such as the following illustrates. [or] will also evaluate its arguments, allowing you to use it as a part of richer comparison trees, the same way [and] allows you to. Below is a simple example.
or
.:bool:false
.:bool:false
or
.:bool:false
.:bool:true
[not]
[not] expects exactly one argument, and will negate its boolean value, whatever it is, such as the following illustrates.
not
.:bool:true
not
.:bool:false
[not] will also evaluate its argument, allowing you to use it in richer comparison trees, the same you could do with both [or] and [and].
Modifying your graph
[add]
This slot allows you to dynamically add nodes into a destination node. Its primary argument is the destination, and it assumes that each children is a collection of nodes it should append to the destination node's children collection. The reasons for this additional level of indirection, is because the [add] slot might have children that are by themselves slot invocations, which it will evaluate before it starts adding the children nodes of these arguments to its destination node's children collection. Below is an example.
.dest
add:x:@.dest
.
foo1:howdy
foo2:world
.
bar1:hello
bar2:world
Notice how all the 4 nodes above (foo1, foo2, bar1, bar2) are appended into the [.dest] node's children collection. This allows you to evaluate slots and add the result of your slot invocation into the destination, such as the following illustrates.
.src
foo1:foo
foo2:bar
.dest
add:x:@.dest
get-nodes:x:@.src/*
[add] can also take an expression leading to multiple destinations as its main argument, allowing you to add a copy of your source nodes into multiple node collections at the same time, with one invocation.
[insert-before]
This slot functions exactly the same as the [add] node, except it will insert the nodes before the node(s) referenced in its main argument.
.foo
foo1
foo2
insert-before:x:@.foo/*/foo1
.
inserted
[insert-after]
This slot functions exactly the same as the [add] node, except it will insert the nodes after the node(s) referenced in its main argument.
.foo
foo1
foo2
insert-after:x:@.foo/*/foo1
.
inserted
[remove-nodes]
This slot will remove all nodes its expression is pointing to.
.data
foo1
foo2
foo3
remove-nodes:x:@.data/*/foo2
[set-value]
Changes the value of a node referenced as its main expression to whatever its single source happens to be. Notice, when you invoke a slot that tries to change the value, name, or the node itself of some expression, and you supply a source expression to your invocation - Then the result of the source expression cannot return more than one result. The destination expression however can modify multiple nodes at the same time.
.foo
set-value:x:@.foo
.:SUCCESS
[set-name]
Changes the name of a node referenced as its main expression to whatever its single source happens to be.
.foo
old-name
set-name:x:@.foo/*
.:new-name
[unwrap]
This slot is useful if you want to invoke another slot, but before you do, you want to evaluate some expressions inside of some argument to your slot. Imagine the following.
.src:Hello World
unwrap:x:+
.dest:x:@.src
In the above example, before the [.dest] node is reached by the Hyperlambda instruction pointer, the value of the [.dest] node will have been "unwrapped" (evaluated), and its value will be "Hello World". When you invoke lambda objects that are cloned for some reasons, this slot becomes very handy, since it allows you to "forward evaluate" expressions inside your lambda object. It's also useful when you have expressions inside for instance a [return] slot, and you want to return the value the expression evaluates to, and not the expression itself.
Source slots
[get-value]
Returns the value of the node its expression is pointing to.
.data:Hello World
get-value:x:-
[get-name]
Returns the name of the node referenced in its expression.
.foo
get-name:x:-
[get-count]
This slot returns the number of nodes its expression is pointing to.
.data
foo1
foo2
get-count:x:@.data/*
[get-nodes]
Returns the nodes its expression is referencing.
.data
foo1
foo2
get-nodes:x:-/*
[reference]
This slot will evaluate its expression, and add the entire node the expression is pointing to, as a referenced node into its value. This allows you to pass a node into a slot by reference, and have that slot modify the node itself, or its children. This might sometimes be useful to have slots modify some original graph object, or parts of a graph - Or get access to iterate over parts of your graph object's children.
.foo
reference:x:@.foo
set-value:x:-/#
.:Yup!
You can think of this slot as the singular version of [get-nodes], except instead of returning multiple nodes, it assumes its expression only points to a single node, and instead of returning a copy of the node, it returns the actual node by reference.
Notice - The #
iterator above, will enter into the node referenced as a value of its current
result - Implying it allows you to deeply traverse nodes passed in as references. This is sometimes
useful in combination with referenced nodes, passed in as values of other nodes.
[format]
This slot converts the format some expression or value according to some specified String.Format
expression.
The following code will string format the number 57 making sure it's prefixed with leading zeros always ending
up having at least 5 digits. See .Net String.Format for which patterns you can use. The culture used will always
be the invariant one.
.foo:int:57
format:x:-
pattern:"{0:00000}"
[get-context]
This slot returns a context stack object, which is an object added to the stack using [context].
Exceptions
[try]
This slot allows you to create a try/catch/finally block of lambda, from where exceptions are caught, and optionally hadled in a [.catch] lambda, and/or a [.finally] lambda.
try
throw:Whatever
.catch
log.info:ERROR HAPPENED!! 42, 42, 42!
.finally
log.info:Yup, we are finally there!
[throw]
This slot simply throws an exception, with the exception message taken from its value. See the [try] slot for an example. Notice, you can make the exception propagate to the client by adding a [public] parameter, and set its value to boolean "true". At which point the exception will be returned to the client, even in release builds. Otherwise, the exception will only be visible in debug builds, and never returned to the client. You can also modify the [status] HTTP return value that's returned to the client, to become e.g. 404, indicating "not found", etc. In addition you can pass in a [field] which will be serialised back to the client if specified to help the client to semantically figure out which field name that triggered the exception.
Loops
[for-each]
Iterates through each node as a result of an expression, and evaluates its lambda object, passing in the currently iterated node as a [.dp] argument, containing the actual node iterated by reference.
.data
foo1
foo2
for-each:x:-/*
set-value:x:@.dp/#
.:hello
See the documentation for the [reference] slot to understand how reference nodes works.
[while]
Semantically similar to [if], but instead of evaluating its lambda object once, will iterate it for as long as the condition evaluates to true. Requires exactly two arguments, the same way [if] does.
.no:int:0
.res
while
lt
get-value:x:@.no
.:int:5
.lambda
add:x:@.res
.
foo
math.increment:x:@.no
Evaluating slots
[eval]
Evaluates each lambda object found by either inspecting its children collection, or evaluating the expression found in its value.
.res
.lambda
set-value:x:@.res
.:OK
eval:x:@.lambda
Threading
[fork]
Forks the given lambda into a new thread of execution, using a thread from the thread pool. This slot is useful for creating "fire and forget" lambda objects, where you don't need to wait for the result of the execution before continuing executing the current scope.
fork
info.log:I was invoked from another thread
[join]
Joins all child [fork] invocations, implying slot will wait until all forks directly below it has finished executing, and automatically copy the result of the [fork] into the original node.
join
fork
http.get:"https://servergardens.com"
fork
http.get:"https://gaiasoul.com"
[semaphore]
Creates a named semaphore, where only one thread will be allowed to evaluate the same semaphore at the same time. Notice, the semaphore to use is defined through its value, implying you can use the same semaphore multiple places, by using the same value of your [semaphore] invocation.
semaphore:foo-bar
/*
* Only one thread will be allowed entrance into this piece of
* code at the same time, ensuring synchronised access, for cases
* where you cannot allow more than one thread to enter at the
* same time.
*/
[sleep]
This slot will sleep the current thread for x number of milliseconds, where x is an integer value, expected to be passed in as its main value.
// Sleeps the main thread for 1 second, or 1000 milliseconds.
sleep:1000
Miscellaneous slots
[types]
This slot returns all Hyperlambda types your current installation supports.
types
[type]
This slot return the Hyperlambda type name of some value.
.foo:int:57
type:x:-
After invoking the above, the value of [type] will be int
.
[convert]
This slot converts the value of an expression from its original type, to whatever [type] declaration you supply as its argument.
.foo:57
convert:x:-
type:int
Notice - You can also base64 encode and decode byte[]
with this slot, by passing in "base64" or
"from-base64" as your [type] argument.
[vocabulary]
Returns the name of every static slot in your system, optional passing in a string, or an expression leading to a string, which is a filtering condition where the slot must start with the filter in its name, to be considered a part of the end result.
// Returns ALL slots in your system.
vocabulary
// Returns only slots starting with [io.file]
vocabulary:io.file
[whitelist]
This slot temporarily within the given scope changes the available slots, allowing you to declare a block of lambda, where only a sub set of your vocabulary is available for some piece of code to signal. This allows you to relatively securely allow some partially untrusted source to pass in a piece of Hyperlambda, for then to allow it to evaluate its own Hyperlambda. The slot takes two arguments.
- [vocabulary] - Whitelisted slots
- [.lambda] - Lambda object to evaluate for the given scope
.result
whitelist
vocabulary
set-value
.lambda
// Inside of this [.lambda] object, we can only invoke [set-value], and no other slots!
set-value:x:@.result
.:foo
// Notice, the next line will throw an exception,
// because [add] is not whitelisted in our above [vocabulary] declaration!
add:x:@.result
.
foo:bar
[context]
This slot allows you to add an object unto the stack, such that it can later be retrieved with the [get-context] slot. Below is an example.
.result
context:foo
value:bar
.lambda
set-value:x:@.result
get-context:foo
The slot requires a name as the value of its slot invocation node, a [value] as the value you want to put onto the stack, and a [.lambda] object being the lambda where the stack object exists, and can be retrieved using [get-context].
[apply]
This slot takes an expression in addition to a list of arguments, and "applies" the arguments unto the expression's result node set, allowing you to perform dynamic substitutions on lambda hierarchies such as the following illustrates.
.lambda
foo
arg1:{some_arg}
some-static-node:static-value
apply:x:-
some_arg:value of argument
After execution of the above Hyperlambda you will have a result resembling the following.
apply
foo
arg1:value of argument
some-static-node:static-value
Notice how the nodes from your template lambda object have been copied as children into your [apply]
node, while during this "copying process", the arguments you supplied to [apply] have been used
to perform substitutions on all nodes in your template lambda having a value of {xxx}
, where xxx is
your argument name. In the above example for instance the {arg1:some_arg}
template node had its value
replaced by the value of the [some_arg] node passed in as a parameter to [apply]. If the name of
the argument to [apply], matches the value of your template node wrapped inside curly braces - Then
the value of your argument to apply becomes the new value of your template node after substitution has
been performed.
Only node values starting out with {
and ending with }
will be substituted, and you are expected to provide
all arguments found in the template lambda object, or the invocation will fail, resulting in an exception. This
allows you to create "template lambda objects" that you dynamically transform into something else, without
really caring about its original structure, but rather only its set of dynamic substitution arguments. This
slot leaves all other nodes as is.
Basically, the way it works, is that it takes your expression, and recursively iterate each node below the
result of your expression, checks to see if the node's value is an argument such as e.g. {howdy}
-
And if so, it substitutes the {howdy}
parts with the value of the argument you are expected to supply to your
invocation having the name [howdy].
You can also reference the same argument multiple times in your template, in addition to create arguments that are lambda objects instead of simple values. Below is an example of both of these constructs.
.lambda
foo
foo1:{arg1}
foo2:{arg1}
foo3:{arg2}
apply:x:-
arg1:int:5
arg2
this:is
an:entire
lambda:object
used_as_an_argument:int:7
The above will result in the following.
apply
foo
foo1:int:5
foo2:int:5
foo3
this:is
an:entire
lambda:object
used_as_an_argument:int:7
Above you can also see how typing information is not lost, since the [foo1] and [foo2] nodes are both having integrer values of 5 after apply is done executing. At the same time you can see how the [foo3] node instead of having a simple value applied, actually ended up with a copy of all children passed in as [arg2]. You can also substitute names of nodes, such as the following illustrates.
.lambda
foo
{arg1}:value-is-preserved
apply:x:-
arg1:foo1
The above of course results in the following.
apply
foo
foo1:value-is-preserved
This is a fairly advanced slot, but it's the heart of the generator, allowing us to generate HTTP endpoints, starting out with some template file, which is dynamically changed, according to input arguments supplied during the crudification process. You can also combine transformations of names, values, and children in the same template nodes. The process is also recursive in nature, performing substitutions through the entire hierarchy of your template lambda.
Project website
The source code for this repository can be found at github.com/polterguy/magic.lambda, and you can provide feedback, provide bug reports, etc at the same place.
Quality gates
License
This project is the copyright(c) 2020-2021 of Aista, Ltd thomas@servergardens.com, and is licensed under the terms of the LGPL version 3, as published by the Free Software Foundation. See the enclosed LICENSE file for details.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- magic.node.extensions (>= 10.0.14)
- magic.signals (>= 10.0.14)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on magic.lambda:
Package | Downloads |
---|---|
magic.library
Helper project for Magic to wire up everything easily by simply adding one package, and invoking two simple methods. When using Magic, this is (probably) the only package you should actually add, since this package pulls in everything else you'll need automatically, and wires up everything sanely by default. To use package go to https://polterguy.github.io |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
17.1.7 | 434 | 1/12/2024 |
17.1.6 | 392 | 1/11/2024 |
17.1.5 | 440 | 1/5/2024 |
17.0.1 | 488 | 1/1/2024 |
17.0.0 | 713 | 12/14/2023 |
16.11.5 | 701 | 11/12/2023 |
16.9.0 | 737 | 10/9/2023 |
16.7.0 | 1,109 | 7/11/2023 |
16.4.1 | 914 | 7/2/2023 |
16.4.0 | 922 | 6/22/2023 |
16.3.1 | 834 | 6/7/2023 |
16.3.0 | 836 | 5/28/2023 |
16.1.9 | 1,149 | 4/30/2023 |
15.10.11 | 988 | 4/13/2023 |
15.9.1 | 1,111 | 3/26/2023 |
15.9.0 | 969 | 3/24/2023 |
15.8.2 | 1,011 | 3/20/2023 |
15.7.0 | 947 | 3/6/2023 |
15.5.0 | 2,119 | 1/28/2023 |
15.2.0 | 1,313 | 1/18/2023 |
15.1.0 | 1,661 | 12/28/2022 |
14.5.7 | 1,255 | 12/13/2022 |
14.5.5 | 1,324 | 12/6/2022 |
14.5.1 | 1,187 | 11/23/2022 |
14.5.0 | 1,121 | 11/18/2022 |
14.4.5 | 1,272 | 10/22/2022 |
14.4.1 | 1,284 | 10/22/2022 |
14.4.0 | 1,163 | 10/17/2022 |
14.3.9 | 1,171 | 10/13/2022 |
14.3.1 | 1,599 | 9/12/2022 |
14.3.0 | 1,178 | 9/10/2022 |
14.1.3 | 1,456 | 8/7/2022 |
14.1.2 | 1,187 | 8/7/2022 |
14.1.1 | 1,179 | 8/7/2022 |
14.0.14 | 1,228 | 7/26/2022 |
14.0.12 | 1,148 | 7/24/2022 |
14.0.11 | 1,158 | 7/23/2022 |
14.0.10 | 1,102 | 7/23/2022 |
14.0.9 | 1,141 | 7/23/2022 |
14.0.8 | 1,226 | 7/17/2022 |
14.0.5 | 1,342 | 7/11/2022 |
14.0.4 | 1,304 | 7/6/2022 |
14.0.3 | 1,223 | 7/2/2022 |
14.0.2 | 1,199 | 7/2/2022 |
14.0.0 | 1,371 | 6/25/2022 |
13.4.0 | 2,598 | 5/31/2022 |
13.3.4 | 1,908 | 5/9/2022 |
13.3.0 | 1,491 | 5/1/2022 |
13.2.0 | 1,702 | 4/21/2022 |
13.1.0 | 1,551 | 4/7/2022 |
13.0.0 | 1,210 | 4/5/2022 |
11.0.5 | 1,948 | 3/2/2022 |
11.0.4 | 1,261 | 2/22/2022 |
11.0.3 | 1,328 | 2/9/2022 |
11.0.2 | 1,316 | 2/6/2022 |
11.0.1 | 1,008 | 2/5/2022 |
11.0.0 | 1,297 | 2/5/2022 |
10.0.21 | 1,249 | 1/28/2022 |
10.0.20 | 1,238 | 1/27/2022 |
10.0.19 | 1,254 | 1/23/2022 |
10.0.18 | 1,206 | 1/17/2022 |
10.0.16 | 1,082 | 1/14/2022 |
10.0.15 | 1,294 | 12/31/2021 |
10.0.14 | 1,125 | 12/28/2021 |
10.0.7 | 2,022 | 12/22/2021 |
10.0.5 | 1,268 | 12/18/2021 |
9.9.9 | 2,175 | 11/29/2021 |
9.9.3 | 1,776 | 11/9/2021 |
9.9.2 | 1,518 | 11/4/2021 |
9.9.0 | 1,828 | 10/30/2021 |
9.8.9 | 1,626 | 10/29/2021 |
9.8.7 | 1,584 | 10/27/2021 |
9.8.6 | 1,568 | 10/27/2021 |
9.8.5 | 1,595 | 10/26/2021 |
9.8.0 | 2,474 | 10/20/2021 |
9.7.9 | 1,515 | 10/19/2021 |
9.7.5 | 2,630 | 10/14/2021 |
9.7.3 | 884 | 10/14/2021 |
9.7.1 | 1,060 | 10/14/2021 |
9.7.0 | 1,628 | 10/9/2021 |
9.6.6 | 1,717 | 8/14/2021 |
9.2.0 | 7,545 | 5/26/2021 |
9.1.4 | 2,361 | 4/21/2021 |
9.1.0 | 1,979 | 4/14/2021 |
9.0.0 | 1,790 | 4/5/2021 |
8.9.9 | 2,038 | 3/30/2021 |
8.9.3 | 2,452 | 3/19/2021 |
8.9.2 | 1,968 | 1/29/2021 |
8.9.1 | 2,033 | 1/24/2021 |
8.9.0 | 2,146 | 1/22/2021 |
8.6.9 | 3,884 | 11/8/2020 |
8.6.7 | 1,759 | 11/4/2020 |
8.6.6 | 2,015 | 11/2/2020 |
8.6.3 | 1,762 | 11/1/2020 |
8.6.2 | 2,493 | 10/30/2020 |
8.6.0 | 2,753 | 10/28/2020 |
8.5.0 | 2,835 | 10/23/2020 |
8.4.3 | 3,528 | 10/17/2020 |
8.4.2 | 1,800 | 10/16/2020 |
8.4.1 | 1,706 | 10/16/2020 |
8.4.0 | 2,739 | 10/13/2020 |
8.3.1 | 3,444 | 10/5/2020 |
8.3.0 | 2,055 | 10/3/2020 |
8.2.2 | 2,801 | 9/26/2020 |
8.2.1 | 2,065 | 9/25/2020 |
8.2.0 | 2,054 | 9/25/2020 |
8.1.18 | 1,846 | 9/23/2020 |
8.1.17 | 6,842 | 9/13/2020 |
8.1.16 | 1,380 | 9/13/2020 |
8.1.15 | 1,986 | 9/12/2020 |
8.1.11 | 3,915 | 9/11/2020 |
8.1.10 | 1,584 | 9/6/2020 |
8.1.9 | 3,129 | 9/3/2020 |
8.1.8 | 2,161 | 9/2/2020 |
8.1.7 | 1,908 | 8/28/2020 |
8.1.4 | 1,977 | 8/25/2020 |
8.1.3 | 1,830 | 8/18/2020 |
8.1.2 | 1,743 | 8/16/2020 |
8.1.1 | 1,732 | 8/15/2020 |
8.1.0 | 1,140 | 8/15/2020 |
8.0.1 | 3,159 | 8/7/2020 |
8.0.0 | 1,770 | 8/7/2020 |
7.0.1 | 1,835 | 6/28/2020 |
7.0.0 | 1,739 | 6/28/2020 |
5.0.1 | 1,725 | 5/31/2020 |
5.0.0 | 7,333 | 2/25/2020 |
4.0.4 | 8,263 | 1/27/2020 |
4.0.3 | 1,748 | 1/27/2020 |
4.0.2 | 1,930 | 1/16/2020 |
4.0.1 | 1,893 | 1/11/2020 |
4.0.0 | 1,855 | 1/5/2020 |
3.1.1 | 2,561 | 12/12/2019 |
3.1.0 | 5,398 | 11/10/2019 |
3.0.0 | 4,410 | 10/23/2019 |
2.0.2 | 2,289 | 10/21/2019 |
2.0.1 | 7,622 | 10/15/2019 |
2.0.0 | 2,127 | 10/13/2019 |
1.2.2 | 1,838 | 10/11/2019 |
1.2.1 | 1,874 | 10/10/2019 |
1.2.0 | 1,100 | 10/10/2019 |
1.1.9 | 1,102 | 10/9/2019 |
1.1.8 | 1,048 | 10/9/2019 |
1.1.7 | 1,031 | 10/7/2019 |
1.1.6 | 1,101 | 10/6/2019 |
1.1.5 | 1,068 | 10/6/2019 |
1.1.3 | 5,121 | 10/6/2019 |
1.1.0 | 4,617 | 10/5/2019 |
1.0.1 | 1,468 | 9/26/2019 |
1.0.0 | 1,082 | 9/26/2019 |