简体   繁体   中英

How to assign a function as a return type from function in Scala

I'm very new to Scala and I have run in to a problem.

I am trying to write a class which will hold a queue of functions. I want to be able to add functions to the queue, and when all functions have been added, run these. Essentially build an expression like: "function1(function2(function3()))" to be returned and then evaluated. This is the code I've got so far:

  class Pipeline() {

    // Queue of functions to run
    private var queue: Queue[ _ => _] = new LinkedList();

    // Add functions to the queue 
    def addFunction(func:_ => _ ): Unit ={
      queue.add(func)
    }

    // Run all the functions in the queue
    def run(): Unit = {
      val function = runHelper(queue.poll(), queue)
      function
    }

    def runHelper(func: _ => _, queue: Queue[_ => _]): _  = {
      // Recursion base case
      if(queue.isEmpty)
        return func
      // Keep building the function recursively
      else
        func(runHelper(queue.poll(), queue))        
    }    
  }

I'm sure that here is more than one error in there. But right now, what I'm stuck at is the return type of the runHelper function. As you can see I'm trying to use the _ wildcard, but this gives a compilation error. How would I defined that the function will return a function? And am I going about this in a good way - if not please point me in the direction of a more suitable solution to the problem.

edit1: Clarification The input and return type of the functions are not known beforehand, and they their sequence needs to be possible to assign dynamically.

Edit2: More problems I've been trying to get the code Edmondo1984 suggested to work the way I want it to, but I can't seem to get it.

What I need to do would be something like this:

val func1: String => File = function1
val func2: File => File = function2

var queue = func1

if(runFunc2)
    queue = queue :: func2

queue("exampleString")

What I specifically need to know is how to be able to do the "queue = queue :: func2". Since :: returns a FunctionQueue, I would have imagined that I would be able to assign it to the queue variable. But then again, I guess that since the first initialization of the variable leads to it having the "String => File" demand. I feel like I'm in a bit over my head here, and any help would be much appreciated.

The form I prefer for functions is: (parameter list) => returnType .

Here is what I think your code should look like. My IDE likes it, but that's no guarantee:

class Pipeline[T]() {

 // Queue of functions to run
 private var queue: util.Queue[ (T) => T] = new util.LinkedList()

 // Add functions to the queue
 def addFunction(func: (T)=> T ) {
   queue.add(func)
 }

 // Run all the functions in the queue
 def run() {
   val function = runHelper(queue.poll(), queue)
   function
 }

 def runHelper(func: (T) => T, queue: util.Queue[(T)=> T ]): (T)=>T = {
   // Recursion base case
   if(queue.isEmpty)
     func
   // Keep building the function recursively
   else
     func compose runHelper(queue.poll(), queue)
  }
}

You could give function composition a try. For example like this.

val f = (x:Int) => 2 * x
val g = f.compose(f)

if the in and output types vary you have to take care that in and output match...

What you are trying to do is not possible because _ is a placeholder for an existential type: A type which exists but is not currently relevant: you can use that for example when you want to print a list like the following:

scala>  def aMethod(a:List[_]) = println(a.size)
aMethod: (a: List[_])Unit

scala>  val b = List(2,3,4)
b: List[Int] = List(2, 3, 4)

scala>  aMethod(b)
3

This works because in fact you do not make access to the elements of the list, so you can imagine you do not need their class.

Because Scala is a strongly typed language, ie the compiler checks that the signatures are respected in your code. A Queue[ _ => _] is a queue of function from an unknown type to an unknown type and it is not useful, because if you try to pop a function and apply it to an input parameter, you won't be able to verify if that input parameter matches the signature.

What you are trying to do is not trivial, and you need to define your queue in a recursive way. You might want to read Shapeless HLIST implementation, but the idea is the following :

trait FunctionQueue[A,B]{

  def ::[C](newFunction: C => A): FunctionQueue[C,B]

  def apply(a:A):B
}

class FunctionQueueImpl[A,B,C](val f:A=>B, val queue:FunctionQueue[B,C]) extends FunctionQueue[A,C]{

  def apply(a:A) = queue.apply(f(a))

  def ::[D](newFunction: (D) => A):FunctionQueue[D,C] = new FunctionQueueImpl[D,A,C](newFunction,this)
}

object FunctionQueue {
  def EmptyQueue[T]:FunctionQueue[T,T] = new FunctionQueue[T,T] {
    def ::[C](newFunction: (C) => T):FunctionQueue[C,T] = new FunctionQueueImpl[C,T,T](newFunction,this)

    def apply(a: T):T = a
  }

  implicit def functionToQueue[A,B](function:A => B):FunctionQueue[A,B] = new FunctionQueueImpl(function, EmptyQueue[B])
}

Now you can try it in the repl:

scala>  import FunctionQueue._
import FunctionQueue._

scala>  val a: Int => Int = _ * 10
a: Int => Int = <function1>


scala>  val b: Double => Int = _.toInt 
b: Double => Int = <function1>

scala>  val c : String => Double = _.toDouble
c: String => Double = <function1>

scala>  val queue = c::b::a
queue: FunctionQueue[String,Int] = FunctionQueueImpl@cccfa5e

scala>  queue("1.25")
res1: Int = 10

scala>  queue("3.25")
res2: Int = 30

I would suggest reading Miles Sabins work to understand more.

Concerning the implementation, you can use a simple List of functions T => T (which are called endofunctions).

Now, in scalaz you have an instance of a Monoid (built from Semigroup and Zero ) for such functions. This means, you can write the following code:

scala> import scalaz._, Scalaz._
import scalaz._
import Scalaz._

scala> val f = ((_:Int) + 6).endo
f: scalaz.Endo[Int] = <function1>

scala> val g = ((_:Int) * 4).endo
g: scalaz.Endo[Int] = <function1>

scala> val h = (-(_:Int)).endo
h: scalaz.Endo[Int] = <function1>

scala> f :: g :: h :: nil
res3: List[scalaz.Endo[Int]] = List(<function1>, <function1>, <function1>)

scala> .asMA.sum
res4: scalaz.Endo[Int] = <function1>

scala> f(g(h(1)))
res5: Int = 2

scala> res4(1)
res6: Int = 2

You can specify the type of a function like so:

Function[T, T]

or

T => T

Assuming both input and output of your function are equal, which they really should be. You'd have to specify or replace T of course.

If you really have variadic input–output, the whole scheme will become much more complicated.

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