Я заинтересован в кодировании этого типа потока из бумаги Stream Fusion от Coutts et al. Я исследую слияние потоков в Scala, пытаясь использовать макросы вместо правил перезаписи GHC.Исправить кодировку этого экзистенциального типа в Scala?
data Stream a = ∃s. Stream (s → Step a s) s
data Step a s = Done
| Yield a s
| Skip s
Я пробовал несколько различных подходов, но я не уверен, как кодировать тип потока в Scala таким образом, что оба вхождения 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 будет работать, даже если мы получим доходность и вернем Done или Step. Как в Хаскелле.
Моей точкой крепления является подпись 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)
, где каждый член может быть отдельное поле.
Это также происходит со мной, что я мог бы закодировать это в стиле/Functor SML модуль следующим образом:
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) }
^
Есть ли класс case Stream [A] (t: (S => Step [A, S], S) forSome {type S}) 'не работает? –
@TravisBrown да, но jroesch пытается устранить кортеж. – mergeconflict
Ошибка «unapply-selector» - https://issues.scala-lang.org/browse/SI-6130. – extempore