varargs的上下文绑定

时间:2018-04-14 07:46:22

标签: scala variadic-functions implicit scala-cats context-bound

几天前我开始学习Cats,我想为appendOptional实施方法Map[String, _: Show]

我从以下想法开始:

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
    }

并使用它:

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)

我理解这种方法非常幼稚和直接,因为implicitly[Show[T]].show(value)实际上会查找Show[Any]

另外,我有一个想法接受带有上下文限制的HList,但我没有找到任何这样的例子。

另一个变体是创建大量重载方法(因为它在很多库中完成):

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

问题:有没有办法为varargs函数定义上下文绑定?

1 个答案:

答案 0 :(得分:1)

严格来说,第一个定义边界的正确方法; varargs并不意味着论据的类型各不相同,只有它们的数量。

使用不同类型实现所需内容的方式更为复杂,需要将Show个实例与值一起打包。 E.g。

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(_)))

您可以为HasShow添加一些隐式转化,以简化呼叫网站,但这样您就可以更好地了解进展情况。

对于这个特定情况,我认为更好更简单的解决方案是

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)