I have a whole set of functions that look like this:
package rules;
import "project/some"
func frobDiscountLimit(frob some.Frob, maxDiscount float64) (RuleStatus, string)
func frobDiscountPercentageLimit(frob some.Frob, maxDiscountPercentage float64) (RuleStatus, string)
func frobUsageLimit(frob some.Frob, interval valueInterval) (RuleStatus, string)
func frobPermanentExpiryLimit(frob some.Frob) (RuleStatus, string)
func frobVoucherValidity(frob some.Frob, maxValidityDays uint) (RuleStatus, string)
Usage example:
package rules
import "project/some"
func doChecks(frob some.Frob) {
status, message := frobDiscountLimit(frob, 100)
if status != PASSED {
....
}
... evaluate all remaining rules here ...
}
I wonder is there any advantage writing it like:
func (f someFrob) discountLimit(maxDiscount float64) (RuleStatus, string)
...
It seems shorter to write, but I'm unsure which is the correct style. Because I don't think of these validation methods as "part of the object", they are not part of the behaviour of the some.Frob object (unlike, say, a getter or a setter for property of some.Frob).
You could do something like this to avoid having to explicitly pass frob over and over, while still being able to avoid making those functions methods on frob.
type frobChecker struct {
frob some.Frob
}
func (fc frobChecker) discountLimit(maxDiscount float64) (RuleStatus, string)
func (fc frobChecker) discountPercentageLimit(maxDiscountPercentage float64) (RuleStatus, string)
func (fc frobChecker) usageLimit(interval valueInterval) (RuleStatus, string)
func (fc frobChecker) permanentExpiryLimit() (RuleStatus, string)
func (fc frobChecker) voucherValidity(maxValidityDays uint) (RuleStatus, string)
func doChecks(frob some.Frob) {
fc := frobChcker{frob}
status, message := fc.discountLimit(100)
if status != PASSED {
....
}
... evaluate all remaining rules here ...
}
Related
I am parsing multiple CSV files and would like to provide my application with some generic parsers with logging capabilities. Is it possible to give some generic solution for it?
My try to do that is:
interface Converter<out T> {
fun convert(fieldName: String, value: String): T?
}
object DoubleMapper : Converter<Double> {
private val log = LoggerFactory.getLogger(this::class.java)
override fun convert(fieldName: String, value: String): Double {
log.info("Converting $fieldName: $value to Double")
return 123.3
}
}
object ConverterProvider {
private val log = LoggerFactory.getLogger(ConverterProvider::class.java)
inline fun <reified T : Any> getConverter(): (String, String) -> T {
return when (T::class) {
Double::class -> DoubleMapper::convert
Int::class -> IntMapper::convert
else -> {
throw java.lang.RuntimeException("We do not have mapper")
}
}
}
}
However, this does not compile, does kotlin provide such capabilities to have function return type depend on type parameter?
Your solution is almost correct one. The only problem is that the compiler is not smart enough to understand that you verified the type of T and you return the right type of the converter. You just need to cast the converter to T:
return when (T::class) {
...
} as (String, String) -> T
This cast is unchecked, meaning that the compiler can't guarantee at runtime that the cast is safe. However, as long as you return correct converter for the T, such cast should be safe and you can just suppress the warning:
#Suppress("UNCHECKED_CAST")
return when (T::class) {
...
} as (String, String) -> T
I want to apply Basic authentication in golang using echo framework. I have following error :
"# command-line-arguments
.\main.go:44:29: cannot use func literal (type
func(string, string, echo.Context) bool) as type middleware.BasicAuthValidator in argument to
middleware.BasicAuth"
my code:
package main
import (
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"net/http"
)
// main function
func main(){
e := echo.New()
g := e.Group("/home")
g.Use(middleware.BasicAuth(func (username, password string, c echo.Context) bool {
if(username=="iamsns" && password=="Iamsns355#"){
return true
} else {
return false
}
}))
g.POST("/login", getHome)
e.Start(":8008")
}
According to the docs middleware.BasicAuthValidator is defined as func(string, string, echo.Context) (bool, error) not func(string, string, echo.Context) bool. You need to change the signature to also return an error.
in the below code i am learning how ot use lampda function with object receiver and without. with function max1 when impl1 is passed as body block parameter the code works as expected.
However, with max2 and impl2 as code parameter, i could not use the reciever object style/notation...is it ever possible to apply the receiver object on a lmpda function as a prameter to a high order function?
the error eclipse generates is:
#unresolved reference error
please let me know if it ever possible to use the receiver object in side the body of a high order function
code:
//lampda
val impl1 : (String,String) -> Boolean = {
s1, s2 -> s1.length > s2.length
}
//lampda with a reciver object and scoping object
val impl2 : String.(String) -> Boolean = {
s -> this.length > s.length
}
print(max1("1234", "12", impl1))
print(max2("1234", "12", impl2))
}
fun max1 (s1: String, s2: String, c: (String, String) -> Boolean) : String {
return "inside methods.....${ c(s1, s2) }"
}
fun max2 (s1: String, s2: String, c: String.(String) -> Boolean) : String {
return "inside methods ${ s1.impl2.s2 } "// #unresolved reference error
return "inside methods ${ s1.c.s2 } "// #unresolved reference error
}
You have to invoke your function with the receiver:
fun max2(s1: String, s2: String, c: String.(String) -> Boolean): String {
return "inside methods ${s1.impl2(s2)} "//works
return "inside methods ${s1.c(s2)} "//also works
}
Here, Handler is a function type.
And doSomething is one of such a handler function.
addHandler register it and give it a name.
Question is are there simpler way to convert a function doSomething to a lambda?
typealias Handler = (cmd: String, obj: Any?) -> Any?
fun doSomething(cmd: String, obj: Any?): Any? {...}
fun addHandler(name: String, handler: Handler) {...}
fun foo() {
addHandler("doSomething", { cmd, obj -> doSomething(cmd, obj) })
// or in other syntax
addHandler("doSomething") { cmd, obj -> doSomething(cmd, obj) }
}
Here, the phrase
{ cmd, obj -> doSomething(cmd, obj) }
is just converting a function to a lambda which has the same parameter sequence.
C++ has very simple syntax &doSomething for it. How about in Kotlin?
Kotlin also supports method references, in your case, you can do this:
addHandler("doSomething", ::doSomething)
The following does not compile:
fun<T> doSomething(value: T, action: (value: T) -> String = Any::toString){
//do something
}
The error is:
Kotlin: Type mismatch: inferred type is KFunction1<Any, String> but (T) -> String was expected
Making it work is easy:
fun<T> doSomething(value: T, action: (t: T) -> String = {t -> t.toString()}) = action(value)
However, this leaves me wondering: what is the difference between lambdas and KFunctions? Why do we need both?
Also is there a simpler way to provide Any::toString as the default action?
The reason why the code does not compile has nothing to do with the difference between lambdas and KFunctions. It doesn't compile because the parameter needs to be a function of type (T) -> String, and Any::toString is a function of type (Any) -> String.
When you obtain any function (lambda or otherwise) reference with :: you are using reflection. KFunction is Kotlin's way to to wrap around reflected functions.
As to making Any::toString work - there is a way but you may not like it:
fun <T> doSomething(value: T, action: (t: T) -> String = Any::toString as (T) -> String) {
// ...
}
It would have compiled if do like this:
fun <T> doSomething(value: T, action: (value: Any) -> String = Any::toString) {
//do something
}
or
fun <T : Any> doSomething(value: T, action: (t: T) -> String = Any::toString) {
// ...
}