Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing a shapeless extensible record to a function (never ending story?

I continue to investigate extensible records as in Passing a Shapeless Extensible Record to a Function (continued): the provided solution works with functions that all takes a parameter that includes at least foo1, foo2, and foo3; that is one can write:

fun1(("foo1" ->> "hello") :: ("foo2" ->> 1) :: ("foo3" ->> 1.2) :: HNil)
fun1(("foo1" ->> "hello") :: ("foo2" ->> 1) :: ("foo3" ->> 1.2) :: ("foo4" ->> true) :: HNil)

and so on

And if we can add a second function fun2:

def fun2[L <: HList : HasMyFields](xs: L) = {
  val selectors = implicitly[HasMyFields[L]]
  import selectors._
  xs("foo1").length + xs("foo2") + xs("foo3")
}

So far, so good.

Now, let's assume we have a set of fields foo1, foo2,... And a set of functions fun1, fun2, which take as parameter a record composed with any subset of {foo1, foo2,...}. For example, fun1 could take as parameter a record that contains foo1 and foo3 but not necessarily foo2, fun2 would expects a record with at least foo4 and so on.

Is there a way to avoid to declare as many class like HasMyFields as they are possible combinations (if we have n fields, there are 2**n combinations!)?

like image 482
bhericher Avatar asked Jun 07 '15 12:06

bhericher


1 Answers

This is a lot easier without an additional type class with the new-ish Witness syntax:

import shapeless._, ops.record.Selector, record._, syntax.singleton._

// Uses "foo1" and "foo2" fields; note that we get the appropriate static types.
def fun1[L <: HList](l: L)(implicit
   foo1: Selector.Aux[L, Witness.`"foo1"`.T, String],
   foo2: Selector.Aux[L, Witness.`"foo2"`.T, Int]
): (String, Double) = (foo1(l), foo2(l))

And then if we have this record:

val rec = ("foo1" ->> "hello") :: ("foo2" ->> 1) :: ("foo3" ->> 1.2) :: HNil

We get this:

scala> fun1(rec)
res0: (String, Double) = (hello,1.0)

The new syntax allows us to avoid creating witness values separately, so it's pretty easy to just require the Selector instances you need.

like image 106
Travis Brown Avatar answered Sep 18 '22 12:09

Travis Brown