My problem comes from an error handler that I need to write. The handler is meant to catch an error, do an action 'A', and then proceed to retry the original function that failed in the first place. The blocker comes when I need to generalise the handler to be able to accept any function definition.
For example:
fun foo(a: Any) {
// Do something and return null if unsuccessful
}
fun bar(b: Any, c: Any) {
// Do something else and return false if unsuccessful
}
fun handler(fn: Function???, args: Array[arguments???]) {
log.warn("Error has been caught at <${functionName}>, doing action A and retrying")
// Do action A
log.warn("Retrying <${functionName}>")
fn(args)
}
fun main() {
val resultFoo = foo('bar')
if (resultFoo == null) {
handler(foo, ['bar'])
}
val resultBar = bar('foo', { 'foo': 'bar' })
if (!resultBar) {
handler(bar, ['foo', { 'foo': 'bar' }])
}
}
As you can see, I have two functions foo and bar with different definitions which I would like to be able to call from handler. Otherwise I would need to copy-paste the body inside handler to each individual function and handle the error inside there. I was hoping to be able to generalise this and be able to reuse it.
If you want to avoid reflection, which would provide no compile-time error checking, you should overload your function to have variants with different numbers of variables. Then you can use generics, and you'll get compile-time checking.
There's not a clean way to pass functions along with their names, so I made the name for logging a separate argument in this example.
// zero args handler
inline fun <R> handler(loggedFunctionName: String?, fn: ()->R): R {
val functionName = "<$loggedFunctionName>" ?: "function"
log.warn("Error has been caught at $functionName, doing action A and retrying")
// Do action A
log.warn("Retrying $functionName")
return fn()
}
// one arg handler
inline fun <A, R> handler(loggedFunctionName: String?, fn: (A)->R, argA: A): R
= handler(loggedFunctionName) { fn(argA) }
// two args handler
inline fun <A, B, R> handler(loggedFunctionName: String?, fn: (A, B)->R, argA: A, argB: B): R
= handler(loggedFunctionName) { fn(argA, argB) }
fun main() {
val resultFoo = foo("bar")
if (resultFoo == null) {
handler("foo", ::foo, "arg")
}
val resultBar = bar("arg1", "arg2")
if (!resultBar) {
handler("bar", ::bar, "arg1", "arg2")
}
}
However, it seems you could reduce boilerplate further by incorporating your logical test of the result as another argument:
// zero args handler
inline fun <R> handler(loggedFunctionName: String?, fn: ()->R, verifyResult: (R)->Boolean): R {
val firstResult = fn()
if (verifyResult(firstResult)){
return firstResult
}
val functionName = "<$loggedFunctionName>" ?: "function"
log.warn("Error has been caught at $functionName, doing action A and retrying")
// Do action A
log.warn("Retrying $functionName")
return fn()
}
// one arg handler
inline fun <A, R> handler(loggedFunctionName: String?, fn: (A)->R, verifyResult: (R)->Boolean, argA: A): R
= handler(loggedFunctionName, { fn(argA) }, verifyResult)
// two args handler
inline fun <A, B, R> handler(loggedFunctionName: String?, fn: (A, B)->R, verifyResult: (R)->Boolean, argA: A, argB: B): R
= handler(loggedFunctionName, { fn(argA, argB) }, verifyResult)
fun main() {
handler("foo", ::foo, { it != null }, "arg")
handler("bar", ::bar, { it }, "arg1", "arg2")
}
First of all you need to know, what exactly you want to generalize.
As I can see from your example, you want to repeat function call with same arguments. So instead all this messing with argument-arrays you can wrap payload (function call with arguments) into lambda and call it twice.
Second question is repeat condition. In foo it's result == null, in bar it's result == false. So condition is different for different functions. You can calculate condition in separate lambda.
So you can write something like this:
fun <T, F: KFunction<*>> F.callAndRepeatOnCondition (
payload: (F) -> T,
condition: (T) -> Boolean,
): T {
var result = payload(this)
if (condition(result)) {
println("log: doing something and repeat function ${this.name} ")
println("some super logic before repeat")
result = payload(this)
}
return result
}
fun foo(a: Int, b: String): String? { return null }
fun bar(c: Int): Boolean { return false }
fun main() {
val resultFoo = ::foo.callAndRepeatOnCondition({ it(1, "a") }, { it == null })
val resultBar = ::bar.callAndRepeatOnCondition({ it(2) }, { !it })
}
PS. If you don't need to log function name or can pass it explicitly, here is a little less ugly solution:
fun foo(a: Int, b: String): String? { return null }
fun bar(c: Int): Boolean { return false }
fun <T> (() -> T).repeatIf(condition: (T) -> Boolean): T {
val result = this()
if (!condition(result)) return result
println("some super logic before repeat")
return this()
}
fun <T> (() -> T).repeatWithLoggingIf(logName: String, condition: (T) -> Boolean): T {
val result = this()
if (!condition(result)) return result
println("log: repeating $logName")
println("some super logic before repeat")
return this()
}
fun main() {
run { // without logName
val resultFoo = { foo(1, "a") }.repeatIf { it == null }
val resultBar = { bar(2) }.repeatIf { !it }
}
run { // with logName
val resultFoo = { foo(1, "a") }.repeatWithLoggingIf("foo") { it == null }
val resultBar = { bar(2) }.repeatWithLoggingIf("bar") { !it }
}
}
Related
What I want to make is a function, that returns itself, so I can call it like this:
foo()()...()
In C# it would be done via delegates:
delegate SelfFunc SelfFunc();
static void Main() {
SelfFunc foo = null;
foo = () => {
return foo;
};
foo()()...();
}
Anticipating questions like "why implement such silly behavior?": I want to sum numbers in a very strange way using single function continues calls: foo(1)(2)(3)() = 6, but in this question I just want to know how to return function itself. Example realization of this method that I made in C#. This is all just for fun and to learn Rust:
static int sum = 0;
delegate dynamic InfFunc(int i = int.MaxValue);
static void InfFuncTest() {
InfFunc f = null;
f = (int i) => {
if(i == int.MaxValue) {
return sum;
}
sum += i;
return f;
};
var g = f;
var value = g(1)(2)(3)();
Console.WriteLine(value);
}
A function that returns itself is possible on nightly.
First you need to enable the features unboxed_closures and fn_traits.
Then you can define a struct which, when called, returns self. The full code looks something like this:
#![feature(unboxed_closures, fn_traits)]
struct SelfFunc;
impl FnOnce<()> for SelfFunc {
type Output = SelfFunc;
extern "rust-call" fn call_once(self, _args: ()) -> Self::Output {
self
}
}
Then you can call the function as many times as you want:
fn main() {
let f = SelfFunc;
f()()()()()();
}
Based on #cameron1024's answer, you can "overload" using traits, but you will need 2 structs to handle the empty case properly of foo() (here called Add) without any arguments returning 0.
#![feature(unboxed_closures, fn_traits)]
struct Add;
impl FnOnce<(u32,)> for Add {
type Output = AddImpl;
extern "rust-call" fn call_once(self, args: (u32,)) -> Self::Output {
AddImpl(args.0)
}
}
impl FnOnce<()> for Add {
type Output = u32;
extern "rust-call" fn call_once(self, _args: ()) -> Self::Output {
0
}
}
struct AddImpl(u32);
impl FnOnce<()> for AddImpl {
type Output = u32;
extern "rust-call" fn call_once(self, _args: ()) -> Self::Output {
self.0
}
}
impl FnOnce<(u32,)> for AddImpl {
type Output = AddImpl;
extern "rust-call" fn call_once(self, args: (u32,)) -> Self::Output {
Self(self.0 + args.0)
}
}
fn main() {
dbg!( Add(1)(2)(3)() );
dbg!( Add() );
}
Playground
If you do not care about the no-args foo() requirement, you can make Add a tuple struct instead and remove AddImpl:
#![feature(unboxed_closures, fn_traits)]
struct Add(u32);
impl FnOnce<(u32,)> for Add {
type Output = Add;
extern "rust-call" fn call_once(self, args: (u32,)) -> Self::Output {
Add(self.0 + args.0)
}
}
impl FnOnce<()> for Add {
type Output = u32;
extern "rust-call" fn call_once(self, _args: ()) -> Self::Output {
self.0
}
}
fn main() {
dbg!( Add(1)(2)(3)() );
//dbg!( Add() ); // doesn't compile
}
Playground
Although I should note that this likely isn't such a great idea, using an slice/iterator would likely result in cleaner code:
fn main() {
dbg!([1, 2, 3].iter().copied().sum::<u32>());
}
Playground
I started working with moshi a couple of weeks ago, so maybe I am missing something trivial, but I spent already quite a bit of time trying to fix this without success, so here is my question.
Having the following reproducible code:
fun main() {
val moshi = Moshi.Builder().add(OptionalAdapter).build()
val objectToSerialize = DummyObject()
val json = moshi.adapter(DummyObject::class.java).serializeNulls().toJson(objectToSerialize)
println(json)
}
#JsonClass(generateAdapter = true)
data class DummyObject(val value: Int=123, val someNullable: String? = null,
val someNotPresent: Optional<String> = Optional.NotPresent,
val somePresent: Optional<String> = Optional.Present("aaaa"))
class OptionalAdapter<T>(private val valueAdapter: JsonAdapter<T>) : JsonAdapter<Optional<T>>() {
#Suppress("UNCHECKED_CAST")
override fun fromJson(reader: JsonReader) = Optional.Present(valueAdapter.fromJson(reader) as T)
override fun toJson(writer: JsonWriter, value: Optional<T>?) {
when (value) {
is Optional.NotPresent -> writer.nullValue()
is Optional.Present -> valueAdapter.serializeNulls().toJson(writer, value.value)
}
}
companion object Factory : JsonAdapter.Factory {
override fun create(type: Type, annotations: Set<out Annotation>, moshi: Moshi): JsonAdapter<*>? {
return if (Types.getRawType(type) == Optional::class.java && annotations.isEmpty()) {
val valueType = if(type is ParameterizedType) {
type.actualTypeArguments.get(0)
} else {
//Should not happen
throw IllegalArgumentException()
}
return OptionalAdapter(moshi.adapter<Any>(valueType).nullSafe())
} else {
null
}
}
}
}
sealed class Optional<out T> {
val provided get() = this !is NotPresent
abstract val value: T
object NotPresent : Optional<Nothing>() {
// have the IDE raise an error if the user knows a type is missing but still tries to access a value
#Deprecated(
"Cannot access a missing value",
level = DeprecationLevel.ERROR,
replaceWith = ReplaceWith("TODO(\"value is missing\")")
)
override val value: Nothing
get() = error("cannot access provided field")
}
data class Present<out T>(override val value: T) : Optional<T>()
}
I would like to serialize as {"value":123,"someNullable":null,"somePresent":"aaaa"} instead of {"value":123,"someNullable":null,"someNotPresent":null,"somePresent":"aaaa"}, which is what is doing now.
Basically, I want to skip the serialization in case the type is Optional.NotPresent. Any suggestion?
The solution I ended up with:
override fun toJson(writer: JsonWriter, value: Optional<T>?) {
when (value) {
is Optional.NotPresent -> {
val wasSerializeNulls = writer.serializeNulls
writer.serializeNulls = false
try {
writer.nullValue()
} finally {
writer.serializeNulls = wasSerializeNulls
}
}
is Optional.Present -> valueAdapter.serializeNulls().toJson(writer, value.value)
}
}
I have a lot of code like this, it is all the same except for the type PositionJson, it could be AnotherJson or FooJson or BarJson
Is there some way I can exctract all this code into one function that I can somehow pass into it the type? So that I don't have several of these big blocks of almost identical code littering my class?
I'm not sure if this is possible or not, just thought I'd ask because it would be nice to do...
/**
* #return the _open_ [PositionJson]s
*/
val positions: Array<PositionJson>?
#Throws(AccountsAPIException::class)
get() {
val service = constructServiceURL(POSITIONS, null, true)
try {
val messageJson = mapper.readValue<MessageJson<Array<PositionJson>>>(
callURL(service),
object: TypeReference<MessageJson<Array<PositionJson>>>() {
})
val error = messageJson.error
if (error != null) throw AccountsAPIException(error.errorCode, error.description)
return messageJson.data
} catch (e: Exception) {
throw AccountsAPIException(e)
}
}
You can do what you want with generics. However, to use generics we first need to extract that giant block of code into a method:
val positions: Array<PositionJson>? get() = getPositions()
fun getPositions(): Array<PositionJson>? {
...
}
We haven't solved the problem, but now we're in a position to be able to solve it by making getPositions generic (note that I also rename the function):
val positions: Array<PositionJson> get() = getArrayOf<PositionJson>()
// thanks to type inference I can omit the type on getArrayOf if desired:
val positions: Array<PositionJson> get() = getArrayOf()
fun <T> getArrayOf(): Array<T>? {
val service = constructServiceURL(POSITIONS, null, true)
try {
val messageJson = mapper.readValue<MessageJson<Array<T>>>(
callURL(service),
object: TypeReference<MessageJson<Array<T>>>() {
})
val error = messageJson.error
if (error != null) throw AccountsAPIException(error.errorCode, error.description)
return messageJson.data
} catch (e: Exception) {
throw AccountsAPIException(e)
}
}
Perfect! Except this won't compile thanks to type erasure. But we can fix this too by making the function inline and making the type parameter reified:
inline fun <reified T: Any> getArrayOf(): Array<T>? {
...
}
And that should do it. Now you can reuse this function as needed:
val positions: Array<PositionJson>? get() = getArrayOf()
val persons: Array<PersonJson>? get() = getArrayOf()
val bananas: Array<BananaJson>? get() = getArrayOf()
inline fun <reified T: Any> getArrayOf(): Array<T>? {
val service = constructServiceURL(POSITIONS, null, true)
try {
val messageJson = mapper.readValue<MessageJson<Array<T>>>(
callURL(service),
object: TypeReference<MessageJson<Array<T>>>() {
})
val error = messageJson.error
if (error != null) throw AccountsAPIException(error.errorCode, error.description)
return messageJson.data
} catch (e: Exception) {
throw AccountsAPIException(e)
}
}
One last thing: note that in all my examples I used property getters (get() = ...) as in your original code. However, I strongly suspect that you do NOT want to use a getter. Getters will be called every time someone accesses your property, which in this case means that every time someone reads the positions property you'll be calling constructServiceURL and making the service call, etc. If you want that code to only happen once then you should just call getArrayOf() once and assign the result to your property:
val positions: Array<PositionJson>? = getArrayOf()
// this syntax would also work:
val positions = getArrayOf<PositionJson>()
Given a struct S defined in this way
struct S {
let a : String
let b : Int
let c : Bool
}
and a function sConstructorFun
func sConstructorFun(#a:String, #b:Int, #c:Bool) -> S {
return S(a:a, b:b, c:c)
}
I can use both sConstructorFun(a:"", b:1, c:false) and S(a:"", b:1, c:false) to get the following S value (as the REPL outputs it)
S = {
a = ""
b = 1
c = false
}
So S and sConstructorFun have the very same interface and unsurprisingly return the same result.
However, a sFactory function defined as follows
func sFactory(f:(String, Int, Bool) -> S) -> S {
return f("foo", 42, false)
}
can only be used with the sConstructorFun but not with S directly:
REPL> sFactory(sConstructorFun)
$R2: S = {
a = "foo"
b = 42
c = false
}
and
REPL> sFactory(S)
repl.swift:18:1: error: cannot invoke 'sFactory' with no arguments
sFactory(S)
^
repl.swift:18:9: note: expected an argument list of type '((String, Int, Bool) -> S)'
sFactory(S)
^
Is there any way of using the default constructor of a struct (S in this example) as a function (without defining a new function/closure to do so)?
You just need to put the default constructor inside of a closure and pass that to the sFactory function. Try this:
let f = { S(a: $0, b: $1, c: $2) }
func sFactory(f:(String, Int, Bool) -> S) -> S {
return f("foo", 42, false)
}
let s = sFactory(f)
println("s = (a: \(s.a), b: \(s.b), c: \(s.c))") // S = (a: foo, b: 42, c: false)
Not very clear to me your intent, but maybe you are looking for constructors like these:
extension S {
init (f: (a: String, b: Int, c: Bool) -> S) {
self = f(a: "foo", b: 1, c: true)
}
init(f: () -> S) {
self = f()
}
}
Is there any difference in swift between function declaration:
func function(a: String) {
print(a);
}
function("test");
and closure assignment:
let closure = {
(a: String) in
print(a);
}
closure("test");
Is there any difference between those?
Named or anonymous
func function(a: String) {
print("\(a), name: \(__FUNCTION__)");
}
let closure = { (a: String) in
print("\(a), name: \(__FUNCTION__)");
}
Capture List
supported in closures only:
let obj = FooClass()
let closure = { [weak obj] in ... }
Curried function
supported in functions only:
func curriedFunc(x:Int)(y:Int) { ... }
let curried = curriedFunc(1)
curried(y:2)
Similar, but not exact the same with using closure:
let closure = { (x:Int) in { (y:Int) in ... }}
Generics
supported in functions only:
func function<T>(x:T) { ... }
Referenceability from its own initial declaration
supported in global functions only:
func recursive(var x:Int) -> Int {
...
return condition ? x : recursive(x)
}
You can do this using closure also:
var recursive:((Int) -> Int)!
recursive = { (var x) in
...
return condition ? x : recursive(x)
}
But this is not recommended because this causes strong reference cycles.
Overload
supported in functions only:
func function(a: String) { print("String: \(a)") }
func function(a: Float) { print("Float: \(a)") }
n.b. You can reference them as a closure like this:
let f:(Float) -> Void = function
Another difference: recursivity inside another function
Nested functions cannot be recursive:
func foo() {
func bar() { bar() } // does not compile
}
but closures inside other functions can be recursive:
func foo() {
var bar: (() -> ())!
bar = { bar() }
}