加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

Scala类型类最佳实践

发布时间:2020-12-16 09:56:00 所属栏目:安全 来源:网络整理
导读:我正在阅读并通过使用类型类来解决这个问题,并且我从Shapeless指南中找到了这种定义类型类的方法: 所以这里是一个例子: object CsvEncoder { // "Summoner" method def apply[A](implicit enc: CsvEncoder[A]): CsvEncoder[A] = enc // "Constructor" meth
我正在阅读并通过使用类型类来解决这个问题,并且我从Shapeless指南中找到了这种定义类型类的方法:

所以这里是一个例子:

object CsvEncoder {
  // "Summoner" method
  def apply[A](implicit enc: CsvEncoder[A]): CsvEncoder[A] =
    enc
  // "Constructor" method
  def instance[A](func: A => List[String]): CsvEncoder[A] =
    new CsvEncoder[A] {
      def encode(value: A): List[String] =
        func(value)
      }
    // Globally visible type class instances
}

我不明白需要申请方法吗?在上面这个背景下它做了什么?

稍后,该指南描述了如何创建类型类实例:

implicit val booleanEncoder: CsvEncoder[Boolean] =
  new CsvEncoder[Boolean] {
    def encode(b: Boolean): List[String] =
      if(b) List("yes") else List("no") 
  }

实际上缩写为:

implicit val booleanEncoder: CsvEncoder[Boolean] =
instance(b => if(b) List("yes") else List("no"))

所以现在我的问题是,这是如何工作的?我没有得到的是需要申请方法?

编辑:我发现了一篇博客文章,描述了创建类型类的步骤,如下所示:

>定义类型类合同特征Foo.
>使用辅助方法定义伴随对象Foo,其作用类似于隐式,以及通常从函数定义Foo实例的方法.
>定义定义一元或二元运算符的FooOps类.
>定义从Foo实例隐式提供FooOps的FooSyntax特征.

那么点数2,3和4的交易是什么?

解决方法

大多数这些实践来自Haskell(基本上是模仿Haskell类型类的意图是这么多模板的原因),其中一些只是为了方便起见.所以,

2)正如@Alexey Romanov所提到的,带有apply的伴随对象只是为了方便,所以你可以只编写CsvEncoder [IceCream](又名CsvEncoder.apply [IceCream]())而不是隐式[CsvEncoder [IceCream]],这将返回你是一个必需的类型类实例.

3)FooOps为DSL提供便利方法.例如,你可以有类似的东西:

trait Semigroup[A] {
   ...
   def append(a: A,b: A)
}

import implicits._ //you should import actual instances for `Semigroup[Int]` etc.
implicitly[Semigroup[Int]].append(2,2)

但有时调用append(2,2)方法很不方便,因此提供符号别名是一种很好的做法:

trait Ops[A] {
    def typeClassInstance: Semigroup[A]
    def self: A
    def |+|(y: A): A = typeClassInstance.append(self,y)
  }

  trait ToSemigroupOps {
    implicit def toSemigroupOps[A](target: A)(implicit tc: Semigroup[A]): Ops[A] = new Ops[A] {
      val self = target
      val typeClassInstance = tc
    }
  }

  object SemiSyntax extends ToSemigroupOps

4)您可以按如下方式使用它:

import SemiSyntax._ 
import implicits._ //you should also import actual instances for `Semigroup[Int]` etc.

2 |+| 2

如果你想知道为什么这么多样板文件,以及为什么scala的隐式类语法不能从头开始提供这个功能 – 答案是隐式类实际上提供了一种创建DSL的方法 – 它只是不那么强大 – 它(主观上)提供操作更难别名,处理更复杂的调度(如果需要)等.

但是,有一个宏解决方案可以自动生成样板:https://github.com/mpilquist/simulacrum.

关于CsvEncoder示例的另一个重点是,instance是创建类型类实例的便捷方法,但apply是“召唤”(需要)这些实例的快捷方式.因此,第一个用于库扩展器(一种实现接口的方式),另一个用于用户(一种调用为该接口提供的特定操作的方法).

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读