简体   繁体   English

上下文绑定到varargs

[英]Context bound for varargs

Few days ago I started to learn Cats and I want to implement method appendOptional for Map[String, _: Show] . 几天前,我开始学习Cats,并且想为Map[String, _: Show]实现方法appendOptional

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] . 我知道这种方法非常幼稚和直接,因为implicitly[Show[T]].show(value)实际上会查找Show[Any]

Also, I had an idea to accept HList with context bound, but I have not found any example of that. 另外,我有一个想法可以接受带有上下文绑定的HList ,但是我还没有找到任何这样的例子。

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? 问题 :有没有一种方法可以为varargs函数定义上下文绑定?

Strictly speaking, the first is the correct way to define the bound; 严格来说,第一种定义边界的正确方法;第二种定义边界的正确方法。 varargs don't mean the type of arguments varies, only their number. varargs并不意味着参数的类型有所不同,只是它们的数量有所不同。

The way to achieve what you want with varying types is more involved and requires packing Show instances together with values. 使用各种类型来实现所需功能的方法涉及更多,并且需要将Show实例与值打包在一起。 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. 您可以对HasShow进行一些更多的隐式转换,以简化调用站点,但是通过这种方式,您可以看到发生了什么。

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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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