Simplifying code: suggestions?


#1

Hi,

I am experimenting with a very simple “piping” of functions. The idea is
to facilitate the combination of functions that take in a parameter P,
an input I and output some configuration related data A and the processing
result O. The input will most likely be a container of data (see end of example).

The idea is that I can sequence these operations and have the underlying
"system" automatically collect the As and pass the in/out to the next
function until processing has terminated.

The system is a function that is generated automatically (partial evaluation?).
This is done in partialEvalB. The example below works ok, however it
seems a little complicated.

Can anyone suggest simplifications or ways to make it more manageable?


  def func1a(p:String, v:Boolean): (String, Int) = (p, if (v) 1 else 0)
  def func1b(p:Char, v:Int): (String, Double) = (p.toString, v.toDouble)
  def func1c(p:String, v:Double) : (String, String) = (p, s"p($v)")
  def func1d(p:Char, v:String) : (String, Double) = (p.toString, v.drop(2).dropRight(1).toDouble)

  trait Monoid[T] {
    def unit: T
    def op(a:T, b:T) : T
  }
  object Monoids {
    implicit object stringMonoid extends Monoid[String] {
      def op(x: String, y: String): String = x.concat(y)
      def unit: String = ""
    }
    implicit object intMonoid extends Monoid[Int] {
      def op(x: Int, y: Int): Int = x + y
      def unit: Int = 0
    }
  }

  sealed trait OpB[A,P,I,IO,O]
  case class LastOpBF[A,P,I,IO,O](p:P, f:(P,I) => (A,O)) extends OpB[A,P,I,IO,O]
  case class OpBF[A,P1,I,O1,P2,O2](p:P1, f:(P1,I) => (A,O1), next: OpB[A,P2,O1,_,O2]) extends OpB[A,P1,I,O1,O2]

  val exb0Op = LastOpBF('b', func1b)
  val exb1Op = OpBF("func1a", func1a, exb0Op)
  val exb2Op = OpBF("func1a", func1a, OpBF('b', func1b, OpBF("func1c", func1c, LastOpBF('d', func1d))))

  def partialEvalB[A,P1,I,O1,P2,O2](o:OpB[A,P1,I,O1,O2])(implicit m: Monoid[A]): Iterable[(A,I)] => Iterable[(A, O2)] = o match {
    case LastOpBF(p,f) => (is1:Iterable[(A,I)]) => {
      val tmp: Iterable[(A, O2)] = is1.map{ i =>
        val acc = i._1
        val input = i._2
        val (n_acc,output) = f(p,input)
        (m.op(n_acc,acc), output)
      }
      tmp
    }
    case OpBF(p,f,next) =>
      val f2: Iterable[(A,O1)] => Iterable[(A,O2)] = partialEvalB(next)(m)
      (is1:Iterable[(A,I)]) => {
        val tmp: Iterable[(A, O1)] = is1.map{ i =>
          val acc = i._1
          val input = i._2
          val (n_acc,output) = f(p,input)
          (m.op(n_acc,acc), output)
        }
        val tmp2: Iterable[(A, O2)] = f2(tmp)
        tmp2
      }
  }

  import Monoids._

  val zero = List("","","","")
  val exb1T: Iterable[(String, Int)] => Iterable[(String, Double)] = partialEvalB(exb0Op)
  println(exb1T(zero.zip(List(1, 0, 0, 1))))

  val exb2T: Iterable[(String, Boolean)] => Iterable[(String, Double)] = partialEvalB(exb1Op)
  println(exb2T(zero.zip(List(true, false, false, true))))

  val exb3T: Iterable[(String, Boolean)] => Iterable[(String, Double)] = partialEvalB(exb2Op)
  println(exb3T(zero.zip(List(true, false, false, true))))