Scala 中的类型类
拥有 Haskell 背景的我目前正在尝试熟悉 Scala。
我在尝试将一种小型的、可扩展的表达式语言从 Haskell 转换为 Scala 时遇到了一些问题。编写可通过新数据变体和操作扩展的数据类型的根本问题通常称为 表达式问题。
我在 Haskell 中的原始解决方案使用带有约束的类型类和实例声明。我的表达式的基础定义如下:
module Expr where
class Expr e where
eval :: e -> Integer
data Lit = Lit Integer
instance Expr Lit where
eval (Lit l) = l
data Plus a b = (Expr a, Expr b) => Plus a b
instance (Expr a, Expr b) => Expr (Plus a b) where
eval (Plus x y) = (eval x) + (eval y)
然后,我有一个添加乘法的数据扩展:
module ExprWithMul where
import Expr
data Mul a b = (Expr a, Expr b) => Mul a b
instance (Expr a, Expr b) => Expr (Mul a b) where
eval (Mul x y) = (eval x) * (eval y)
让我们将漂亮的打印机作为操作扩展:
module FormatExpr where
import Expr
class (Expr t) => FormatExpr t where
format :: t -> String
instance FormatExpr Lit where
format (Lit l) = show l
instance (FormatExpr a, FormatExpr b) => FormatExpr (Plus a b) where
format (Plus x y) = "(" ++ (format x) ++ "+" ++ (format y) ++ ")"
最后,在第四个模块中,可以组合两个独立的扩展:
module FormatExprWithMult where
import FormatExpr
import ExprWithMul
instance (FormatExpr a, FormatExpr b) => FormatExpr (Mul a b) where
format (Mul x y) = "(" ++ (format x) ++ "*" ++ (format y) ++ ")"
现在对于我的问题:通常 haskell 中的类型类会被转换为 Scala 中隐含的概念模式。这就是我所取得的进展:
abstract class Expr[A] { // this corresponds to a type class
def eval(e:A): Int;
}
case class Lit(v: Int)
implicit object ExprLit extends Expr[Lit] {
def eval(e: Lit) = x.v;
}
case class Plus[A,B] (e1: A, e2: B) (implicit c1: Expr[A], c2: Expr[B])
在这里,我一直致力于实现 Plus 的隐式对象。如何声明带有类型参数和约束的隐式对象?
我知道 Scala 中的表达式问题还有其他解决方案,但我对这个版本特别感兴趣。
感谢大家阅读我有点冗长的问题。
Having a background in Haskell I am currently trying to get familiar with Scala.
I encountered some problems trying to translate a small, extensible expression language from Haskell into Scala. The underlying issue of writing a data type that is extensible with both new data-variants and operations is commonly known as the expression problem.
My original solution in Haskell uses type classes and instance declarations with constraints. The base of my expression is defined as follows:
module Expr where
class Expr e where
eval :: e -> Integer
data Lit = Lit Integer
instance Expr Lit where
eval (Lit l) = l
data Plus a b = (Expr a, Expr b) => Plus a b
instance (Expr a, Expr b) => Expr (Plus a b) where
eval (Plus x y) = (eval x) + (eval y)
Then, I have one data-extension that adds multiplication:
module ExprWithMul where
import Expr
data Mul a b = (Expr a, Expr b) => Mul a b
instance (Expr a, Expr b) => Expr (Mul a b) where
eval (Mul x y) = (eval x) * (eval y)
Let's take a pretty-printer as an operational extension:
module FormatExpr where
import Expr
class (Expr t) => FormatExpr t where
format :: t -> String
instance FormatExpr Lit where
format (Lit l) = show l
instance (FormatExpr a, FormatExpr b) => FormatExpr (Plus a b) where
format (Plus x y) = "(" ++ (format x) ++ "+" ++ (format y) ++ ")"
And, finally, in the fourth module the two independent extensions can be combined:
module FormatExprWithMult where
import FormatExpr
import ExprWithMul
instance (FormatExpr a, FormatExpr b) => FormatExpr (Mul a b) where
format (Mul x y) = "(" ++ (format x) ++ "*" ++ (format y) ++ ")"
Now for my problem: Usually type classes from haskell are translated to the concept-pattern with implicits in Scala. This is how far I got:
abstract class Expr[A] { // this corresponds to a type class
def eval(e:A): Int;
}
case class Lit(v: Int)
implicit object ExprLit extends Expr[Lit] {
def eval(e: Lit) = x.v;
}
case class Plus[A,B] (e1: A, e2: B) (implicit c1: Expr[A], c2: Expr[B])
Here I am stuck with implementing the implicit object for Plus. How do I declare an implicit object with type parameters and constraints?
I know that there are other solution for the expression problem in Scala, I am however interested in this version in particular.
Thank you all for reading my somewhat lengthy question.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这是 scala 中表达式问题的完整实现,使用类型类
Eval 类型类和隐式实现
让我们通过添加名为 Mult 的新类型来扩展解决方案
现在可以像这样评估表达式
让我们通过添加新的操作来扩展系统 打印
定义一个新的打印表达式的方法
更新打印表达式的 main 方法
可以通过将代码包装在对象内来执行整个解决方案。
Here is the complete implementation of Expression Problem in scala using Type classes
Eval type class and implicit implementations
Lets extend the solution by adding new Type called Mult
Now the expressions can be evaluated like this
Lets extend the system by adding a new Operation Print
Define a new method to print the Expressions
Update the main method to print the expression
Entire solution can be executed by wrapping the code inside an object.
第一次尝试(有缺陷):
编辑1:
上面的功能不够强大(您不能添加两个
Plus
表达式),并且隐式见证不需要在Plus< 内部定义/code> case class...试试这个:
编辑2:
这是一个(也许)稍微更惯用的版本:
First attempt (flawed):
Edit 1:
The above isn't sufficiently powerful (you can't add two
Plus
expressions), and the implicit witness need not be defined inside of thePlus
case class... try this instead:Edit 2:
Here's a (perhaps) slightly more idiomatic version: