Passing an additional argument to a polymorphic function?

I have a polymorphic function that can turn lists into groups:

import shapeless.PolyDefns.~> import shapeless._ val lists = List(1,2) :: List("A", "B") :: List(1.1, 2.2) :: HNil object sss extends (List ~> Set) { def apply[T](l:List[T]):Set[T] = { l.toSet } } lists.map(sss) // I want: Set(1,2) :: Set("A", "B") :: Set(1.1, 2.2) :: HNil 

But what if I want to change the behavior of this function - now I want to add an additional argument that will indicate which element in the input list should be placed in the set. The syntax is wrong here - can you show me the correct way to do this?

 object sss extends (List ~> Set) { // Compiler says no! def apply[T](i:Int)(l:List[T]):Set[T] = { l.slice(i,i+1).toSet } } 

I think this fails because the extra argument makes it no longer suitable for the List ~> Set signature, so how can I overcome this?

+3
scala shapeless
source share
2 answers

There are several workarounds for parameterizing a Poly , one of which is mentioned in the other answer, although the exact implementation will not work there. Instead, you need to do this:

 import shapeless._, shapeless.poly.~> val lists = List(1, 2) :: List("A", "B") :: List(1.1, 2.2) :: HNil class sss(i: Int) extends (List ~> Set) { def apply[T](l: List[T]): Set[T] = l.slice(i, i+1).toSet } object sss1 extends sss(1) lists.map(sss1) 

... where the last line of compilation requires the fact that sss1 is defined as an object (and not a val ).

This approach compiles, but cannot be used in many contexts. you cannot define your sss1 (or any other) object in a method where the type hlist is shared.

Here's a slightly dirtier, but more flexible workaround I used before:

 import shapeless._ val lists = List(1, 2) :: List("A", "B") :: List(1.1, 2.2) :: HNil object sss extends Poly2 { implicit def withI[T]: Case.Aux[List[T], Int, Set[T]] = at((l, i) => l.slice(i, i + 1).toSet) } lists.zipWith(lists.mapConst(1))(sss) // Set(2) :: Set(B) :: Set(2.2) :: HNil 

Now you can write a method that L <: HList generic L <: HList and slice i parameter - you just need a few implicit arguments to support the mapConst and zipWith .

None of these approaches are very elegant, and I personally try to avoid Poly most of the time, defining a class of a custom type will be almost clean and in many cases necessary.

+3
source share

As you have already indicated, you cannot change the signature of a polymorphic function. But it can dynamically create your function:

 class sss(i: Int) extends (List ~> Set) { def apply[T](l:List[T]): Set[T] = { l.slice(i, i+1).toSet } } val sss1 = new sss(1) lists.map(sss1) // Set(2) :: Set(B) :: Set(2.2) :: HNil 
+1
source share

All Articles