在Scala中输入类 [英] Type classes in Scala

查看:136
本文介绍了在Scala中输入类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



我遇到了一些问题,试图将一个小的,可扩展的表达式语言从Haskell转换成Scala 。编写可与新数据变体和操作一起扩展的数据类型的基本问题通常称为表达式问题



我在Haskell中的原始解决方案使用带约束的类型和实例声明。我的表达式的基础定义如下:

  module Expr where 

class Expr e其中
eval :: e - >整数

数据点亮=点亮整数
实例显示其中
eval(Lit l)= l

data加上ab =(Expr a,Expr b)=>加上b
实例(Expr a,Expr b)=> Expr(Plus ab)其中
eval(Plus xy)=(eval x)+(eval y)

然后,我有一个数据扩展添加乘法:

  module ExprWithMul其中
import Expr

data Mul ab =(Expr a,Expr b)=> Mul a b
instance(Expr a,Expr b)=> Expr(Mul ab)其中
eval(Mul xy)=(eval x)*(eval y)


$ b $

  module FormatExpr where 
import Expr

class(Expr t)=> FormatExpr t其中
format :: t - >字符串

实例FormatExpr Lit其中
格式(Lit l)= show l

实例(FormatExpr a,FormatExpr b)=> FormatExpr(Plus ab)其中
格式(Plus xy)=(++(format x)+++++(format y)++)

最后,在第四个模块中,可以组合两个独立的扩展:

<$模块FormatExprWithMult其中
import FormatExpr
import ExprWithMul

实例(FormatExpr a,FormatExpr b)=> FormatExpr(Mul ab)其中
格式(Mul xy)=(++(format x)++*++(format y)++)

 抽象类Expr [A] {//这对应于一个类型类
def eval(e:A):Int;


case class Lit(v:Int)
隐式对象ExprLit extends Expr [Lit] {
def eval(e:Lit)= x.v;

case class Plus [A,B](e1:A,e2:B)(隐式c1:Expr [A],c2:Expr [B])

这里我坚持实现Plus的隐式对象。我如何声明带有类型参数和约束的隐式对象?



我知道在Scala中有表达式问题还有其他解决方案,但我对此版本感兴趣特别是。

谢谢大家阅读我有点冗长的问题。

解决方案

< h3>第一次尝试(有缺陷):

  case class Plus [A,B](e1:A,e2:B)(隐式c1:Expr [A],c2:Expr [B]){
隐式对象ExprPlus扩展Expr [Plus [A,B]] {
def eval(p:Plus [A,B]) = c1.eval(p.e1)+ c2.eval(p.e2)
}
}



编辑1:



以上功能不够强大(不能添加两个 Plus 表达式),隐式见证不需要在 Plus case类中定义......试试这个:

  case class Plus [A,B](e1:A,e2:B)(隐式val c1:Expr [A],c2:Expr [B])
含义它确定ExprPlus [A,B](隐含的c1:Expr [A],c2:Expr [B])=
新的Expr [Plus [A,B]] {
def eval(p:Plus [A,B])= c1.eval(p.e1)+ c2.eval(p.e2)
}



编辑2:



以下是一个稍微更习惯的版本:

  case class Plus [A:Expr,B:Expr](e1:A,e2:B)
隐式def ExprPlus [A:Expr,B:Expr] = new Expr [Plus [A,B]] {
def eval(p:Plus [A,B])=隐含[Expr [A]]。eval(p.e1)+
隐含[Expr [B ]]。eval(p.e2)
}


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.

解决方案

First attempt (flawed):

case class Plus[A,B] (e1: A, e2: B) (implicit c1: Expr[A], c2: Expr[B]) {
    implicit object ExprPlus extends Expr[Plus[A, B]] { 
        def eval(p:Plus[A, B]) = c1.eval(p.e1) + c2.eval(p.e2)
    }
}

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 the Plus case class... try this instead:

case class Plus[A,B] (e1: A, e2: B) (implicit val c1: Expr[A], c2: Expr[B])
implicit def ExprPlus[A, B](implicit c1: Expr[A], c2: Expr[B]) = 
    new Expr[Plus[A, B]] { 
        def eval(p:Plus[A, B]) = c1.eval(p.e1) + c2.eval(p.e2)
    }

Edit 2:

Here's a (perhaps) slightly more idiomatic version:

case class Plus[A: Expr, B: Expr] (e1: A, e2: B)
implicit def ExprPlus[A: Expr, B: Expr] = new Expr[Plus[A, B]] {
    def eval(p:Plus[A, B]) = implicitly[Expr[A]].eval(p.e1) + 
                             implicitly[Expr[B]].eval(p.e2)
}

这篇关于在Scala中输入类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆