How to declare a function type throwable? - kotlin-multiplatform

I know in kotlin-multiplatform, you can annotate a function with #Throws to make it a throwable function. So that converted Java/Swift code has the throws modifier to function signature.
However, when I have a function's parameter who is a function as well, how do I mark this function throwable?
e.g:
fun foo(run : () -> Unit) {}
the above function has a parameter "run", which takes a closure/function. But this parameter will be compiled without "throws" modifier, so that in Java/Swift, I can't throw anything in the closure that is passed to this function.
I tried to add the #Throws annotation to the function. Putting it outside of function declaration results the foo() function marked as "throws". Putting it in the parameter parenthesis, either before or after the colon, results an error saying annotation is not applicable to target "value parameter"/"type usage"
To summarize, what I'm looking for is something like:
fun foo(#Throws(Throwable::class) run : () -> Unit)
or
fun foo(run : #Throws(Throwable::class) () -> Unit)
Any hint/help on this issue is very very appreciated!

You can not annotate function parameter with #Throws, because the annotation has following targets: AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER and AnnotationTarget.CONSTRUCTOR.
Created feature request in Kotlin tracker: https://youtrack.jetbrains.com/issue/KT-39688

Related

What is the correct syntax to Verify a mock logger call in F#?

I'm unit testing an F# function that calls ILogger.LogInformation. I'm attempting to verify that the function made the call as expected. Here is the verify statement I have so far:
let mockLogger = Mock<ILogger<MyFunction>>()
// call function that uses ILogger.LogInformation.
mockLogger.Verify(fun x -> x.Log(
LogLevel.Information,
It.IsAny<EventId>(),
It.IsAny(),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception, string>>()), Times.Once)
When I try this I get the following error:
System.ArgumentException: Expression of type 'System.Void' cannot be used for constructor parameter of type 'Microsoft.FSharp.Core.Unit' (Parameter 'a...
System.ArgumentException
Expression of type 'System.Void' cannot be used for constructor parameter of type 'Microsoft.FSharp.Core.Unit' (Parameter 'arguments[0]')
at System.Dynamic.Utils.ExpressionUtils.ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arguments, ParameterInfo pi, String methodParamName, String argumentParamName, Int32 index)
at System.Dynamic.Utils.ExpressionUtils.ValidateArgumentTypes(MethodBase method, ExpressionType nodeKind, ReadOnlyCollection`1& arguments, String methodParamName)
at System.Linq.Expressions.Expression.New(ConstructorInfo constructor, IEnumerable`1 arguments)
at Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.ConvExprToLinqInContext(ConvEnv env, FSharpExpr inp) in D:\a\_work\1\s\src\fsharp\FSharp.Core\Linq.fs:line 616
at Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.ConvExprToLinqInContext(ConvEnv env, FSharpExpr inp) in D:\a\_work\1\s\src\fsharp\FSharp.Core\Linq.fs:line 599
at Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.QuotationToLambdaExpression[T](FSharpExpr`1 e) in D:\a\_work\1\s\src\fsharp\FSharp.Core\Linq.fs:line 698
The exception is thrown when it calls the Verify method. How can I change this to get past this error?
I don't know much about Moq, but I think part of the problem here is that you need another pair of parentheses around your lambda to separate it from the Times.Once argument. Try something like this instead:
mockLogger.Verify(
(fun x -> x.Log(
LogLevel.Information,
It.IsAny<EventId>(),
It.IsAny(),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception, string>>())),
Times.Once)
Without the extra parens, your lambda returns a tuple, so your code is currently calling this method:
Verify: expression: Expression<Action<'T>> -> unit
But I think you want to call this method instead:
Verify: expression: Expression<Action<'T>> * times: Times -> unit
Here's a simpler example that should make the difference clear:
Verify(fun x -> x, 1) // call Verify with a single argument (a lambda that returns a tuple)
Verify((fun x -> x), 1) // call Verify with two arguments (a lambda and the literal value 1)

How can I print the name of a generic type?

I have a dart method that takes a generic type.
At the top of the method I want to print the name of the type that was passed as T, is this possible?
eg
void myMethod<T> () {
print("myMethod called with type="+????);
}
If myMethod is called with myMethod() it would print "myMethod called with type=String".
Yes and no.
You can write
void myMethod<T> () {
print("myMethod called with type=$T");
}
The only problem is that the Dart libraries don't promise that a Type object (which is what T evaluates to) will have a toString which returns the source name of the type in the original program.
It generally does give that string, but if you compile for the web with "minification", it might not keep the source names available.
There is also no promise that the toString of Type won't change in the future, since it is entirely unspecified.

The argument type '(On....) -> Null' can't be assigned to the parameter type `(Object) -> void`

I've enabled strict mode in my Dart code-base today and have fixed most of the warnings and errors.
This one I'm not sure how to fix ...
This is my method signature:
StreamSubscription listen(Type eventType, void onData(Object event), { Function onError, void onDone(), bool cancelOnError}) {
... code here ...
}
When doing: _bus.listen(OnRowDoubleClick, (OnRowDoubleClick event){
I'm getting
The argument type '(OnRowDoubleClick) -> Null' can't be assigned to the parameter type `(Object) -> void`
I can change the method signature to
StreamSubscription listen(Type eventType, Null onData(Object event), {...`,
(which doesn't make sense since I don't actually want to return anything), the error changes to
The argument type '(OnRowDoubleClick) -> Null' can't be assigned to the parameter type `(Object) -> Null`
Changing the method call to this:
_bus.listen(OnRowDoubleClick, (event){
The error goes away, but now I have to cast event as OnRowDoubleClick the whole time.
Is there a better way of dong this?
* Solution *
Solution is to mark the listener with T:
StreamSubscription listen<T>(Type eventType, Null onData(T event), {
Now you can call _bus.listen<OnRowDoubleClick>(OnRowDoubleClick, (event){ and the event will be of type OnRowDoubleClick
The problem is that listen expects the argument onData to be a function that takes any Dart Object as an argument. You are passing a function that only takes OnRowDoubleClick as an argument, so strong mode gives an error.
You could change your listen method so that it takes a type parameter, take a look at Stream#listen for an example.

Are instance methods closures?

In the book, "Swift Programming Language 3.0", it mentioned that types of closure include:
Global functions are closures that have a name and do not capture
any values
Nested function are closures that have a name and can
capture values from their enclosing function
Closure expression are
unnamed closure written in a lightweight syntax that can capture
values from their surrounding context
I was just wondering does a function that exist in class scope count as a closure? One can certainly pass around such function as an argument to other function, but is it a closure?
Yes! Absolutely! Here's an example that uses the lowercased() method of String.
let aClosure: (String) -> () -> String = String.lowercased
let anUpperCasedString = "A B C"
print(anUpperCasedString)
let aLowerCaseString = aClosure(anUpperCasedString)()
print(aLowerCaseString)
You can see that the type of this closure is (String) -> () -> String. This is because String.lowercased is completely unapplied, it has no clue what instance it's operating on.
Calling aClosure(anUpperCasedString) will return a closure that's now () -> String. Baked into it is the instance it'll operate on. Only when you call this new closure with no params (()), will it actually execute the body of lowercased(), operating on the instance you gave it in the previous step, and return you the String result.
As a consequence, this is also valid:
let aLowerCaseString = String.lowercased("QWERTY")()
It just does all the steps above in one inlined step.
This technique is called function currying. This post talks more about this technique (called function currying) as it applies to instance methods in Swift.

DART: syntax of future then

I don´t understand the syntax of the then() clause.
1. myFuture(6).then( (erg) => print(erg) )
What´s (erg) => expr syntactically?
I thougt it could be a function, but
then( callHandler2(erg)
doesn´t work, Error:
"Multiple markers at this line
- The argument type 'void' cannot be assigned to the parameter type '(String) ->
dynamic'
- Undefined name 'erg'
- Expected to find ')'"
2. myFuture(5).then( (erg) { callHandler(erg);},
onError: (e) => print (e)
What´s `onError: (e) => expr"` syntactically?
3. Is there a difference between the onError: and the .catchError(e) variants?
1) The Fat Arrow is syntactic sugar for short anonymous functions. The two functions below are the same:
someFuture(arg).then((erg) => print(erg));
// is the same as
someFuture(arg).then((erg) { return print(erg); });
Basically the fat arrow basically automatically returns the evaluation of the next expression.
If your callHandler2 has the correct signature, you can just pass the function name. The signature being that it accept the number of parameters as the future will pass to the then clause, and returns null/void.
For instance the following will work:
void callHandler2(someArg) { ... }
// .. elsewhere in the code
someFuture(arg).then(callHandler);
2) See answer 1). The fat arrow is just syntactic sugar equivalent to:
myFuture(5).then( (erg){ callHandler(erg);}, onError: (e){ print(e); });
3) catchError allows you to chain the error handling after a series of futures. First its important to understand that then calls can be chained, so a then call which returns a Future can be chained to another then call. The catchError will catch errors both synchronous and asynchronous from all Futures in the chain. Passing an onError argument will only deal with an error in the Future its an argument for and for any synchronous code in your then block. Any asynchronous code in your then block will remain uncaught.
Recent tendency in most Dart code is to use catchError and omit the onError argument.
I will attempt to elaborate more on Matt's answer, hopefully to give more insights.
What then() requires is a function (callback), whose signature matches the future's type.
For example, given a Future<String> myFuture and doSomething being any function that accepts a String input, you can call myFuture.then(doSomething). Now, there are several ways to define a function that takes a String in Dart:
Function(String) doSomething1 = (str) => /* do something with str */ // only one command
Function(String) doSomething2 = (str) { /* do something with str */ } // several commands
Function(String) doSomething3 = myFunction;
myFunction(String) { // Dart will auto imply return type here
/* do something with str */ // several commands
}
Any of those 3 function definitions (the right hand side of =) could go inside then(). The first two definitions are called lambda functions, they are created at runtime and cannot be reused unless you manually copy the code. Lambda functions can potentially yield language-like expressions, i.e. (connection) => connection.connect(). The third approach allows the function to be reused. Lambda functions are common in many languages, you can read more about it here: https://medium.com/#chineketobenna/lambda-expressions-vs-anonymous-functions-in-javascript-3aa760c958ae.
The reason why you can't put callHandler2(erg) inside then() is because callHandler2(erg) uses an undefined variable erg. Using the lambda function, you will be able to tell then() that the erg in callHandler2(erg) is the output of the future, so it knows where to get erg value.

Resources