【问题标题】:Scala higher kinded type syntaxScala 高级类型语法
【发布时间】:2023-03-30 23:56:01
【问题描述】:

我是 Scala 的新手,也是高级类型的新手。我想写这样的东西;

trait Actor[E[Dependency] <: Event[Dependency]] {
  def execute(dependency: Dependency): Unit
}

但是我不能在execute方法中引用类型参数Dependency - 编译器不知道它。

我知道我可以在没有 HKT 的情况下通过以下方式解决它,但这不是这个问题的目的;

trait Actor[T <: Event[Dependency], Dependency] {
   def execute(dependency: Dependency): Unit
}

我想了解为什么它不适用于我尝试过的更高种类的类型语法?是否有可能用 HKT 表达这一点?这是 HKT 的有效用例吗?


编辑

更多信息,Event 看起来像这样;

trait Event[Data] {
   val payload: Data
}

...我正在寻找像这样定义一个事件和一个演员;

case class FooEvent(payload: Foo) extends Event[Foo]

class FooActor extends Actor[FooEvent] {
   def execute(dependency: Foo) = {}
}

【问题讨论】:

    标签: scala generics types higher-kinded-types


    【解决方案1】:

    我会尝试改进 Alexey 的答案 - 他是对的,但他太矮了。但我必须说我不是 HKT 的专家,我想我才刚刚开始理解这个概念。

    在您的代码中,E[Dependency]E[_] 相同,这表示您有 E 以某种类型作为参数。这意味着您不会将Dependency 作为类型进行操作。您也不要对 EE[Dependency] 作为类型进行操作。 E 是一个类型构造函数,如果我理解正确的话,E[Dependency] 是一个存在类型。请注意,

    trait Actor[E[D] <: Event[D]] { def execute(d: E) {} }
    

    trait Actor[E[D] <: Event[D]] { def execute(d: E[D]) {} }
    

    也不会编译。

    您需要指定正确的类型作为执行的参数:

    trait Actor[E[D] <: Event[D]] { def execute[B](d: E[B]) {} }
    

    这将编译为E[B] 是此上下文中的类型。

    更新:

    请看一下这段代码:

      trait Event[P] {
        val payload: P
      }
    
      case class FooEvent(payload: Int) extends Event[Int]
    
      trait BaseActor {
        type E = Event[P]
        type P
        def execute(dep: P)
        def runEvent(event: E)
      }
    
      trait IntActor extends BaseActor {
        type P = Int
      }
    
      class FooActor extends IntActor {
        def execute(dependency: P) = {}
        def runEvent(event: E) = {}
      }
    
      val c = new FooActor()
      c.runEvent(FooEvent(5))
      c.execute(5)
    

    基本上,诀窍是定义type P,这是我们的Dependencytype E = Event[P],它始终是Event[Dependency],然后您可以通过定义P 来使用actor,而无需像已经定义的那样定义E。不确定它是否能解决问题,但它看起来像是找我的一种方式。这里的类型也太多了,像IntActor这样的就不需要了。我把它们放在了上面,这样更容易理解这个例子

    【讨论】:

    • 这很有用,但只是为了澄清:尽管语法相似,但不涉及存在类型。
    • 我现在不在电脑旁,但我认为您的第一个示例确实可以编译。想想我昨天就这么简单地做了。
    • 你的最后一个例子很有趣,但并没有真正解决我的问题,因为方法定义上的类型参数与特征上的参数没有关系。
    • @RyanWorsley 我不认为你的情况是你需要使用 HKT 的东西。您需要将 Dependency 定义为一种类型,以便能够指定它。您想要的确切内容在逻辑上是不可能的,请参见 Alexey 的示例。如果不指定Data 类型,则无法创建Actor 实例,因为Actor 依赖于Data
    • 你说得对,我不需要这样做,事实上我在我原来的问题中展示了如何在没有 HKT 的情况下做到这一点。我走这条路的原因是因为在我看来编译器有足够的信息让我捕获和使用类型依赖。这使得整体语法更好,因为我只需要为 Actor 指定一个类型参数,而不是两个。仔细想想,FooActor 的定义告诉编译器 Dependency 的类型,所以我认为 HKT 几乎就像类型的模式匹配,我可以在其中分解类型结构并使用部分。
    【解决方案2】:

    但是我不能在execute方法中引用类型参数Dependency - 编译器不知道它。

    你不能,因为它不是Actor 的参数。考虑

    val actor = new Actor[Event] // E is Event
    actor.execute(???) // what argument is this supposed to take? I.e. what is Dependency for Actor[Event]?
    

    更新:鉴于您的编辑,[Dependency, T &lt;: Event[Dependency]] 选项正是您需要的。当你写Actor[E[Dependency] &lt;: Event[Dependency]] 时,这意味着E 本身必须有一个类型参数。而FooEvent 不会,所以Actor[FooEvent] 不会编译。

    更新 2:您可以尝试如下使用类型成员:

    trait Event {
      type Dependency
      val payload: Dependency
    }
    
    trait Actor {
      type E <: Event
    
      def execute(e: E#Dependency)
    }
    
    class Foo
    
    case class FooEvent(payload: Foo) extends Event {
      type Dependency = Foo
    }
    
    class FooActor extends Actor {
      type E = FooEvent
    
      def execute(e: Foo) = {}
    }
    

    【讨论】:

    • 不确定我是否遵循你的论点,所以我添加了一些更多细节,应该向你展示我正在尝试做的事情。
    • 所以我实际上要说的是,Actor 依赖于某种类型,而该类型本身又依赖于另一种类型。然后我无法将方法执行定义为采用类型依赖关系?这只是编译器的限制还是根本不可能?
    • 我实际上混合了两种答案,现在认为我更好地理解了 HKT 的目的。我选择了 Archeg,因为我觉得那里有更多信息可供以后来阅读此内容的任何人使用。感谢两位的帮助。
    猜你喜欢
    • 2013-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-16
    • 2013-05-24
    • 2017-09-12
    • 2015-03-08
    • 2022-12-11
    相关资源
    最近更新 更多