1
\$\begingroup\$

I am trying to create a collection of Unit functions that have any arity and type, and will execute them in order.

This is the working implementation that I have:

/** A Subroutine simply represents a unit function that takes any number of parameters. */
type Subroutine = (Any) => Unit

/** A Routine represents a collection of functions which may consume a collection of parameters and will always
  * complete an action.
  */
type Routine = List[(Subroutine, Seq[_])]

/** This function will consume a list of functions executing them in order.
  *
  * @param routine is the list of functions and their parameters to be consumed.
  */
def executeRoutine(routine: Routine): Unit = {
  routine.headOption match {
    case Some((f, x)) =>
      f(x)
      executeRoutine(routine.tail)
    case None => ()
  }
}

and when I declare a function or "subroutine", I have to declare it like this in order to extract the parameters:

def myFunction: Subroutine = {
  case (firstParam: Int) :: (secondParam: String) :: _ => ...
  case _ => throw new InvalidParameterException()
}

def myFunction2: Subroutine = {
  case (firstParam: Int) :: _ => ...
  case _ => throw new InvalidParameterException()
}

val routine: Routine = List(
  (myFunction, Seq(firstParam, secondParam)),
  (myFunction2, Seq(firstParam))
)

I realize that this is not close to idiomatic Scala code, and I've been working on implementing a different approach which will partially apply functions one parameter at a time, and then execute the function after the parameters are fully applied. I do not yet have a working version of this approach, but this is as far as I have gotten:

/** A Subroutine simply represents a unit function that takes any number of parameters. */
sealed abstract class Subroutine extends (Any => Unit)

case class Curried(partiallyApplied: Function[_, Function[_, _]], unapplied: Function[_, _]) extends Subroutine

case class Uncurried(function: Function[_, Unit]) extends Subroutine

/** A Routine represents a collection of functions which may consume a collection of parameters and will always
  * complete an action.
  * Using HList for the parameters, from Shapeless
  */
type Routine = List[(Subroutine, HList)]

/** This function will consume a list of functions executing them in order.
  *
  * @param routine is the list of functions and their parameters to be consumed.
  */
def executeRoutine(routine: Routine): Unit = {
  routine.headOption match {
    case Some((f, x)) =>
      executeSubroutine(f, x)
      executeRoutine(routine.tail)
    case None => ()
  }
}

private def executeSubroutine(subroutine: Subroutine, params: HList): Any = {
  subroutine match {
    case Curried(partiallyApplied, unapplied) =>
      partiallyApplied(params.head)(executeSubroutine(unapplied, params.tail))
    case Uncurried(function) => function(params.head)
  }
}

If anyone can help me out that would be greatly appreciated!

\$\endgroup\$
-1
\$\begingroup\$

Unless I misunderstand your question, I think this is what you want:

type Subroutine[Param] = Param => Any

type Routine = Seq[(Subroutine[_], Seq[Any])]

def execRoutine(routine: Routine): Unit = routine foreach { case (sub, params) => execSubroutine(sub, params) }

def execSubroutine[SubParam] (subroutine: Subroutine[SubParam], params: Seq[Any]): Unit = {
    require(params.nonEmpty)

    subroutine(params.head.asInstanceOf[SubParam]) match {
        case sub: Subroutine[_] => execSubroutine(sub, params.tail)
        case () => require(params.tail.isEmpty) // Require correct number of params.
        case _ => throw new IllegalStateException()
    }
}

val add2 = (a: Int) => (b: Int) => println(a + b)
val add3 = (a: Int) => (b: Int) => (c: Int) => println(a + b + c)

execRoutine(List(
    (add2, List(3, 4)),
    (add3, List(3, 4, 5))
))

Try it online!

This program prints 7 and 12. Unfortunately, if the parameters are of the wrong type, a runtime exception occurs instead of a compile time one. Attempting to apply the less or more than the required parameters also results in a runtime exception.

\$\endgroup\$
  • 2
    \$\begingroup\$ So does the OP's cod enot work, or yours? \$\endgroup\$ – Dannnno Apr 16 '18 at 1:14
  • \$\begingroup\$ @Dannnno The OP's code does not work, as they stated, but my code does compile and run correctly. Does that address your question? \$\endgroup\$ – Llew Vallis Apr 16 '18 at 2:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.