所有在斯卡拉

如下所示,在Haskell中,可能存储具有不同types的列表值,并且在其上具有特定的上下文边界:

data ShowBox = forall s. Show s => ShowBox s heteroList :: [ShowBox] heteroList = [ShowBox (), ShowBox 5, ShowBox True] 

我怎么能达到相同的斯卡拉,最好是没有分类?

正如@Michael Kohl所评论的那样,Haskell中的这个使用是一个存在的types,可以使用forSome构造或通配符在Scala中精确地复制。 这意味着@范例的答案基本上是正确的。

尽pipe如此,Haskell的原创还是有一些缺点,那就是它的ShowBoxtypes的实例也以一种使它们可用于列表元素的方式捕获相应的Show类类实例,即使当确切的基础types已经被存在地量化。 你对@ paradigmatic答案的评论表明,你希望能够写一些相当于下面的Haskell的东西,

 data ShowBox = forall s. Show s => ShowBox s heteroList :: [ShowBox] heteroList = [ShowBox (), ShowBox 5, ShowBox True] useShowBox :: ShowBox -> String useShowBox (ShowBox s) = show s -- Then in ghci ... *Main> map useShowBox heteroList ["()","5","True"] 

@Kim Stebel的回答显示了通过利用子types在面向对象的语言中进行这种操作的规范方法。 其他事情是平等的,这是在斯卡拉正确的方式。 我相信你知道这一点,并有充分的理由希望避免分类和复制斯卡拉Haskelltypes类的方法。 开始 …

请注意,在单元的Show类类实例之上的Haskell中,Int和Bool在useShowBox函数的实现中可用。 如果我们试图直接把它转换成Scala,我们会得到类似的东西,

 trait Show[T] { def show(t : T) : String } // Show instance for Unit implicit object ShowUnit extends Show[Unit] { def show(u : Unit) : String = u.toString } // Show instance for Int implicit object ShowInt extends Show[Int] { def show(i : Int) : String = i.toString } // Show instance for Boolean implicit object ShowBoolean extends Show[Boolean] { def show(b : Boolean) : String = b.toString } case class ShowBox[T: Show](t:T) def useShowBox[T](sb : ShowBox[T]) = sb match { case ShowBox(t) => implicitly[Show[T]].show(t) // error here ^^^^^^^^^^^^^^^^^^^ } val heteroList: List[ShowBox[_]] = List(ShowBox(()), ShowBox(5), ShowBox(true)) heteroList map useShowBox 

而这个在useShowBox中编译失败如下,

 <console>:14: error: could not find implicit value for parameter e: Show[T] case ShowBox(t) => implicitly[Show[T]].show(t) ^ 

这里的问题是,与Haskell的情况不同,Show类的实例不会从ShowBox参数传播到useShowBox函数的主体,因此不能使用。 如果我们试图通过在useShowBox函数上添加一个额外的上下文绑定来解决这个问题,

 def useShowBox[T : Show](sb : ShowBox[T]) = sb match { case ShowBox(t) => implicitly[Show[T]].show(t) // Now compiles ... } 

这修复了useShowBox中的问题,但现在我们不能在我们的存在量化列表中使用它与地图,

 scala> heteroList map useShowBox <console>:21: error: could not find implicit value for evidence parameter of type Show[T] heteroList map useShowBox ^ 

这是因为当useShowBox被作为参数提供给map函数时,我们必须根据我们在那个点上的types信息来select一个Show实例。 显然,不只有一个Show实例可以完成这个列表的所有元素,所以这个编译失败(如果我们已经为Any定义了一个Show实例,那么就会有,但这不是我们在这里之后……我们要根据每个列表元素的最具体的types来select一个types类实例)。

为了使它在Haskell中以相同的方式工作,我们必须在useShowBox的主体中显式传播Show实例。 这可能是这样的,

 case class ShowBox[T](t:T)(implicit val showInst : Show[T]) val heteroList: List[ShowBox[_]] = List(ShowBox(()), ShowBox(5), ShowBox(true)) def useShowBox(sb : ShowBox[_]) = sb match { case sb@ShowBox(t) => sb.showInst.show(t) } 

那么在REPL中,

 scala> heteroList map useShowBox res7: List[String] = List((), 5, true) 

请注意,我们已经清除了ShowBox的上下文绑定,以便为包含的值显示实例的名称(showInst)。 然后在useShowBox的主体中,我们可以明确地应用它。 还要注意,模式匹配对于确保我们只在函数的主体中打开一次存在types至关重要。

应该很明显,这是比等效的Haskell更多的vebose,我强烈build议在Scala中使用基于子types的解决scheme,除非您有非常好的理由。

编辑

正如在注释中指出的,上面的ShowBox的Scala定义有一个可见的types参数,它在Haskell原始中不存在。 我认为,看看我们如何使用抽象types来纠正这个问题,实际上是非常有益的。

首先我们用抽象types成员replacetypes参数,并用抽象的valsreplace构造函数参数,

 trait ShowBox { type T val t : T val showInst : Show[T] } 

我们现在需要添加案例类可以免费提供给我们的工厂方法,

 object ShowBox { def apply[T0 : Show](t0 : T0) = new ShowBox { type T = T0 val t = t0 val showInst = implicitly[Show[T]] } } 

我们现在可以使用简单的ShowBox,无论我们以前使用ShowBox [_] …抽象types成员现在正在为我们扮演存在量词的angular色,

 val heteroList: List[ShowBox] = List(ShowBox(()), ShowBox(5), ShowBox(true)) def useShowBox(sb : ShowBox) = { import sb._ showInst.show(t) } heteroList map useShowBox 

(值得注意的是,在Scala中引入一些和通配符之前,这正是你如何表示存在types。)

现在,我们在原有的Haskell中拥有完全一样的存在。 我认为这与您可以在Scala中获得的忠实呈现非常接近。

你给的ShowBox例子涉及到一个存在types 。 我正在将ShowBox数据构造函数重命名为SB以将其与以下types区分开来:

 data ShowBox = forall s. Show s => SB s 

我们说s是“存在的”,但这里的全部是与SB数据构造函数有关的通用量词。 如果我们打开明确所有的要求的SB构造函数的types,这变得更清晰:

 SB :: forall s. Show s => s -> ShowBox 

也就是说,一个ShowBox实际上是由三件事情构成的:

  1. A型
  2. typess的值
  3. Show s一个实例。

因为typess成为构build的ShowBox一部分,所以它是存在的量化 。 如果Haskell支持存在量化的语法,我们可以将ShowBox作为一个types别名:

 type ShowBox = exists s. Show s => s 

斯卡拉确实支持这种存在的量化,而迈尔斯的答案用上述三件事情组成的特质给出了细节。 但是由于这是一个关于“斯卡拉的虚拟化”的问题,我们就像哈斯克尔那样做。

Scala中的数据构造函数不能用forall进行明确的量化。 但是,模块上的每个方法都可以。 所以你可以有效地使用types构造函数多态作为通用量化。 例:

 trait Forall[F[_]] { def apply[A]: F[A] } 

一个ScalatypesForall[F] ,给定一些F ,然后相当于一个Haskelltypes的forall a. F a forall a. F a

我们可以使用这种技术为types参数添加约束。

 trait SuchThat[F[_], G[_]] { def apply[A:G]: F[A] } 

typesF SuchThat G的值F SuchThat G就像Haskelltypes的值forall a. G a => F a forall a. G a => F a 。 如果存在的话, G[A]的实例隐式地被Scala查找。

现在,我们可以使用它来编码您的ShowBox

 import scalaz._; import Scalaz._ // to get the Show typeclass and instances type ShowUnbox[A] = ({type f[S] = S => A})#f SuchThat Show sealed trait ShowBox { def apply[B](f: ShowUnbox[B]): B } object ShowBox { def apply[S: Show](s: => S): ShowBox = new ShowBox { def apply[B](f: ShowUnbox[B]) = f[S].apply(s) } def unapply(b: ShowBox): Option[String] = b(new ShowUnbox[Option[String]] { def apply[S:Show] = s => some(s.shows) }) } val heteroList: List[ShowBox] = List(ShowBox(()), ShowBox(5), ShowBox(true)) 

ShowBox.apply方法是普遍量化的数据构造函数。 你可以看到,它的types是SShow[S]一个实例,以及一个typesS的值,就像Haskell版本一样。

这是一个示例用法:

 scala> heteroList map { case ShowBox(x) => x } res6: List[String] = List((), 5, true) 

在Scala中更直接的编码可能是使用一个case类:

 sealed trait ShowBox case class SB[S:Show](s: S) extends ShowBox { override def toString = Show[S].shows(s) } 

然后:

 scala> val heteroList = List(ShowBox(()), ShowBox(5), ShowBox(true)) heteroList: List[ShowBox] = List((), 5, true) 

在这种情况下, List[ShowBox]基本上等同于List[String] ,但是您可以使用Show以外的其他特性来获得更有趣的内容。

这全部使用Scalaz的Showtypes类。

我不认为从Haskell到Scala的一对一翻译是可能的。 但是,为什么你不想使用子types? 如果要使用的types(如Int)缺lessshow方法,则仍然可以通过隐式转换来添加。

 scala> trait Showable { def show:String } defined trait Showable scala> implicit def showableInt(i:Int) = new Showable{ def show = i.toString } showableInt: (i: Int)java.lang.Object with Showable scala> val l:List[Showable] = 1::Nil l: List[Showable] = List($anon$1@179c0a7) scala> l.map(_.show) res0: List[String] = List(1) 

编辑 :添加方法显示,回答评论。)

我认为你可以使用带有上下文边界的隐式方法得到相同的结果:

 trait Show[T] { def apply(t:T): String } implicit object ShowInt extends Show[Int] { def apply(t:Int) = "Int("+t+")" } implicit object ShowBoolean extends Show[Boolean] { def apply(t:Boolean) = "Boolean("+t+")" } case class ShowBox[T: Show](t:T) { def show = implicitly[Show[T]].apply(t) } implicit def box[T: Show]( t: T ) = new ShowBox(t) val lst: List[ShowBox[_]] = List( 2, true ) println( lst ) // => List(ShowBox(2), ShowBox(true)) val lst2 = lst.map( _.show ) println( lst2 ) // => List(Int(2), Boolean(true)) 

为什么不:

 trait ShowBox { def show: String } object ShowBox { def apply[s](x: s)(implicit i: Show[s]): ShowBox = new ShowBox { override def show: String = i.show(x) } } 

正如当局的回答所build议的那样,我经常感到惊讶的是,Scala可以将“Haskell型怪物”翻译成非常简单的怪物。