简体   繁体   English

模块,与路径有关的类型和共享

[英]Modules, path-dependent types, and sharing

I'm going to briefly provide some context for my question, and then the question itself will follow. 我将简要地为我的问题提供一些背景信息,然后问题将随之而来。

In Martin Odersky's previous publications and lately at some Scala talks, he has emphasized the following relationship between Scala's type system and ML's: 在Martin Odersky的先前出版物以及最近在Scala的一些演讲中,他强调了Scala的类型系统与ML的以下关系:

  • ML signature => Scala trait ML签名=> Scala特性
  • ML module => Scala object ML模块=> Scala对象
  • ML functor => Scala class ML函子=> Scala类

There has been work on experimenting with these ideas more concretely in blog posts such as: 在以下博客文章中已经进行了更具体地试验这些想法的工作:

In particular, in the second link contributes the idea of a function definition in order to achieve type ascription. 特别是,在第二个链接中提供了功能定义的思想,以实现类型归属。

trait Set[A] {
  type T

  def empty: T
}

abstract class SetMod[A](Ord: Ordered[A]) extends Set[A] {
  // Not the real implementaton, obviously.
  type T = List[A]

  override def empty = ???

  def implementationHelper(x: Double): Int = ???
}

object MkSet {
  def apply[A](implicit O: Ordered[A]): Set[A] =
    new SetMod[A](O) {}
}

and we can now say something like 现在我们可以说类似

lazy val IntSet = MkSet[Int]

where implementationHelper is not visible to IntSet , but we could still play with the implementation interactively in the REPL if we so desired because nothing is private . 在这里IntSet不到implementationHelper ,但是如果需要的话,我们仍然可以在REPL中交互地进行实现,因为没有什么是private

Let's consider a more involved example, which is a very generic graph module: 让我们考虑一个更复杂的示例,它是一个非常通用的图形模块:

We start with the interface, keeping the representation abstract: 我们从接口开始,使表示形式保持抽象:

trait Graph[A] {
  type T <: GraphSig

  type NodeLabel

  type EdgeLabel

  type Weight

  case class Node(labe: NodeLabel, value: A)

  case class Edge(start: Node, end: Node, weight: Weight, label: EdgeLabel)

  def empty: T

  trait GraphSig {
    def nodes: Stream[Node]

    def edges: Stream[Edge]
  }
}

and then add a specialization of the interface for simple cases: 然后为简单情况添加接口的特殊化:

trait SimpleGraph[A] extends Graph[A] {
  type Weight = Unit

  type EdgeLabel = Unit

  def edge(start: Node, end: Node): Edge =
    Edge(start, end, (), ())
}

Next, the actual implementation of the module: 接下来,该模块的实际实现:

abstract class GraphMod[A] extends Graph[A] {
  override def empty = ???

  class T extends GraphSig {
    override def nodes = ???

    override def edges = ???
  }

  def someHelper(x: Double): Int = ???
}

and finally a nice helper to help us create new graph modules: 最后是一个很好的助手,可以帮助我们创建新的图形模块:

object Graph {
  trait StringLabels {
    type NodeLabel = String
  }
}

We can now construct a new specialization of this module as follows (inside the Modules object): 现在,我们可以按以下方式(在Modules对象内部)构造此模块的新专业化:

lazy val IntGraph: SimpleGraph[Int] with Graph.StringLabels =
    new GraphMod[Int] with SimpleGraph[Int] with Graph.StringLabels {}

Okay, now the actual question. 好的,现在是实际问题。

I want to construct a new module that depends on a particular graph module. 我想构造一个依赖于特定图形模块的新模块。 A functor. 函子。

In particular, I want a module to help me export graphs to different formats (like the DOT format). 特别是,我想要一个模块来帮助我将图形导出为不同的格式(例如DOT格式)。

If was writing a single function, then this works as expected: 如果正在编写单个函数,则可以按预期工作:

object GraphFormat {
  def asDot[A](G: Graph[A])(graph: G.T): String =
    "okay"
}

For example, 例如,

scala> GraphFormat.asDot(IntGraph)(IntGraph.empty)
scala.NotImplementedError: an implementation is missing
  at scala.Predef$.$qmark$qmark$qmark(Predef.scala:225)
  at GraphMod.empty(Graph.scala:39)
  at GraphMod.empty(Graph.scala:38)
  ... 43 elided

(This is not a compilation error, but expected since the body is ??? ). (这不是编译错误,但是由于正文是??? ,所以可以预期)。

But what I really want is something like this: 但是我真正想要的是这样的:

class GraphFormat[A](val Graph: Graph[A]) {
  def asDot(graph: Graph.T): String =
    "better"

  // Other stuff that depends on `Graph`...
}

and this doesn't work because there's no way (that I can think of) to "expose" the type of Graph.T outside of the definition. 这是行不通的,因为(我能想到的)没有办法在定义之外“暴露” Graph.T的类型。

scala> val F = new GraphFormat(IntGraph)
F: GraphFormat[Int] = GraphFormat@27da03d5

scala> F.asDot(IntGraph.empty)
<console>:13: error: type mismatch;
 found   : Modules.IntGraph.T
 required: F.Graph.T
              F.asDot(IntGraph.empty)

See? 看到?

Is there a reasonable way to get around this problem? 有合理的方法解决这个问题吗?

In this particular case, I could embed a Format object inside of the Graph trait, but I think this example is representative in general of problems that come up when trying to use functors in the fashion. 在这种特殊情况下,我可以在Graph特性内嵌入一个Format对象,但是我认为该示例通常代表尝试以某种方式使用函子时出现的问题。 Hence the question. 因此是一个问题。 :-) :-)

Thanks to the folks on the #scalaz IRC channel, I have found the solution based on this blog post . 感谢#scalaz IRC频道上的人们,我根据此博客文章找到了解决方案。

Namely, let's augment the Graph object with a helper, Aux , that "exposes" the value of the type parameter: 也就是说,让我们使用一个辅助器Aux扩展Graph对象,该辅助器“公开” type参数的值:

object Graph {
  trait StringLabels {
    type NodeLabel = String
  }

  type Aux[T0, A0] = Graph[A0] { type T = T0 }
}

and redefine the GraphFormat class as: 并将GraphFormat类重新定义为:

class GraphFormat[GraphInstance, A](val G: Graph.Aux[GraphInstance, A]) {
  def asDot(graph: GraphInstance): String =
    "better"

  // Other stuff that depends on `Graph`...
}

This looks scary, but using it works as expected: 这看起来很吓人,但使用起来却可以正常工作:

scala> import Modules._

scala> val F = new GraphFormat(IntGraph)
F: GraphFormat[Modules.IntGraph.T,Int] = GraphFormat@305bb85a

scala> F.asDot(IntGraph.empty)
scala.NotImplementedError: an implementation is missing
  at scala.Predef$.$qmark$qmark$qmark(Predef.scala:225)
  at GraphMod.empty(Graph.scala:41)
  at GraphMod.empty(Graph.scala:40)
  ... 43 elided

scala> :t F.asDot _
Modules.IntGraph.T => String

Woohoo! oo! :-) :-)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM