Skip to content

Instantly share code, notes, and snippets.

@frabbit
Created August 5, 2012 16:32
Show Gist options
  • Save frabbit/3265763 to your computer and use it in GitHub Desktop.
Save frabbit/3265763 to your computer and use it in GitHub Desktop.
// two special types to fake type constructor ploymorphism.
@:native('Dynamic')
class Of<M,A> {}
class In {}
interface Functor<F>
{
public function map<A,B>(val:Of<F,A>, f:A->B):Of<F,B>;
}
class ArrayFunctor implements Functor<Array<In>>
{
public function new () {}
override public function map<A,B>(of:Of<Array<In>,A>, f:A->B):Of<Array<In>,B> {
//...
}
}
// special types for imclit conversions and implicit objects
typedef IConv<A,B> = { implicitFrom : A, implicitTo : B->Void };
typedef IObj<A> = A->Void;
class ArrayMonadImplicitObj
{
public static var monad(default, null) = new ArrayOfMonad();
// imnplicitObj is by convention and used in the macro
public static inline function implicitObj <T>(_:IObj<Monad<Array<T>>>):Monad<Array<In>>
{
return monad;
}
}
class ArrayToArrayOfConv
{
public static inline function implicit <T>(_:IConv<Array<T>, ArrayOf<T>>, a:Array<T>)
{
return cast a;
}
}
class Functors {
public static function map<B,C>(of:Of<M, B>, f:B->C, functor:Functor<M>):Of<M,B> {
return functor.map(of, f);
}
}
// this is where the magic happens
class Implicits {
public static function apply (f:Expr, params:Array<Expr>) {
var c = macro f.curry(); // we have a function from the first to the second type
for (p in numArgsOfF)
{
if (we have a param at this position)
{
var needed = macro c.thunk() // we now have a function of type Of<F,A> -> Void
var check = Context.typeof( macro needed($p) ); // check if it can be applied directly to the param
var resultingExpr = null; // the resulting expr
if (check) resultingExpr = p; // it doesn't, we need a conversion
else
{
// search implicit conversion
var conversionTuple = macro { implicitFrom : param[0], implicitTo : needed }; // generated a special type, faking a little bit of multiple dispatch
var convExpr = macro conversionTuple.implicit();
if (Context.typeof(convExpr)) resultingExpr = convExpr; // it works
else throw "cannot find a conversion"
}
} else {
// search implicit object
// same as conversion tuple with one difference
// if there is an implicit object it may need dependecies and as such it's a function
// how do we get these dependencies
// ah i see, we have this function already ;)
resultingExpr = ...
}
res.push(resultingExpr);
c = macro $c($resultingExpr).curry(); // apply the current expression and move on
}
return macro $f($[res]);
}
}
class Identity
{
@:macro public static function map <M,A,B>(e:Expr, f:Expr):Expr
{
return Implicits.apply(macro Functors.map, [e,f]);
}
}
using ArrayToArrayOfConv;
using ArrayMonadImplicitObj;
using Identity;
class MyClass {
public function main () {
[1,2,3].map(function (x) return x + 1);
// becomes Identity.map([1,2,3], function (x) return x+1);
// becomes Functors.map(ArrayToArrayOfConv.implicit([1,2,3]), ArrayMonadImplicitObj.implicitObj());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment