简体   繁体   中英

Context bound for varargs

Few days ago I started to learn Cats and I want to implement method appendOptional for Map[String, _: Show] .

I started with the following idea:

def appendOptional[T: Show](to: Map[String, String], values: (String, Option[T])*): Map[String, String] = 
    values.foldLeft(values) {
        case (collector, (key, Some(value))) => 
            collector + (key -> implicitly[Show[T]].show(value)) 
        case (collector, _) => collector
    }

And to use it like:

def createProps(initial: Map[String, String], name: Option[String], age: Option[Int])

val initial = Map("one" -> "one", "two" -> "two")
val props = appendOptional(initial, "name" -> name, "age" -> age)

I understand that this approach is quite naive and straightforward because implicitly[Show[T]].show(value) will actually lookup for Show[Any] .

Also, I had an idea to accept HList with context bound, but I have not found any example of that.

One more variant is to create a lot of overloaded methods (as it is done in a lot of libraris):

def appendOptional[T1: Show, T2: Show](to: Map[String, String], v1: (String, Option[T1], v2: (String, Option[T2])))

Question : Is there a way to define context bound for varargs functions?

Strictly speaking, the first is the correct way to define the bound; varargs don't mean the type of arguments varies, only their number.

The way to achieve what you want with varying types is more involved and requires packing Show instances together with values. Eg

case class HasShow[A](x: A)(implicit val ev: Show[A])

def appendOptional(to: Map[String, String], values: (String, Option[HasShow[_]])*): Map[String, String] =     
    values.foldLeft(values) {
        // value.ev.show(value.x)) can be extracted into a method on HasShow as well
        case (collector, (key, Some(value: HasShow[a]))) => 
            collector + (key -> value.ev.show(value.x)) 
        case (collector, _) => collector
    }

val props = appendOptional(initial, "name" -> name.map(HasShow(_)), "age" -> age.map(HasShow(_)))

You can sprinkle some more implicit conversions for HasShow to simplify the call-site, but this way you can see what's going on better.

For this specific case I think a better and simpler solution would be

implicit class MapOp(self: Map[String, String]) extends AnyVal {
    def appendOptional[A: Show](key: String, value: Option[A]) =  
        value.fold(self)(x => self + (key -> Show.show(x)))
}

val props = initial.appendOptional("name", name).appendOptional("age", age)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM