gire
gire

Reputation: 1105

Scala shapeless polymorphic value function for numeric types

I need to write a function that will receive a function that will transform a List[T] into a T. For example, summing the elements of the list.

My first try was to use shapeless polymorphic functions as:

object sumPoly extends (List ~> Option) = {
  def apply[T: Numeric](list: List[T]): Option = Some(list.sum)
}

but I get an error because shapeless expect the function to be def apply[T](...)

The function that receives the function above would look like:

def test(list: List[Any], f: (List ~> Option)) = {
  val intTypeCase = TypeCase[List[Int]]
  val doubleTypeCase = TypeCase[List[Double]]
  val longTypeCase = TypeCase[List[Long]]
  list match {
    intTypeCase(l) => f(l)
    doubleTypeCase(l) => f(l)
    longTypeCase(l) => f(l)
    // more type matchings here
  }
  ...
}

Is there a way to achieve what I want to do?

EDIT

After some searches I found that it is possible to do the following:

object sumPoly extends Poly1 {
  implicit def caseListInt = at[List[Int]](x => x.sum)
  implicit def caseListDouble = at[List[Double]](x => x.sum)
  // more type matchings here
}

and calling sumPoly(List(1, 1, 1)) correctly returns 3. But if I define the test function as:

def test(list: List[Any], f: Poly1) = {
  val intTypeCase = TypeCase[List[Int]]
  val doubleTypeCase = TypeCase[List[Double]]
  val longTypeCase = TypeCase[List[Long]]
  list match {
    intTypeCase(l) => f(l)
    doubleTypeCase(l) => f(l)
    longTypeCase(l) => f(l)
    // more type matchings here
  }
  ...
}

and pass the sumPoly function, I get the errors like this for each type I defined in the test function: could not find implicit value for parameter cse: shapeless.poly.Case[f.type,shapeless.::[List[Int],shapeless.HNil]]

Any ideas?

Upvotes: 1

Views: 324

Answers (1)

gire
gire

Reputation: 1105

By looking at shapeless' code I found I can do the following:

object sumPoly extends Poly1 {
  implicit def caseList[T: Numeric] = at[List[T]] { x => x.sum }
}

and then the function which will receive the Poly1 must have the following definition:

def test(list: List[Any], f: Poly1) 
        (implicit li : f.Case[List[Int]], 
                  ld : f.Case[List[Double]], 
                  ll : f.Case[List[Long]])= {

  val intTypeCase = TypeCase[List[Int]]
  val doubleTypeCase = TypeCase[List[Double]]
  val longTypeCase = TypeCase[List[Long]]
  list match {
    intTypeCase(l) => f(l)
    doubleTypeCase(l) => f(l)
    longTypeCase(l) => f(l)
    // more type matchings here
  }
  ...
}

this way, I can create different Poly1 functions like sum, prod, min, max that convert List[T] into a T where T is numeric.

The design might seem contrived, but I am interop-ing with Java. More specifically, with Hadoop libraries written in Java.

Upvotes: 0

Related Questions