/// <summary> /// Checks if this expression binds any variables bound in the current group. /// </summary> private static bool ShouldSplit(ExpressionVariables vars, List <string> boundInGroup) { if (vars == null) { return(false); } if (vars.BindsNonTransparentParam) { return(true); } if (vars.Bound.Any(boundInGroup.Contains)) { return(true); } return(false); }
public LetStatement(string name, LambdaExpression expression, ExpressionVariables variables) { Name = name; Expression = expression; Variables = variables; }
public BindProjectStatement(BindProjectPair expressions, ExpressionVariables bindVars, ExpressionVariables projectVars) { Expressions = expressions; BindVariables = bindVars; ProjectVariables = projectVars; }
/// <summary> /// Groups statements that can be fetched concurrently. /// </summary> public static List <ApplicativeGroup> MakeApplicative(LambdaExpression initial, IEnumerable <QueryStatement> statements) { var applicatives = new List <ApplicativeGroup>(); LambdaExpression previousProject = initial; ExpressionVariables previousProjectVars = null; var currentApplicative = new List <Statement>(); var boundInGroup = new List <string>(); Action split = () => { if (currentApplicative.Any()) { applicatives.Add(new ApplicativeGroup(currentApplicative)); } currentApplicative = new List <Statement>(); boundInGroup.Clear(); }; var first = true; foreach (var statement in statements) { var blockNumber = statement.BlockNumber; Func <LambdaExpression, string, BoundExpression> boundExpression = (e, s) => new BoundExpression(e, s, blockNumber); statement.Match( bind => { // The result of the previous monad is bound to this variable name. var previousBindName = bind.BindVariables.ParameterNames.First(); var prefixed = PrefixedVariable(blockNumber, previousBindName); if (first) // Add the initial fetch. { currentApplicative.Add(new BindStatement(boundExpression(initial, prefixed))); } var shouldSplit = ShouldSplit(bind.BindVariables, boundInGroup); if (shouldSplit) { split(); } // If we're at the beginning of a new block, we should add the previous project statement. if (bind.StartsBlock && !first) { var splitBlock = previousProjectVars != null && ShouldSplit(previousProjectVars, boundInGroup); if (splitBlock) { split(); } boundInGroup.Clear(); currentApplicative.Add( // This project was from the previous block, so we subtract one here. new ProjectStatement(new BoundExpression(previousProject, prefixed, blockNumber - 1))); if (shouldSplit) { split(); } } // The result of the current monad is bound to the second parameter of the project fuction: // x.SelectMany( // a => m a, // a, b => new { a, b } // // ^ this b is the result of m a. // ) var bindName = bind.ProjectVariables.ParameterNames.Last(); var prefixedBindName = PrefixedVariable(blockNumber, bindName); currentApplicative.Add(new BindStatement(boundExpression(bind.Expressions.Bind, prefixedBindName))); // We take the final projection function and bind it to the HAXL_RESULT_NAME constant. if (bind.IsFinal) { split(); currentApplicative.Add(new ProjectStatement(boundExpression(bind.Expressions.Project, HAXL_RESULT_NAME))); } // Push out the project function and its variables in case it's the final select of // a nested block and we need to bind it. previousProject = bind.Expressions.Project; previousProjectVars = bind.ProjectVariables; // If we've split the only dependency is the current monad. if (shouldSplit) { boundInGroup.Add(bindName); } else { boundInGroup.AddRange(bind.ProjectVariables.ParameterNames); } return(UnitVal); }, let => { var paramNames = let.Variables.ParameterNames; var previousBindName = paramNames.First(); var prefixed = PrefixedVariable(blockNumber, previousBindName); if (ShouldSplit(previousProjectVars, boundInGroup)) { split(); } // The initial lambda is always returns a monad, so we place it into a bind. if (first) { currentApplicative.Add(new BindStatement(boundExpression(previousProject, prefixed))); } else if (!LetExpression.IsLetExpression(previousProject)) { currentApplicative.Add(new ProjectStatement(boundExpression(previousProject, prefixed))); } boundInGroup.Add(let.Variables.ParameterNames.First()); if (ShouldSplit(let.Variables, boundInGroup)) { split(); } boundInGroup.Add(let.Name); currentApplicative.Add(new ProjectStatement(boundExpression(let.Expression, PrefixedVariable(blockNumber, let.Name)))); return(UnitVal); } ); first = false; } if (currentApplicative.Any()) { applicatives.Add(new ApplicativeGroup(currentApplicative)); } return(applicatives); }