Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[epsilon-dev] EOL lambdas outside of FirstOrderOperationCallExpression

Hi all,

 

You may recall that I implemented a somewhat half-baked feature which allows users to call native methods that take a functional interface as a parameter using EOL’s declarative operation call _expression_ syntax. There were two limitations with this:

 

  • Methods requiring both simple parameters (i.e. an object) and lambdas could not be parsed (and thus invoked) properly.
  • Lambda expressions could not be assigned to variables for re-use or returned from a statement block.

 

I believe I may have found a workaround for the latter; which should in turn enable the former, without any changes in the grammar. The trick comes from having a “LambdaFactory” object in the FrameStack, which users can call methods on to effectively return them the lambda _expression_ that was passed in. Of course this could still be achieved through DynamicOperation and the mere presence of methods with the appropriate signature, but does not take advantage of an optimisation opportunity, since we know what these functional interfaces are in advance.

For all of the interfaces in the java.util.function package (bar the tedious primitive wrappers and converters which are not applicable to Epsilon), there is an EOL variant allowing for throwing of checked exceptions which extend the java.util.function ones.

By allowing users to assign lambdas to variables, they can in turn call methods requiring both lambdas an simple parameters through the regular OperationCallExpression, since the lambda is treated as an ordinary object.

An example of how this might work is as follows:

 

var isEven = LambdaFactory.predicate(i | i > 0 and i.mod(2) == 0);

var mySupplier = LambdaFactory.supplier(| thisWillNeverHappen);

var doubler = LambdaFactory.unary(i | i * 2);

var toString = LambdaFactory.func(t | t.toString());

 

Sequence{0..10}.stream()

         .filter(isEven)

         .findFirst()

         .orElseGet(mySupplier)

         .println();

 

Native("java.util.stream.Stream")

         .iterate(1, doubler)

         .limit(12l)

         .forEach(LambdaFactory.printlnConsumer);

 

Notice how with the factory, we can also have pre-defined implementations for common operations, such as printing or a predicate which tests whether the parameter is defined.

Currently this is available in the smadani/lambda-support branch. I am not sure if this is an elegant solution or a hack which users may find ugly or confusing. I should also note that the names of the methods in LambdaFactory can be aliased; the implementation is basically a switch statement which implements the correct functional interface based on the name of the method (e.g. Supplier is also aliased to “getter”).

I would be keen to read your thoughts/suggestions on this, and if they are largely positive I will add tests and propose merging into the main branch.

 

Thanks,

Sina


Back to the top