/// <summary> /// "Applicative" instance for HaxlFetch. /// </summary> /// <remarks> /// This isn't a true applicative instance; we don't have: /// /// > (<*>) :: f (a -> b) -> f a -> f b /// /// In Haskell Haxl, the applicative instance is used to keep fetched values in scope: /// /// > (a, b) <- (,) <$> fetch1 <*> fetch2 /// /// C# can't do nested lambda scoping, and uses transparent identifers instead. /// Because the transparent identifers aren't accessible to us, we use our own scoping system. /// /// This means our (a -> b) function is *always* (Scope -> Scope); /// we therefore can write our "Applicative" instance as simply a function that takes two Fetches. /// </remarks> public static Haxl Applicative(this Haxl fetch1, Haxl fetch2) { return(Haxl.FromFunc((cache, logger) => { var result1 = fetch1.Result(cache, logger); var result2 = fetch2.Result(cache, logger); return result1.Match ( done1 => result2.Match <Result> ( done2 => Done.New(compose(done2.AddToScope, done1.AddToScope)), blocked2 => Blocked.New(blocked2.BlockedRequests, blocked2.Continue.Map(done1.AddToScope)) ), blocked1 => result2.Match <Result> ( done2 => Blocked.New(blocked1.BlockedRequests, blocked1.Continue.Map(done2.AddToScope)), blocked2 => Blocked.New( blocked1.BlockedRequests.Concat(blocked2.BlockedRequests), blocked1.Continue.Applicative(blocked2.Continue) ) ) ); })); }
/// <summary> /// Monad instance for HaxlFetch. /// </summary> public static Haxl Bind(this Haxl fetch, Func <Scope, Haxl> bind) { return(Haxl.FromFunc((cache, logger) => { var result = fetch.Result(cache, logger); return result.Match( done => bind(done.AddToScope(Scope.New())).Result(cache, logger), blocked => Blocked.New(blocked.BlockedRequests, blocked.Continue.Bind(bind)) ); })); }
/// <summary> /// Repeatedly fetches requests until we have the result. /// </summary> public static async Task <Scope> Run(Haxl fetch, Scope scope, Func <IEnumerable <BlockedRequest>, Task> fetcher, HaxlCache cache, Action <HaxlLogEntry> logger) { var result = fetch.Result(cache, logger); return(await result.Match( done => Task.FromResult(done.AddToScope(scope)), async blocked => { await fetcher(blocked.BlockedRequests); return await Run(blocked.Continue, scope, fetcher, cache, logger); } )); }
/// <summary> /// Converts a list of applicative groups into a Haxl monad. /// </summary> public static Haxl ToFetch(List <ApplicativeGroup> split, string parentBind, Scope parentScope) { if (parentScope == null) { parentScope = Scope.New(); } Haxl finalFetch = null; Action <Func <Scope, Haxl> > bindToFinal = f => { finalFetch = finalFetch == null?f(parentScope) : finalFetch.Bind(f); }; foreach (var applicative in split) { bindToFinal(ApplicativeToHaxl(applicative, parentBind)); } return(finalFetch); }
/// <summary> /// Folds an applicative group into a Haxl monad. /// </summary> public static Func <Scope, Haxl> ApplicativeToHaxl(ApplicativeGroup applicative, string parentBind) { var expressions = applicative.Expressions; if (applicative.Expressions.Count == 1) { return(StatementToHaxl(expressions.First(), parentBind)); } return(scope => applicative.Expressions.Aggregate ( Haxl.FromFunc((c, l) => Done.New(s => s)), (group, be) => { var haxl = StatementToHaxl(be, parentBind)(scope); return group.Applicative(haxl); } )); }
/// <summary> /// Converts a project expression to Haxl monad. /// </summary> public static Func <Scope, Haxl> ProjectToHaxl(ProjectStatement project, string parentBind) { return(scope => Haxl.FromFunc((cache, logger) => { var rewritten = RebindToScope.Rebind(project.Expression); var result = rewritten.Compile().DynamicInvoke(scope); return Done.New(_ => { if (project.Expression.BindVariable == HAXL_RESULT_NAME && !scope.IsRoot && parentBind != null) { return scope.WriteParent(parentBind, result); } return scope.Add(project.Expression.BindVariable, result); }); })); }
private Blocked(IEnumerable <BlockedRequest> blocked, Haxl cont) { BlockedRequests = blocked; Continue = cont; }
public static Blocked New(IEnumerable <BlockedRequest> blocked, Haxl cont) { return(new Blocked(blocked, cont)); }