moon-core 1.1.1

Utility library for a bunch of stuff. Also generator functions, fibers, yield, await.

Released 2016-08-23.

To install, run:

haxelib install moon-core 1.1.1

See using Haxelib in Haxelib documentation for more information.

Maintainerprofound7
Websitehttps://github.com/profound7/moon-core
Current version1.1.1
StatisticsInstalled 45 times
LicenseMIT
Tags async, cross, utility

README.md

Moon Core Library

The moon-core lib is the result of accumulated codes over the years from freelancing, hobby, and professional work. The cross-platform/general stuff goes into this project.

Some Stuff from this Library

Core

Any with deepEquals, Bits abstract for bit manipulations, Compare for easy comparisons and sorting. Range for Pythonesque Int Iterator. Future, Signal and Observable as events primitives. Pair and Tuple for ordered values, each with its own type. Sugar for short lambdas and other conveniences.

Text is a String abstract with operator overloading (eg. multiply with Int to repeat). Also some functions to display text in fixed width, with options to align left/right/center.

Seq is an abstract Iterable, with automatic conversions from Iterator, Vector, String, Void->Iterator and Void->Seq. It has all of Lambda methods, plus many more. Actually, I've implemented almost every method less some overloads from .NET's LINQ Enumerable class. Like .NET's Enumerable, a number of the query methods have deferred execution. This will pave way for a LINQ-like Haxe macro when I get around to doing it. Seq works best as a static extension (so sequences of sequences can have additional methods).

Generator and Fiber for easy writing of asynchronous code using @yield expression to stop running a function, which can later be resumed. This is done with Async to transform regular functions into a state machine. Not every Haxe expression is supported yet. For example, variable capture from a switch case isn't working. Array comprehension is also not working. In some special cases, if and switch may be detected incorrectly if they're expressions or statements. You can manually annotate with @void or @expr to avoid such compilation errors.

Crypto

PBKDF2 for password hashing, Jwt (JSON Web Tokens) for signed tokens. Arc4 symmetric cipher, Rsa asymmetric crypto (Oaep padding not fully implemented).

Data

HyperArray for creating multi-dimensional array at run-time. MultiArray for an efficient multi-dimensional array, where all calculations are pre-calculated and inlined at compile-time. NestedArray for multi-dimensional arrays that can be jagged.

There's a bunch of different iterators, including IterableIterator and IteratorIterator for iterating through nested sequences as if they're a single linear sequence.

LruCache (Least-Recently-Used Cache) which behaves like a StringMap except that it has limited capacity, and the least-recently used item will get kicked out when inserting a new item when full. It's implemented using DoubleLinkedList. Histogram for tallying stuff, and Set for unique collections.

Numbers

BigInteger, BigRational, and BigBits for dealing with numbers of arbitrary size. BigInteger is used for implementing Rsa (done for fun -- it's slow, not fully tested, so don't use).

Stats for computing stuff like mean, median, mode, variance, standard deviation, zScore etc...

There's a number of seedable pseudo-random number generators to use, such as MersenneTwisterRandom, LcgRandom (Linear Congruent Generator), XorShiftRandom, and a few others. Any of these PRNG algorithms could be assigned to a Random abstract, where you'll get many more methods like shuffling arrays, random sample, n-dice rolls, and so on. You can also generate random numbers that follows a specific distribution like triangular, exponential, gamma, chi-squared, and a number of others.

Units (work-in-progress!) uses Haxe abstracts to give meaning to numbers by typing it with a unit. Automatic conversions between units when you assign to a compatible type. TODO: Only allow units of the same type for add/sub. Dimensionless numbers can be multiplied/divided with a dimensioned number. Type conversion when two units are multiplied/divided.

Remoting

FutureProxy is like AsyncProxy for Haxe remoting, but it returns a Future instead. Future is from moon-lib and not to be confused with the one from tink_core.

Strings

DamerauLevenshtein for checking edit distances between two strings. Inflect to convert from string cases like kebab-case to camelCase etc... HashCode has several different algorithms for calculating hash codes of strings.

Tools

These are all meant to be used as a static extension.

ArrayTools lets you zip and unzip like in python. Also has some set operations between arrays. FloatTools to round, truncate, clamp, interpolate, format numbers. FunctionTools to memoize functions. IteratorTools has all the methods from Seq, except that it doesn't defer execution. TextTools is like Text, but as a String static extension instead of an abstract.

Web

Template is a compile-time templating system that allows you to write with ASP-like tags using Haxe as the language. Since it's a compile-time system, you get all the type checks from the Haxe compiler, and there's no special parsing at run-time. It works on all targets, even on those without sys.io.File.

Router is a general routing class, that can be used as an alternative to haxe.web.Dispatch. It's general, and so it has no dependency on haxe.web.Request, making it available on all targets. There's an option to use a macro for defining routes using meta annotations on methods.

Url can be used to break up a URL into its individual components.

Asynchronous Generators and Fibers

Cooperative Multi-tasking in Haxe!

You can now easily write asynchronous cooperatively-multitasking codes in Haxe! Unlike threads, you don't need to worry about locking, and this is especially useful to write asynchronous codes on single-threaded targets.

Here are some information on what generators/fibers are: - https://en.wikipedia.org/wiki/Generator_(computerprogramming) - https://en.wikipedia.org/wiki/Coroutine - https://en.wikipedia.org/wiki/Fiber(computer_science)

This works just like the generator functions in JavaScript, Python, and C#. If a function or method contains a @yield x or @await f expression, then it is automatically transformed into a generator function.

NEW: @await f where f is a Future<T>.

Generators

Using @yield x expression

You can type the generator function with a valid async type, to get that type when the generator is called. In the following example, it's an Iterator<String>, but it could be other async types too.

function names():Iterator<String>
{
    @yield "alice";
    @yield "bob";
    @yield "carol";
    @yield "dave";
}

for (x in names())
    trace(x);

In this example, we want a Generator<Int, String> instead. Int is what this generator produces. String is what this generator accepts via send(value). This is like in JavaScript and Python where you can also send values back into the generator.

function greet(a:Int, b:String):Generator<Int, String>
{
    for (i in a...10)
    {
        if (i == 5)
            trace("yo" + @yield 999);
        else
            trace(b + @yield i);
    }
}

var it = greet(3, "hi");
var m = 10;

while (it.hasNext())
{
    var out = it.send(" " + m++);
    trace("out:   " + out);
}

Using @await f expression

@await f is syntactic sugar for an expression of @yield. It's mainly useful in Fibers, but it can be used in Generators too. @await f is equivalent to:

{
    while (f.state == FutureState.Awaiting)
        @yield __current;
        
    switch(f.state)
    {
        case FutureState.Success(v): v;
        case FutureState.Failure(e): throw e;
        case FutureState.Awaiting: throw "assert";
    }
}

Here's an example of it being used:

var someFuture = new Future<Int>();

function example():Iterator<String>
{
    @yield "foo";
    var x:Int = @await someFuture;
    trace('value of x is $x');
    @yield "bar";
}

var it = example();
var i = 0;

while (it.hasNext())
{
    // trigger completion when i is 5
    if (i == 5) someFuture.complete(5318008);
    trace(i + ": " + it.next());
    ++i;
}

// output
// 0: foo
// 1: foo
// 2: foo
// 3: foo
// 4: foo
// value of x is 5318008
// 5: foo
// 6: bar

Async Types

So what else is valid besides Iterator and Generator? Here's a complete list:

Custom Async Types

You can define your own async types, for less verbose code. There are 2 ways you can do that.

Method 1: static fromAsync method in class

class CustomWrapper<T>
{
    public var foo:Iterable<T>;
    
    public function new()
    {
        foo = [];
    }
    
    // Iterable<A> can be any valid async type
    public static function fromAsync<A>(it:Iterable<A>):CustomWrapper<A>
    {
        var obj = new CustomWrapper<A>();
        obj.foo = it;
        return obj;
    }
}

Method 2: static extensions with @asyncType meta

class WhateverTools<T>
{
    // Iterable<A> can be any valid async type
    @asyncType public static function whatever<A>(it:Iterable<A>):CustomWrapper<A>
    {
        var obj = new CustomWrapper<A>();
        obj.foo = it;
        return obj;
    }
}

After that, you can do this:

function custom():CustomWrapper<String>
{
    @yield "aaa";
    @yield "bbb";
    @yield "ccc";
}

for (x in custom().foo)
    trace(x);

Method 2 is useful when you don't have access to the class, for example, in 3rd party libraries. This gives you the flexibility to use, for example, tink_core's Future instead of the Future from this library.

Fibers

Async generator types are simply iterators. You need to manually iterate through them. Async fiber types are iterators added into a Fiber object. The fiber Processor is usually added to your game loop or some interval/update function, and the processor will take turns switching between different fibers every loop.

function think(self:Entity):Fiber<Int>
{
    // some long-running algorithm
    var i = 0;
    for (e1 in entities)
        for (e2 in entities)
        {
            self.doSomething(e1, e2);
            if (++i % 10 == 0)
                @yield i; // allow other fibers to run
        }
}

// these fibers are automatically added to Processor.main
var fiberA = think(a);
var fiberB = think(b);
var fiberC = think(c);

// this while loop represents your game loop/update
while (Processor.main.hasNext())
{
    // run the next 7 fibers.
    // this number is arbitrary.
    Processor.main.run(7);
}

The processor will automatically remove fibers that has terminated. You can manually kill a fiber using fiber.kill().

How to Use

There are 2 ways to use this async macro. One is by calling the Async.async(function()...) macro. The other, preferred, way is to add a @:build(moon.core.Sugar.buildAsync()) to your class (or build instead of buildAsync for other stuff like short lambdas too). The second way results in cleaner looking code.

See AsyncExamples for more generator function examples using Async.async().

See AsyncSugaredExamples for the examples that uses @:build.

Running the async examples:

haxe -main AsyncSugaredExamples -cp src -cp test -neko async.n
neko async

How does it Work?

See ASYNC.md

Todos

I need help to iron out some issues related to the async stuff.

  • ~~When using @:build to transform the class, generator methods couldn't really determine the type of certain expressions, leading to some compile errors. Temporary workaround is to annotate such expressions with @void or @expr to indicate if it is a statement or an expression.~~ Should mostly work now.
  • ~~I don't yet know how to deal with try/catch in generator functions.~~
  • ~~Generator functions don't yet support array comprehensions, but its do-able, I just haven't gotten around to doing it yet.~~
  • ~~In a switch case, I don't know how to identify which EConst(CIdent(x)) are variable captures.~~ This is done! Thanks CauĂȘ Waneck!
  • It's possible to further optimize the result of transforming the generator function. - If you'd like to see the output of the various passes involved in transforming the generator function, open moon.macros.async.AsyncTransformer.hx and change DEBUG_OUTPUT_TO_FILE to true.
  • Macro functions very unlikely to work within generator functions
  • Generator functions within generator functions not properly working
  • Unit tests, and ensure it works on all platforms - currently only tested in neko

Contributing

Feel free to contribute. Contributions, bug fixes, and feature requests are welcomed.

Credits

Most of the lib was written by me, however, some portions of it was ported from other open source codes. Some are not ported, but are adaptations or implementations based on ideas and algorithms from articles and online discussions.

  • BigInteger and BigRational moon.numbers.big (port) Peter Olson: https://github.com/peterolson/BigInteger.js/blob/master/BigInteger.js

  • Template moon.web (inspired by) John Resig: http://ejohn.org/blog/javascript-micro-templating/

  • RandomDistributions moon.numbers.random (port) NumPy developers: https://github.com/numpy/numpy/blob/master/numpy/random/mtrand/distributions.c

  • Quarternion moon.numbers.geom (port) Will Perone: http://willperone.net/Code/quaternion.php

  • NeuralNetwork moon.ai.nnet (port) Juan Calaza: https://github.com/cazala/synaptic

  • MersenneTwisterRandom moon.numbers.random.algo (port) Sean Luke: http://www.cs.gmu.edu/~sean/research/mersenne/MersenneTwister.java Sean McCullough via Makoto Matsumoto and Takuji Nishimura: https://gist.github.com/banksean/300494

  • DamerauLevenshtein moon.strings.metric (port) Kevin L. Stern: https://github.com/KevinStern/software-and-algorithms/blob/master/src/main/java/blogspot/software_and_algorithms/stern_library/string/DamerauLevenshteinAlgorithm.java

  • Parser moon.peg.grammar (reference, ideas) Warth, Douglass, Millstein: http://www.vpri.org/pdf/tr2007002_packrat.pdf Mark Engelberg: https://github.com/Engelberg/instaparse

  • Inflect moon.strings.inflect (ideas) Aura PHP developers: https://github.com/auraphp/Aura.Framework/blob/develop/src/Aura/Framework/Inflect.php

License

MIT