Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you curry a function with varargs in scala?

I was thinking about how to go about currying a method with varargs, and I realized that I don't even have an intuition for how one would go about doing it. Ideally, it would be something that would let you start using it whenever you liked, and then end it with an iterable.

def concat(strs: String*) = strs.mkString

val curriedConcat = concat.curry

curriedConcat("OK")("hello", "world")("welcome")(Seq(): _*)

Is there support for this in scala? I couldn't figure out how to do anything more than bind it to a function of length N and then curry that.

like image 726
nnythm Avatar asked Dec 08 '12 14:12

nnythm


1 Answers

Using Scala 2.10 and shapeless:

import shapeless.Nat._
import shapeless.{Nat, Succ}

trait Curry[T, Res, N <: Nat] {
  type Out
  def apply(as: Seq[T], f : Seq[T] => Res) : Out
}

object Curry {
  implicit def curry[Out0, T, Res, N <: Nat](implicit curry : CurryAux[Out0, T, Res, N]) = new Curry[T, Res, N] {
    type Out = Out0
    def apply(as: Seq[T], f : Seq[T] => Res) = curry(as, f)
  }
}

trait CurryAux[Out, T, Res, N <: Nat] {
  def apply(as: Seq[T], f : Seq[T] => Res) : Out
}

object CurryAux {
  implicit def curry0[Res, T] = new CurryAux[Res, T, Res, _0] {
    def apply(as: Seq[T], f : Seq[T] => Res) : Res = f(as)
  }

  implicit def curryN[Out, T, Res, N <: Nat](implicit c : CurryAux[Out, T, Res, N]) =
    new CurryAux[T => Out, T, Res, Succ[N]] {
      def apply(as: Seq[T], f : Seq[T] => Res) : (T => Out) = (a: T) => c(as :+ a, f)
    }
}

implicit class CurryHelper[T, Res](f : Seq[T] => Res) {
  def curry[N <: Nat](implicit c : Curry[T, Res, N]): c.Out = c(IndexedSeq[T](), f)
}

Usage:

scala> def concat(strs: String*) = strs.mkString
concat: (strs: String*)String

scala> val test = ( concat _ ).curry[_3]
test: String => (String => (String => String)) = <function1>

scala> test("1")("2")("3")
res0: String = 123

Without shapeless:

class CurryHelper[T, Res](f: Seq[T] => Res, as: Seq[T]) {
  def myCurry() = this
  def apply(ts: T*) = new CurryHelper(f, as ++ ts)
  def apply(ts: Seq[T]) = f(as ++ ts)
}

implicit def toCurryHelper[T, Res](f: Seq[T] => Res) = new CurryHelper(f, IndexedSeq[T]())

scala> def concat(strs: String*) = strs.mkString
concat: (strs: String*)String

scala> val test = ( concat _ ).myCurry
test: CurryHelper[String,String] = CurryHelper@4f48ed35

scala> test("1")("2")("3", "4")(Nil)
res0: String = 1234
like image 85
senia Avatar answered Sep 29 '22 05:09

senia