scala - 如何使类型类与 scala 中的异构列表一起使用

标签 scala typeclass implicit

给出以下类型类和常见类型的一些实例

trait Encoder[A] {
  def encode(a: A): String
}

object Encoder {
  implicit val stringEncoder = new Encoder[String] {
    override def encode(a: String): String = a
  }
  implicit val intEncoder = new Encoder[Int] {
    override def encode(a: Int): String = String.valueOf(a)
  }
  implicit def listEncoder[A: Encoder] =
    new Encoder[List[A]] {
      override def encode(a: List[A]): String = {
        val encoder = implicitly[Encoder[A]]
        a.map(encoder.encode).mkString(",")
      }
    }
}

有没有办法让它发挥作用

Encoder.listEncoder.encode(List("a", 1))

最佳答案

如果您为 Any 定义实例

object Encoder {
  implicit val anyEncoder = new Encoder[Any] {
    override def encode(a: Any): String = a.toString
  }

  //instances for String, Int, List[A]
}

然后

Encoder.listEncoder[Any].encode(List("a", 1))

会起作用的。

您必须在此处显式指定类型参数 (listEncoder[Any]),因为这就是 scala 中类型推断的工作方式。事实上,在推断出 encode 的参数类型之后

Encoder.listEncoder[???].encode(List("a", 1))
//                              ^^^^^^^^^^^^
//                              List[Any]

现在来推断 listEncoder 的类型参数已经太晚了。

如果您定义委托(delegate)函数

def encode[A](a: A)(implicit encoder: Encoder[A]): String = encoder.encode(a)

或语法

implicit class EncoderOps[A](a: A)(implicit encoder: Encoder[A]) {
  def encode: String = encoder.encode(a)
}

    // or
// implicit class EncoderOps[A](a: A) {
//   def encode(implicit encoder: Encoder[A]): String = encoder.encode(a)
// }

那么您不必显式指定类型参数

encode("a")
encode(1)
encode(List("a", 1))

"a".encode
1.encode
List("a", 1).encode

顺便说一下,List("a", 1) 不是一个异质列表,它是一个元素类型为 Any 的普通同质列表。一个heterogenous list将会是

val l: String :: Int :: HNil = "a" :: 1 :: HNil

您可以尝试magnet而不是type class

trait Magnet {
  def encode(): String
}

object Magnet {
  implicit def fromInt(a: Int): Magnet = new Magnet {
    override def encode(): String = String.valueOf(a)
  }
  implicit def fromString(a: String): Magnet = new Magnet {
    override def encode(): String = a
  }
}

def encode(m: Magnet): String = m.encode()

encode("a")
encode(1)
List[Magnet]("a", 1).map(_.encode())

HList而不是List[A]

sealed trait HList 
case class ::[+H, +T <: HList](head: H, tail: T) extends HList 
case object HNil extends HList 
type HNil = HNil.type

implicit class HListOps[L <: HList](l: L) {
  def ::[A](a: A): A :: L = new ::(a, l)
}

trait Encoder[A] {
  def encode(a: A): String
}

object Encoder {
  implicit val stringEncoder: Encoder[String] = new Encoder[String] {
    override def encode(a: String): String = a
  }
  implicit val intEncoder: Encoder[Int] = new Encoder[Int] {
    override def encode(a: Int): String = String.valueOf(a)
  }
  implicit val hnilEncoder: Encoder[HNil] = new Encoder[HNil] {
    override def encode(a: HNil): String = ""
  }
  implicit def hconsEncoder[H, T <: HList](implicit
                                           hEncoder: Encoder[H],
                                           tEncoder: Encoder[T]
                                          ): Encoder[H :: T] = new Encoder[H :: T] {
    override def encode(a: H :: T): String = 
      s"${hEncoder.encode(a.head)},${tEncoder.encode(a.tail)}"
  }
}

def encode[A](a: A)(implicit encoder: Encoder[A]): String = encoder.encode(a)

implicit class EncoderOps[A](a: A)(implicit encoder: Encoder[A]) {
  def encode: String = encoder.encode(a)
}

val l: String :: Int :: HNil = "a" :: 1 :: HNil
encode(l)
l.encode

关于scala - 如何使类型类与 scala 中的异构列表一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64011072/

相关文章:

scala - Scala中的隐式抽象类构造函数参数和继承

debugging - 是否有一种系统的方法来发现哪些隐式定义在范围内,以及哪些隐式定义在特定点绑定(bind)?

scala - 使用 shapeless.Generic 时,如何避免错误 'super constructor cannot be passed a self reference unless parameter is declared by-name' ?

Scala Curried 类型不匹配

scala - Scala 符号的目的?

Scala 类型不匹配

haskell - 添加约束会导致其他约束超出范围吗?

Haskell 类依赖

scala - 使用带有一些基类、抽象类或特征参数化列表的类型类的最佳方法

scala - Scala 类型检查器如何知道防止在已经平坦的 List 上调用 flatten