【问题标题】:Scala mix with instanceScala 与实例混合
【发布时间】:2021-11-19 10:45:26
【问题描述】:

我想知道是否可以将 Scala 特征与实例混合以创建匿名实例。

基本上我会做这样的事情

trait Duck {
 def walk : Unit
}

def learnToDuckWalk[A](a : A) : A with Duck = a with Duck {
 def walk = print("flic flak floc")
}

这个想法是创建一个新类型,而不必定义一个扩展 2 种类型的全新类。 因为为每种类型定义一个新类可能会导致类数量呈指数级增长,每次我们向 mixin 添加新的 Trait 时都会增长。

我在 ZIO.Has 中很少看到类似这样的东西,但我想知道它是否适用于纯 Scala 2 甚至 Scala 3。

【问题讨论】:

  • 这正是 ad-hoc-polymorphism 的样子。类型类是这些用例的必经之路。
  • 我可以看到有人用宏来做这件事,但是你能举个例子说明 ZIO 是怎么做的,这样用例就更清楚了吗?

标签: scala traits mixins scala-3


【解决方案1】:

你真的需要一个通用参数A,还是只是为了举例?如果你有一个具体的类型而不是A,那么你可以创建一个混合了这两个特征的实例:

trait Duck {
  def walk: Unit
}

trait Goose {
  def run: Unit
}

def learnToDuckWalk: Goose with Duck = new Goose with Duck {
  def run = println("tip tap top")
  def walk = println("flic flak floc")
}

learnToDuckWalk.walk
learnToDuckWalk.run

但是,拥有一个通用的 A 和“附加”一些额外的特征并不是一个好的设计,即使你设法实现它(例如使用反射)。

更好的方法是,不要说“我有一个与Duck 混合在一起的A”,而是说“我有一个A 有一个Duck 类型的类实例”。

trait Duck [A]{
  def walk : Unit
}

object Duck {

  implicit val duckString = new Duck[String] {
    def walk = print("Stringy flic flak floc")
  }

  implicit val duckInt = new Duck[Int] {
    def walk = print("Inty flic flak floc")
  }

  ...

  implicit def duckA[A] = new Duck[A] {
    def walk = print("Generic flic flak floc")
  }
}

通过这种方式,您可以为您想要的任何类型提供类型类实例,并且您可以让它们根据实际类型表现出不同的行为(请参阅walk 如何为每种类型打印不同的消息)。

那么你的使用站点可以是这样的:

import Duck._

def learnToDuckWalk[A : Duck](a : A) = {
  val aDuck = implicitly[Duck[A]]
  aDuck.walk // prints Stringy flic flak floc
}

learnToDuckWalk("Some string")

这里的重要部分是[A : Duck] 类型,这意味着“任何A 存在Duck 类型的类实例”。

【讨论】:

  • 这并不能解决我的问题,我不需要一个类型类,而是一个具有所有能力的类型,因为我想将它们传递给一个单子阅读器。想象一下需要一个鸭子羊和牛鸡,在这种情况下,第一个解决方案将让位于很多实例。
  • 如果您使用 cake 模式或 ZIO 进行依赖管理,那么这正是您要做的 - 将一组依赖定义为 Cow with Duck with Sheep。但是您不需要“固定”它们。相反,您只需要一个一个地提供它们(ZIO 层可以提供帮助)。
  • 是的,我想知道是否可以在不拉动所有 ZIO 的情况下拥有 ZIO Has 功能。我看到另一篇关于该主题的帖子:stackoverflow.com/questions/61135323/… 基本思想是将信息传递给无标签最终程序的“实例”层,而无需在“程序”层中显示信息。通常我直接使用所需的信息创建实例。但有时我需要一些更动态的东西,它必须对同一个实例具有不同的价值。
  • 我明白了。我认为这更像是一个初学者的问题。是的,正如你现在肯定意识到的那样,为了将一些额外的特征动态地限制在已经构建的对象上,你肯定需要魔法(对我来说,“魔法”通常意味着宏和/或反射)。我仍然会选择类型类,特别是考虑到您使用的是无标签 final。像def foo[A : Cow : Duck : Sheep] 这样的东西。但是,是的,为了能够“插入”实例并且从那时起它们在类型中不可见(比如部分应用函数),我会选择 ZLayers。
猜你喜欢
  • 2015-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-10
  • 1970-01-01
  • 1970-01-01
  • 2011-03-21
相关资源
最近更新 更多