13

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)

次に、乗算を追加する 1 つのデータ拡張があります。

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)

運用拡張機能として pretty-printer を考えてみましょう。

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) ++ ")"

最後に、4 番目のモジュールでは、2 つの独立した拡張機能を組み合わせることができます。

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 の式の問題には他の解決策があることは知っていますが、特にこのバージョンに興味があります。

私のやや長い質問を読んでくれてありがとう。

4

2 に答える 2

15

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)
}
于 2010-09-08T21:44:58.310 に答える