简体   繁体   English

在Scala中正确编码这种存在类型?

[英]Correct encoding of this existential type in Scala?

I'm interested in encoding this Stream type from the Stream Fusion paper from Coutts et al. 我有兴趣从Coutts等人的Stream Fusion论文中编码这种Stream类型。 I'm exploring stream fusion in Scala, attempting to use macros in place of GHC's rewrite rules. 我在Scala中探索流融合,尝试使用宏来代替GHC的重写规则。

data Stream a = ∃s. Stream (s → Step a s) s
data Step a s = Done
              | Yield a s 
              | Skip s

I've tried a few different approaches but I'm not sure how to encode the type of Stream in Scala such that both occurrences of S refer to the same type. 我尝试了几种不同的方法,但我不确定如何在Scala中对Stream的类型进行编码,使得两次出现的S都指向相同的类型。 I've written the Step type easily as. 我很容易写出Step类型。

sealed abstract class Step[+A, +S]
case object Done extends Step[Nothing, Nothing]
case class Yield[A, S](a: A, s: S) extends Step[A, S]
case class Skip[S](s: S) extends Step[Nothing, S]

So far this type seems correct. 到目前为止这种类型似乎是正确 I've used covariance so that a function of type A => A will work even if we receive a Yield and return a Done or Step. 我已经使用了协方差,即使我们收到一个收益并返回完成或步骤,A => A类型的函数也会起作用。 Just like in Haskell. 就像在Haskell中一样。

My sticking point has been the signature of Stream. 我的观点一直是Stream的标志。 I've been attempting to define it as just a case class. 我一直试图将它定义为一个案例类。 The only signature that has worked so far is using an Exists type operator and Tuple to perserve the equality of type S in both components as below. 到目前为止唯一有效的签名是使用Exists类型运算符和Tuple来保持两个组件中S类型的相等性,如下所示。

type Exists[P[_]] = P[T] forSome { type T }

case class Stream[A](t: Exists[({ type L[S] = (S => Step[A, S], S)})#L])

Is there a way to encode it such that the tuple is not needed? 有没有办法对它进行编码,以便不需要元组? Something closer to Haskell's (assuming existential operator) this: 更接近Haskell的(假设存在运算符):

case class Stream(∃ S. f: S => Step[A, S], s: S)

where each member can be separate field. 其中每个成员可以是单独的字段。

It also occurs to me that I could encode this in an SML Module/Functor style like so: 我也可以在SML模块/ Functor样式中对此进行编码,如下所示:

trait Stream[A] {
  type S <: AnyRef
  val f: S => Step[A, S]
  val s: S
}

object Stream {
  def apply[A, S1 <: AnyRef](next: S1 => Step[A, S1], st: S1): Stream[A] = new Stream[A] {
    type S = S1
    val f = next
    val s = st
  }

  def unapply[A](s: Stream[A]): Option[(s.f.type, s.s.type)] = Some(s.f, s.s)
}

but this is a little more complicated. 但这有点复杂。 I was hoping there exists a clearer way, that I am ignorant of. 我希望有一种更清晰的方式,我不知道。 Also as I attempted to explore this path, I had to do a few things to satisfy the compiler such as add the AnyRef bound, and the unapply method doesn't work. 此外,当我尝试探索此路径时,我必须做一些事情来满足编译器,例如添加AnyRef绑定,并且unapply方法不起作用。 With this error message from scalac: 来自scalac的此错误消息:

scala> res2 match { case Stream(next, s) => (next, s) }
<console>:12: error: error during expansion of this match (this is a scalac bug).
The underlying error was: type mismatch;
 found   : Option[(<unapply-selector>.f.type, <unapply-selector>.s.type)]
 required: Option[(s.f.type, s.s.type)]
               res2 match { case Stream(next, s) => (next, s) }
                    ^

First off, Step looks perfect to me. 首先, Step看起来很完美。 As for Stream , I think you're on the right track with the abstract type. 至于Stream ,我认为你在抽象类型上走在正确的轨道上。 Here's what I came up with (including implementations of the remaining methods in section 2.1 of the Coutts paper): 以下是我提出的内容(包括Coutts论文2.1节中其余方法的实现):

abstract class Stream[A] {
  protected type S
  def next: S => Step[A, S]
  def state: S

  def map[B](f: A => B): Stream[B] = {
    val next: S => Step[B, S] = this.next(_) match {
      case Done        => Done
      case Skip(s)     => Skip(s)
      case Yield(a, s) => Yield(f(a), s)
    }
    Stream(next, state)
  }

  def unstream: List[A] = {
    def unfold(s: S): List[A] = next(s) match {
      case Done        => List.empty
      case Skip(s)     => unfold(s)
      case Yield(a, s) => a :: unfold(s)
    }
    unfold(state)
  }
}

object Stream {
  def apply[A, S0](n: S0 => Step[A, S0], s: S0) = new Stream[A] {
    type S = S0
    val next = n
    val state = s
  }

  def apply[A](as: List[A]): Stream[A] = {
    val next: List[A] => Step[A, List[A]] = {
      case a :: as => Yield(a, as)
      case Nil     => Done
    }
    Stream(next, as)
  }

  def unapply[A](s: Stream[A]): Option[(s.S => Step[A, s.S], s.S)] =
    Some((s.next, s.state))
}

A couple things to note: 有几点需要注意:

  • My unapply has a dependent method type: it depends on the sS . 我的unapply有一个依赖方法类型:它取决于sS I think that might have been your stumbling block. 我想这可能是你的绊脚石。
  • The unfold method in unstream is not tail-recursive. unstreamunfold方法不是尾递归的。

The thing I'm still not really clear on myself is why it's important for S to be existential / hidden / whatever. 我对自己还不是很清楚的事情是,为什么S对于存在/隐藏/什么是重要的。 If it's not, you could just write: 如果不是,你可以写:

case class Stream[A, S](next: S => Step[A, S], state: S)

... but I assume there's a reason for it. ......但我认为这是有道理的。 That being said, I'm also not sure this approach actually hides S the way you want. 话虽这么说,我也不确定这种方法是否真的以你想要的方式隐藏了S But this is my story and I'm sticking to it. 但这是我的故事,我坚持下去。

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

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