Scala中的参数化数据类型

时间:2011-07-14 13:53:59

标签: scala haskell types

在阅读文章"Data types a la carte" by Wouter Swierstra时,我一直坚持将以下Haskell代码翻译成Scala:

data Expr f = In (f (Expr f ))

Expr是用于表示算术表达式的数据类型,具体表达式的编写方式如下:

data Val e = Val Int
type IntExpr = Expr Val

data Add e = Add e e
type AddExpr = Expr Add

我的问题是在Scala中实现f(可能被认为是构造函数的签名)。

P.S。定义两个签名的副产品,稍后您可以组合数据类型,获得Expr (Val :+: Add )类型的表达式:

data (f :+: g) e = Inl (f e) | Inr (g e)

addExample :: Expr (Val :+: Add )
addExample = In (Inr (Add (In (Inl (Val 118))) (In (Inl (Val 1219)))))

2 个答案:

答案 0 :(得分:6)

也许像

case class Expr[f[_]] (in : f [Expr[f]])

但这并不像Haskell那样有用。假设你定义了

case class Val[e] (v: Int)

然后Val(3)的类型为Val[Nothing],您无法将其与Expr一起使用。

scala> val e = Expr(Val(3))               
<console>:9: error: no type parameters for method apply: 
(in: f[Expr[f]])Expr[f] in object Expr exist so that it can be applied 
to arguments (Val[Nothing])
 --- because ---
argument expression's type is not compatible with formal parameter type;
 found   : Val[Nothing]
 required: ?f[ Expr[?f] ]
       val e = Expr(Val(3))

您仍然可以明确指定类型

val e = Expr(Val(3):Val[Expr[Val]])

但这并不好玩。您当然可以定义正确类型的函数并使用它而不是Val。

请注意,我仍然是Scala noob,也许还有更优雅的方法。

答案 1 :(得分:2)

我突然发现这个blogpost提供了一些关于将“数据类型单点”翻译成Scala的一些很好的解释。建议的解决方案如下:

case class Val[E](i: Int)
case class Add[E](left: E, right: E)

case class Expr[F[X]](e: F[Expr[F]])

sealed trait Sum[F[X], G[X], E]
case class Inl[F[X], G[X], E](l: F[E]) extends Sum[F,G,E]
case class Inr[F[X], G[X], E](r: G[E]) extends Sum[F,G,E]

trait Apply2Of3[F[A[_],B[_],_],A[_],B[_]] {
        type It[C] = F[A,B,C]
}

type Tmp[X] = Apply2Of3[Sum,Val,Add]#It[X]

val addExample: Expr[Tmp] = In[Tmp](Inr(Add(In[Tmp](Inl(Val(118))), In[Tmp](Inl(Val(1219))))))

它远没有原始的那样(在Haskell中制作),但在1)它表明通常可以在Scala中实现这个想法,以及2)带来Scala的一些弱点的意义上非常有用。与Haskell相比。