简体   繁体   中英

Scala: How to write a generic check function that evaluates any function that returns boolean?

I'm struggling a bit with this: I need a function that takes any function of type fun(Any*) : Boolean as parameter, evaluates the function and returns true or false, depending on the success of the function evaluation.

Essentially, what I need is a function type that allows any number and any type of parameter but the function must return Boolean.

Which would allow me to write functions like:

def checkLenght(str : String, length : Int) : Boolean ={
if (str.lenght == length)}

or

def ceckAB(a : Int, b : Int) : Boolean = {
if(a < b && a >= 23 && b < 42) }

so that, for example

eval(checkLenght(abc, 3)) //returns true

eval(ceckAB(4,1)) // returns false 

I thought, a function type of:

  type CheckFunction = (Any*) => Boolean

may does the trick but I struggle with writing the generic eval function.

Any advise?

Thank you

Solution:

The function requires 1) Another function of return type Boolean: "(func : => Boolean)" 2) Return type Boolean ": Boolean" 3) Returns the value of the passed function-parameter: " = func"

Altogether the function is:

   def eval(func : => Boolean) : Boolean = func

It amazes me over again how simple simple things are in Scala.

As pointed out by the comments, this is a rather unusual function with no obvious sense. Just a word about the underlying reasons.

Motivation:

There were a lot of question about the underlying motivation, so here a short summary why such a function is needed.

Essentially, there are two reasons.

First one is about moving the failure handling away from the function itself into a handler function. This preserves the purity of the check function and even allows re-usage of generic checks.

Second, it's all about "pluggable failure handling". This means, the eval function only tells if a failure happened (or not). In case of a failure, a handler is called through an interface. The implementation of the handler can be swapped using profiles as required.

Why?

Swapping profiles means, I code my checks and functions as usual but by switching the profile, I switch the handler which means I can chose between full-stop, console print out, email alert, SNMP notification, push message... you name it. To do so, I need to decouple the check function from its evaluation and from its handling. That's the motivation for such a rather strange looking eval function.

And for the sake of completeness, I've already implemented all that stuff but was I facing the limitation of only handling trivial checks ie check(Boolean*) which is neat but often I would prefer to write a function to do more sophisticated checks.


Solved

The function is defined by returning the value of the passed function:

def eval(func : => Boolean) : Boolean = {func}

I can't say that I really understand your motivations for wanting to do what you want to do, but I guess that's beside the point. Maybe the eval function will check something before invoking the supplied function and not invoke that other function (like a fast fail) given some certain condition. Maybe you do some post checking after invoking the function and change the result based on something else. Either way, I suppose you could accomplish something similar to what you want with code looking like this:

def main(args: Array[String]) {
  val str = "hello world"
  println(eval(checkLength(str, 3)))
  println(eval(intsEqual(1,1)))
}

def eval(func: => Boolean):Boolean = {
  //Do whetever you want before invoking func, maybe
  //not even invoke it if some other condition is present
  val fres = func

  //Maybe change something here before returning based on post conditions
  fres
}

def checkLength(s:String, len:Int) = s.length() == len
def intsEqual(a:Int, b:Int) = a == b

If you really want the eval function to be able to support any function that takes any types of args and returns a Boolean , then using a by-name function like this, and then leveraging closure inside the by-name function to pass any params along to whatever actual function you want to invoke. A better way to demonstrate this is as follows:

def checkMyString(str:String, len:Int) = {
  eval(str.length == len)
}

It's probably hard to see that the check str.length == len is not invoked unless eval decides to invoke it until you expand it to it's true form:

def checkMyString(str:String, len:Int) = {
  def check = {
   str.length == len 
  }
  eval(check)
}

Here, the nested function check has access to str and len due to closure, and this will allow you to get around the requirement that eval must be able to invoke a function with any params that returns a Boolean .

This is just one way to solve your problem, and it might not even be suitable given your needs, but I just wanted to throw it out there.

If your input functions only have 2 arguments, like your two examples, you can write a semi generic function take takes all functions with two arguments of any type:

def eval[A,B](func: (A,B) => Boolean, arg1: A, arg2: B) = {
    func(arg1, arg2)
}

def checkLength(str: String, length: Int) : Boolean = {
    str.length == length
}

eval(checkLength, "ham", 4)
res0: Boolean = false

But if you want to support functions with more arguments, you would have to write one eval function for three arguments, four arguments, etc

Maybe there is a better way that can handle all cases?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM