Overloading a method in Groovy using Closure arguments with different return types - grails

I'm reasonably proficient with Groovy insofar as my job requires, but not having a background in OOP means that some things still elude me, so apologies if some of the wording is a little off here (feel free to edit if you can make the question clearer).
I'm trying to create an overloaded method where the signature (ideally) differs only in the return type of the single Closure parameter. The Closure contains a method call that returns either an ItemResponse or ListResponse object, both of which could contain an object/objects of any type (which is the type I would like to infer).
The following code is a simplified version of what I'm trying to implement - an error handling method which takes a reference to a service call, safely attempts to resolve it, and returns the item/items from the response as appropriate:
public <T> T testMethod(Closure<ItemResponse<T>> testCall) {
testCall.call().item as T
}
public <T> List<T> testMethod(Closure<ListResponse<T>> testCall) {
testCall.call().items as T
}
Obviously this doesn't work, but is there any alternate approach/workaround that would achieve the desired outcome?

I'm trying to create an overloaded method where the signature
(ideally) differs only in the return type of the single Closure
parameter.
You cannot do that because the return type is not part of the method signature. For example, the following is not valid:
class Demo {
int doit() {}
String doit() {}
}

As mentioned by yourself and #jeffscottbrown, you can't have two methods with the same parameters but different return value. The workaround I can see here is to use a call-back closure. The return value of your testMethod would default to Object and you would provide an "unwrapper" that would the bit after the closure call (extract item or items). Try this out in your GroovyConsole:
class ValueHolder <T> {
T value
}
Closure<List<Integer>> c = {
[1]
}
Closure<ValueHolder<String>> d = {
new ValueHolder(value:'hello world')
}
Closure liu = {List l ->
l.first()
}
Closure vhsu = {ValueHolder vh ->
vh.value
}
// this is the generic method
public <T> Object testMethod(Closure<T> testCall, Closure<T> unwrapper) {
unwrapper(testCall.call()) as T
}
println testMethod(c, liu)
println testMethod(d, vhsu)
It works with both a list or a value holder.

Related

How to use extended generics in Dart? [duplicate]

I'm trying to call a static method from a generic type I receive.
Is that even possible?
Furthermore, I apply a Type constraint in order to only manipulate the object from its parent class.
Here is a short example of what I'm trying to achieve:
class A {
static func() {
print("A");
}
}
class B extends A {
static func() {
print("B");
}
}
concret<T extends A>() {
T.func(); // I expected a print('B')
}
main() {
concret<B>();
}
No, it's not possible.
Dart static method invocations are resolved at compile-time, so it's not possible to call them on type variables which only have a value at run-time.
If it was possible, it would be completely unsafe. Anyone can create a class C extending A which does not have a static func member and invoke concret<C>();. Since static members are not inherited, it would have to give you a run-time error, and there is nothing you can do to detect that at compile-time. That is the primary reason why it is not allowed.

how to use setMethodCallHandler [duplicate]

I am writing a native plugin that, in some cases, has to call functions in the Flutter portion of the app, written in Dart.
How it's achieved, is explained here:
https://flutter.io/platform-channels/
Furthermore, an example of invoking a method from the native/platform part towards the Dart/non-native is here:
https://github.com/flutter/plugins/tree/master/packages/quick_actions
Now, this example is really nice in case the platform only needs to invoke a method, i.e. that call returns nothing/void, but in case it needs to invoke a function, i.e. needs a return value from the non-native/Dart part, I could not have found an example or documentation on the internet. I believe it can be implemented though, because in the native Java part, there is a method:
public void invokeMethod(String method, Object arguments, MethodChannel.Result callback)
So, there is a callback object that could have a return value from the non-native part - or, I am mistaken here, and there is currently no way of returning a value from the non-native Dart portion of the app?
The signature is void setMethodCallHandler(Future<dynamic> handler(MethodCall call)), so we need to provide a function at the Dart end that returns Future<dynamic>, for example _channel.setMethodCallHandler(myUtilsHandler);
Then implement the handler. This one handles two methods foo and bar returning respectively String and double.
Future<dynamic> myUtilsHandler(MethodCall methodCall) async {
switch (methodCall.method) {
case 'foo':
return 'some string';
case 'bar':
return 123.0;
default:
throw MissingPluginException('notImplemented');
}
}
At the Java end the return value is passed to the success method of the Result callback.
channel.invokeMethod("foo", arguments, new Result() {
#Override
public void success(Object o) {
// this will be called with o = "some string"
}
#Override
public void error(String s, String s1, Object o) {}
#Override
public void notImplemented() {}
});
In Swift, the return value is an Any? passed to the result closure. (Not implemented is signaled by the any parameter being the const NSObject value FlutterMethodNotImplemented.)
channel.invokeMethod("foo", arguments: args, result: {(r:Any?) -> () in
// this will be called with r = "some string" (or FlutterMethodNotImplemented)
})

how to implement string concatenation alike extension function with unknown arguments number using Saxon-HE?

I want to add a custom xpath extension function to the Saxon-HE transformer. This custom function should accept one or more arguments. Let's use the string concatenation analogy for concatenating one or more string arguments. Following the sample on the saxon page, i wrote the following code:
ExtensionFunction myconcat = new ExtensionFunction() {
public QName getName() {
return new QName("http://mycompany.com/", "myconcat");
}
public SequenceType getResultType() {
return SequenceType.makeSequenceType(
ItemType.STRING, OccurrenceIndicator.ONE
);
}
public net.sf.saxon.s9api.SequenceType[] getArgumentTypes() {
return new SequenceType[]{
SequenceType.makeSequenceType(
ItemType.STRING, OccurrenceIndicator.ONE_OR_MORE)};
}
public XdmValue call(XdmValue[] arguments) throws SaxonApiException {
//concatenate the strings here....
String result = "concatenated string";
return new XdmAtomicValue(result);
}
};
i have expected that the following xpath expression would work in an xsl file
<xsl:value-of select="myconcat('a','b','c','...')">
Unfortunately i got the following exception:
XPST0017: Function myconcat must have 1 argument
What is the right way of creating a custom function for this use case?
Thanks.
The standard mechanisms for creating extension functions don't allow a variable number of arguments (it's not really pukka to have such functions in the XPath view of the world - concat() is very much an exception).
You can do it by creating your own implementation of the class FunctionLibrary and adding your FunctionLibrary to the static context of the XSLT engine - but you're deep into Saxon internals if you attempt that, so be prepared for a rough ride.

How to prevent function return result declaratively?

Assume such conditions:
Some operation does not provide possibility of returning the result.
This operation declared as callback
Using typedef not recommended
Some operation provide of returning the result.
This operation declared as callback
Using typedef not recommended
Assume such scenario:
void main() {
executeVoidOperation(methodNonVoid); // Must throw if method void?
executeNonVoidOperation(methodVoid); // Must throw if method non-void?
}
int methodNonVoid() {
return 0;
}
void methodVoid() {
}
void executeVoidOperation(void operation()) {
operation(); // Must throw if method non-void?
}
void executeNonVoidOperation(dynamic operation()) {
var result = operation(); // Must throw if method void?
print(result); // Result of void operation? (if such passed as argument)
}
Displayed results:
null
Questions (where I wrong?):
Null is object. From where this null appeared (as result) if void function cannot return result (even null)?
Functions with different return types in Dart assumed as the same (not conflicting) types?
How in Dart called this function transformations?
executeNonVoidOperation(methodVoid); works because the callback is defined as dynamic operation(). dynamic can be anything, including void. It's the same as if you just don't specify a type.
The null value stems from a simple rule in Dart. Quoted from the Dart Language Tour:
All functions return a value. If no return value is specified, the statement return null; is implicitly appended to the function body.
That means that every void method always returns null. If you try to return something else, you'll get a runtime error (in checked mode).
executeVoidOperation(methodNonVoid); is a bit more tricky - I'd expect it to throw a runtime error, but it seems the callback is treated as dynamic operation() instead of void operation(). Dart Editor's analyzer seems to think that, too. This may be either a bug or a design choice by the Dart team.

What does Cannot create delegate without target for instance method or closure mean

I am using vala.
This is the source code that gives that compile time bug :
private Gee.HashMap<string,VoidFunc> fill_actions()
{
var actions = new Gee.HashMap<string,VoidFunc>();
MainWindow win = window;
actions["t"] = () => _puts(win.title);
return actions;
}
First I tried to access this.window directly but that gave another error so I tried this with a local scope variable.
Error when doing directly this.window :
This access invalid outside of instance methods
It sounds like VoidFunc is declared with [CCode (has_target = false)]. What that means is that no context information is passed to it, and AFAIK that is the only way delegates work as generic type arguments. The reason for this is limitations in C, so assuming VoidFunc looks like this:
[CCode (has_target = false)]
public delegate void VoidFunc ();
What you'll get in C is something like this:
typedef void (*VoidFunc)();
As opposed to something like this if you didn't have the [CCode (has_target = false)]:
typedef void (*VoidFunc)(gpointer user_data);
When you pass around callbacks in C you generally do so with between one and three arguments. Something with all three would look like this:
void foo (VoidFunc void_func, gpointer user_data, GDestroyNotify notify);
The first parameter is the actual function. The second parameter is the value to pass as user_data to the callback, and is what Vala uses to pass context information to the callback (which is what allows it to act as an instance method, or even a closure). The third parameter is used to specify a function to free user_data when it is no longer needed.
What [CCode (has_target = false)] means is that the delegate doesn't have a user_data argument, and therefore cannot be used as a closure or instance method.
The reason this is necessary with a generic argument is that generics look something like this at the C level:
void foo_bar (gpointer data, GDestroyNotify notify);
The first parameter is the data that you want to use as a generic value, the second is actually only added if the generic argument is owned (as it is in the case of the set methods in Gee), and is called with user_data as an argument when user_data is no longer needed.
As you can see, when trying to use a delegate as a generic, there is nowhere to put the user_data argument, which is why Vala only allows delegates without targets to be generic arguments.
The solution is basically to wrap the delegate in a class:
public delegate void VoidFunc ();
public class YourClass {
private class VoidFuncData {
public VoidFunc func;
public VoidFuncData (owned VoidFunc func) {
this.func = (owned) func;
}
}
private Gee.HashMap<string,VoidFuncData> fill_actions() {
var actions = new Gee.HashMap<string,VoidFuncData>();
string win = "win";
actions["t"] = new VoidFuncData (() => GLib.debug (win));
return actions;
}
}

Resources