使用类型参数与抽象类型实现类型类 [英] Implementing a typeclass using type parameters versus abstract types

查看:92
本文介绍了使用类型参数与抽象类型实现类型类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

接着从所见之处抽象类型实现类型类 我试图在下面的代码片段中并行比较这两种方法:

Following on from Witness that an abstract type implements a typeclass I've tried to compare these two approaches side-by-side in the code snippet below:

// We want both ParamaterizedTC and WithAbstractTC (below) to check that 
// their B parameter implements AddQuotes 
abstract class AddQuotes[A] {
  def inQuotes(self: A): String = s"${self.toString}"  
}
implicit val intAddQuotes = new AddQuotes[Int] {}

abstract class ParamaterizedTC[A, _B](implicit ev: AddQuotes[_B]) {
  type B = _B
  def getB(self: A): B 
  def add1ToB(self: A): String = ev.inQuotes(getB(self)) // TC witness does not need to be at method level
}

abstract class WithAbstractTC[A] private { 
  // at this point the compiler has not established that type B implements AddQuotes, even if we have created
  // this instance via the apply[A, _B] constructor below...
  type B 
  def getB(self: A): B
  def add1ToB(self: A)(implicit ev: AddQuotes[B]): String = 
    ev.inQuotes(getB(self)) // ... so here the typeclass witness has to occur on the method level
}
object WithAbstractTC {
  // This constructor checks that B implements AddQuotes
  def apply[A, _B: AddQuotes](getB: A => _B): WithAbstractTC[A] = new WithAbstractTC[A] { 
    type B = _B 
    def getB(self: A): B = getB(self)
  }
  // But we could also have a constructor that does not check, so the compiler can never be certain that 
  // for a given instance of WithAbstractTC, type B implements AddQuotes
  def otherConstructor[A, _B](getB: A => _B): WithAbstractTC[A] { type B = _B } = new WithAbstractTC[A] { 
    type B = _B 
    def getB(self: A): B = getB(self)
  }
}

case class Container[B: AddQuotes]( get: B )

// These are both fine
implicit def containerIsParamaterized[B: AddQuotes]: ParamaterizedTC[Container[B], B] = 
  new ParamaterizedTC[Container[B], B] { def getB(self: Container[B]): B = self.get }
implicit def containerIsWithAbstract[_B: AddQuotes]: WithAbstractTC[Container[_B]] = 
  WithAbstractTC[Container[_B], _B](self => self.get)

val contIsParamaterized: ParamaterizedTC[Container[Int], Int] = 
  implicitly[ParamaterizedTC[Container[Int], Int]]
val contIsWithAbstract: WithAbstractTC[Container[Int]] = 
  implicitly[WithAbstractTC[Container[Int]]]

implicitly[AddQuotes[contIsParamaterized.B]]
implicitly[AddQuotes[contIsWithAbstract.B]] // This is not fine

我的结论(如果我错了,请纠正我)是,如果在公共构造函数中存在类型类见证人(如下面的ParamaterizedTC所示),则编译器始终可以确定B实现了AddQuotes.而如果将此见证人放在类型类同伴对象的构造函数中(如WithAbstractTC),则它不能.与基于抽象类型的方法相比,这在某种程度上改变了基于类型参数的方法的使用.

My conclusion (please correct me if I'm wrong) is that if the typeclass witness exists in the public constructor (as in ParamaterizedTC below) then the compiler can always be certain that B implements AddQuotes. Whereas if this witness is put in a constructor in the typeclass companion object (like for WithAbstractTC) then it cannot. This somewhat changes the usage of a type-parameter-based approach versus the abstract-type-based approach.

推荐答案

implicitly[AddQuotes[contIsWithAbstract.B]]拒绝与单/多个构造函数/apply方法或类型参数/类型成员差异无关.你只是失去类型的改进随处可见.编译器无法检查您是否丢失了类型优化.您有权放弃某个类型的优化而放弃该类型.

implicitly[AddQuotes[contIsWithAbstract.B]] refusing to compile is not connected with single/multiple constructors/apply methods or type parameter/type member difference. You just lost type refinements everywhere. Compiler can't check that you lost type refinements. You have the right to upcast a type discarding its refinement.

如果您恢复类型细化,则代码会编译

If you restore type refinements the code compiles

object WithAbstractTC {
  def apply[A, _B: AddQuotes](getB: A => _B): WithAbstractTC[A] {type B = _B} = 
//                                                              ^^^^^^^^^^^^^
    new WithAbstractTC[A] {
      type B = _B
      def getB(self: A): B = getB(self)
    }
  ...
}

implicit def containerIsWithAbstract[_B: AddQuotes]: 
  WithAbstractTC[Container[_B]] { type B = _B } =
//                              ^^^^^^^^^^^^^^^
  WithAbstractTC[Container[_B], _B](self => self.get)

val contIsWithAbstract: WithAbstractTC[Container[Int]] { type B = Int } =
//                                                     ^^^^^^^^^^^^^^^^
  shapeless.the[WithAbstractTC[Container[Int]]]
//^^^^^^^^^^^^^

implicitly[AddQuotes[contIsWithAbstract.B]] // compiles

请注意,implicitly放宽了类型细化,shapeless.the是安全版本.

Please notice that implicitly looses type refinements, shapeless.the is safe version.

implicitly不够具体 https://typelevel.org/blog/2014/01/18/implicitly_existential.html

如何通过抽象隐式对类成员类型类使用类级隐式约束,请参见 @AlexeyRomanov 的答案.

How to use class-level implicit constraint for type-member type class via abstract implicit see @AlexeyRomanov's answer.

这篇关于使用类型参数与抽象类型实现类型类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆