MeAmAnUsername / pie

Pipelines for Interactive Environments
Apache License 2.0
0 stars 0 forks source link

Higher order functions #16

Open MeAmAnUsername opened 4 years ago

MeAmAnUsername commented 4 years ago

Allow creating lambdas, passing lambdas, referring to functions by name and applying functions.

Use cases

Some of the options in #15

Call multiple functions on a file and then handle all results the same afterward

val foo1 = print(postProcess(transform1(preProcess(file))));
val foo2 = print(postProcess(transform2(preProcess(file))));
val foo3 = print(postProcess(transform3(preProcess(file))));
val foo4 = print(postProcess(transform4(preProcess(file))));
... etc

becomes

val transformations = [transform1, transform2, transform3, transform4, ...];
val foos = [print(postProcess(transform(preProcess(file)))) | transform <- transformations];

Reparsing can be made generic over which specific parse and prettyprint are used

Could create a spoofax 3 project data with references to specific parse, style, prettyprint etc. functions (depends on #33). This would make the reparse test function above just take such a spoofaxProject data.

data spoofaxProject = {
  parse: func(ast: IStrategoTerm) -> IStrategoTerm
  prettyprint: func(ast: IStrategoTerm) -> string
  // etc.
}
func createReparseTest(project: spoofaxProject) -> IStrategoTerm -> unit = {
  ast1: IStrategoTerm => {
    val ast2 = project.parse(project.prettyprint(ast1));
    assertEquals(ast1, ast2, "Expected prettyprinting and reparsing an AST to yield the same AST")
  }
}

// requires #62
val reparseJavaTest = createReparseTest(javaProject)

Allows threading values in list comprehensions, i.e. reduceLeft. See #61.

Syntax

Lambdas

Exp.Lambda = Lambda
Lambda.SingleParam = [[TBind] => [Exp]] // e.g. x: int => x + 5
Lambda.MultiParam = [([{TBind ", "}*]) => [Exp]] // e.g. (x: int, y: int) => x + y

Extra: allow as parameter name multiple times, means that parameter is ignored. Bonus extra: does not require type? Could use TopTy in that case?

Function Types

Type.FuncTyHeader = FuncHeader, e.g. val parse: func(ast: IStrategoTerm) -> IStrategoTerm = example:cpp:parse; Option: allow leaving out the func and parameter name, e.g. val plus: (int, int) -> int Requires that FuncHeader becomes right associative so that a function can return a function:

val curriedPlus: int -> int -> int = (x: int) => (y: int) => x + y;
val add1 = curriedPlus(1);
val two = add1(1);
val three = curriedPlus(1)(2);

Application

Exp.Application = <<Exp>(<{<Exp> ", "}*>)> // or Exp.Apply; e.g. plus(a, b) This is ambiguous with Call (foo() could be application of variable foo or call to function foo. Remove function calls, application can be used on both Func and Var? See https://slde.slack.com/archives/C7254SF60/p1594220454206400

val const5 = () => 5;
val five = const5();

Should be left associative, see curriedPlus above

MeAmAnUsername commented 3 years ago

Problem: tasks and functions are different, because a task takes an implicit ExecContext and the rest of the parameters in an nested Input class. Might be solvable by explicitly disambiguating between the two with func and function (todo: create better name. func and task is good except that func in a declaration means task).