簡體   English   中英

在Scala中正確編碼這種存在類型?

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

我有興趣從Coutts等人的Stream Fusion論文中編碼這種Stream類型。 我在Scala中探索流融合,嘗試使用宏來代替GHC的重寫規則。

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

我嘗試了幾種不同的方法,但我不確定如何在Scala中對Stream的類型進行編碼,使得兩次出現的S都指向相同的類型。 我很容易寫出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]

到目前為止這種類型似乎是正確 我已經使用了協方差,即使我們收到一個收益並返回完成或步驟,A => A類型的函數也會起作用。 就像在Haskell中一樣。

我的觀點一直是Stream的標志。 我一直試圖將它定義為一個案例類。 到目前為止唯一有效的簽名是使用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])

有沒有辦法對它進行編碼,以便不需要元組? 更接近Haskell的(假設存在運算符):

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

其中每個成員可以是單獨的字段。

我也可以在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)
}

但這有點復雜。 我希望有一種更清晰的方式,我不知道。 此外,當我嘗試探索此路徑時,我必須做一些事情來滿足編譯器,例如添加AnyRef綁定,並且unapply方法不起作用。 來自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) }
                    ^

首先, Step看起來很完美。 至於Stream ,我認為你在抽象類型上走在正確的軌道上。 以下是我提出的內容(包括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))
}

有幾點需要注意:

  • 我的unapply有一個依賴方法類型:它取決於sS 我想這可能是你的絆腳石。
  • unstreamunfold方法不是尾遞歸的。

我對自己還不是很清楚的事情是,為什么S對於存在/隱藏/什么是重要的。 如果不是,你可以寫:

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

......但我認為這是有道理的。 話雖這么說,我也不確定這種方法是否真的以你想要的方式隱藏了S 但這是我的故事,我堅持下去。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM