Scala中此存在类型的正确编码?
我对Coutts等人的流融合论文中的这种流类型编码感兴趣。我正在Scala中探索流融合,试图用宏代替GHC的重写规则Scala中此存在类型的正确编码?,scala,types,stream,scalaz,existential-type,Scala,Types,Stream,Scalaz,Existential Type,我对Coutts等人的流融合论文中的这种流类型编码感兴趣。我正在Scala中探索流融合,试图用宏代替GHC的重写规则 data Stream a = ∃s. Stream (s → Step a s) s data Step a s = Done | Yield a s | Skip s 我尝试了几种不同的方法,但我不确定如何在Scala中对流的类型进行编码,以便两次出现的S都引用相同的类型。我已经很容易地编写了步骤类型 sealed
data Stream a = ∃s. Stream (s → Step a s) s
data Step a s = Done
| Yield a s
| Skip s
我尝试了几种不同的方法,但我不确定如何在Scala中对流的类型进行编码,以便两次出现的S都引用相同的类型。我已经很容易地编写了步骤类型
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的函数将工作,即使我们收到一个收益并返回一个完成或步骤。就像哈斯克尔一样
我的症结一直是Stream的签名。我一直试图将它定义为一个case类。到目前为止唯一有效的签名是使用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(假设存在算子)更接近的是:
其中每个成员可以是单独的字段
我还想到,我可以用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)
}
trait流[A]{
类型S步骤[A,S]
瓦尔s:s
}
对象流{
def应用[A,S1步骤[A,S1],st:S1):流[A]=新流[A]{
类型S=S1
val f=下一个
val s=st
}
def unapply[A](s:Stream[A]):选项[(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) }
^
scala>res2 match{case Stream(next,s)=>(next,s)}
:12:错误:扩展此匹配时出错(这是scalac错误)。
潜在的错误是:类型不匹配;
找到:选项[(.f.type、.s.type)]
必需:选项[(s.f.type,s.s.type)]
res2 match{case Stream(next,s)=>(next,s)}
^
首先,我觉得步骤
看起来很完美。至于流
,我认为您使用抽象类型是正确的。以下是我的想法(包括Coutts论文第2.1节中剩余方法的实现):
有几件事需要注意:
- 我的
有一个dependent方法类型:它取决于unapply
。我想这可能是你的绊脚石s.s
中的unstream
方法不是尾部递归的unfold
s
的存在性/隐藏性/任何东西为什么重要。如果不是,你可以写:
case class Stream[A, S](next: S => Step[A, S], state: S)
…但我认为这是有原因的。尽管如此,我也不确定这种方法是否真的隐藏了
s
你想要的方式。但这是我的故事,我坚持下去。对某些{type s}来说case类流[a](t:(s=>Step[a,s],s)
不起作用吗?@TravisBrown是的,但jroesch正在试图消除元组“unapply selector”bug是。是否有人正在积极研究“unapply selector”bug(我唯一看到的是几个月前Adriaan的分支)?为什么你要在Haskell中放一个存在符号,而这个符号属于一个普遍的符号?我同意抽象类型是最有希望的方法。那么,我将遵循抽象类型成员方法,我仍然对编码存在符号的最佳方法感到困惑。@mergeconflict谢谢你指出我在在依赖类型中,我看到了更清晰的解决方案。此外,隐藏类型是使相同类型的流在不考虑其内部状态/生产者的情况下都是等效的,在这方面我认为它工作得足够好。
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))
}
case class Stream[A, S](next: S => Step[A, S], state: S)