Skip to content

Instantly share code, notes, and snippets.

@einblicker
Created June 14, 2012 04:51
Show Gist options
  • Save einblicker/2928049 to your computer and use it in GitHub Desktop.
Save einblicker/2928049 to your computer and use it in GitHub Desktop.
Expression Problem in F#
(*
type IFoo =
 abstract M1 : unit -> int
 abstract M2 : unit -> int
このインターフェースの一部だけを実装した抽象クラス
[<AbstractClass>]
type Bar() =
 interface IFoo with
  override x.M1() = 0
が作れないのでF#でthisをプロパティにするアプローチは微妙。
恐らく素直にhttp://www.daimi.au.dk/~madst/ecoop04/main.pdfの方法でやるべき。
ただしgenericsの共変性が使えないので少し不便かも。
*)
module Base =
type IBaseNode<'V, 'N when 'V :> IBaseVisitor<'V, 'N> and 'N :> IBaseNode<'V, 'N>> =
//abstract This : 'V //本当はこうしたい
abstract Accept : 'V -> unit
and IBaseVisitor<'V, 'N when 'V :> IBaseVisitor<'V, 'N> and 'N :> IBaseNode<'V, 'N>> =
abstract Visit : AddNode<'V, 'N> -> unit
abstract Visit : SubNode<'V, 'N> -> unit
abstract Visit : NumberNode<'V, 'N> -> unit
and [<AbstractClass>] AddNode<'V, 'N when 'V :> IBaseVisitor<'V, 'N> and 'N :> IBaseNode<'V, 'N>>(l : IBaseNode<'V, 'N>, r : IBaseNode<'V, 'N>) =
member this.Left = l
member this.Right = r
interface IBaseNode<'V, 'N> with
member this.Accept(visitor) = visitor.Visit(this)
and [<AbstractClass>] SubNode<'V, 'N when 'V :> IBaseVisitor<'V, 'N> and 'N :> IBaseNode<'V, 'N>>(l : IBaseNode<'V, 'N>, r : IBaseNode<'V, 'N>) =
member this.Left = l
member this.Right = r
interface IBaseNode<'V, 'N> with
member this.Accept(visitor) = visitor.Visit(this)
and [<AbstractClass>] NumberNode<'V, 'N when 'V :> IBaseVisitor<'V, 'N> and 'N :> IBaseNode<'V, 'N>>(value) =
member this.GetValue = value
interface IBaseNode<'V, 'N> with
member this.Accept(visitor) = visitor.Visit(this)
type Node =
inherit IBaseNode<Visitor, Node>
and Visitor =
inherit IBaseVisitor<Visitor, Node>
type Add(l, r) =
inherit AddNode<Visitor, Node>(l, r)
interface Node
type Sub(l, r) =
inherit SubNode<Visitor, Node>(l, r)
interface Node
type Num(value) =
inherit NumberNode<Visitor, Node>(value)
interface Node
//EvalをExtで再利用するために型パラメータを付けるべき
//F#だとinterfaceの一部だけを実装した抽象クラスが作れないので無理かも
type Eval() =
let mutable value = 0
member this.Result = value
interface Visitor with
override this.Visit(node : AddNode<Visitor, Node>) =
node.Left.Accept(this)
let val0 = value
node.Right.Accept(this)
let val1 = value
value <- val0 + val1
override this.Visit(node : SubNode<Visitor, Node>) =
node.Left.Accept(this)
let val0 = value
node.Right.Accept(this)
let val1 = value
value <- val0 - val1
override this.Visit(node : NumberNode<Visitor, Node>) =
value <- node.GetValue
module Ext =
open Base
type IExtVisitor<'V, 'N when 'V :> IExtVisitor<'V, 'N> and 'N :> IBaseNode<'V, 'N>> =
inherit IBaseVisitor<'V, 'N>
abstract Visit : MulNode<'V, 'N> -> unit
and [<AbstractClass>] MulNode<'V, 'N when 'V :> IExtVisitor<'V, 'N> and 'N :> IBaseNode<'V, 'N>>(l : IBaseNode<'V, 'N>, r : IBaseNode<'V, 'N>) =
member this.Left = l
member this.Right = r
interface IBaseNode<'V, 'N> with
member this.Accept(visitor) = visitor.Visit(this)
type Node =
inherit IBaseNode<Visitor, Node>
and Visitor =
inherit IExtVisitor<Visitor, Node>
type Add(l, r) =
inherit AddNode<Visitor, Node>(l, r)
interface Node
type Sub(l, r) =
inherit SubNode<Visitor, Node>(l, r)
interface Node
type Num(value) =
inherit NumberNode<Visitor, Node>(value)
interface Node
type Mul(l, r) =
inherit MulNode<Visitor, Node>(l, r)
interface Node
type Show() =
let mutable str = ""
member this.Result = str
interface Visitor with
override this.Visit(node : AddNode<Visitor, Node>) =
node.Left.Accept(this)
let val0 = str
node.Right.Accept(this)
let val1 = str
str <- "(" + val0 + " + " + val1 + ")"
override this.Visit(node : SubNode<Visitor, Node>) =
node.Left.Accept(this)
let val0 = str
node.Right.Accept(this)
let val1 = str
str <- "(" + val0 + " - " + val1 + ")"
override this.Visit(node : NumberNode<Visitor, Node>) =
str <- string(node.GetValue)
override this.Visit(node : MulNode<Visitor, Node>) =
node.Left.Accept(this)
let val0 = str
node.Right.Accept(this)
let val1 = str
str <- "(" + val0 + " * " + val1 + ")"
open Base
let test1 () =
let ast = Sub(Add(Num(1), Num(2)), Add(Num(3), Num(4))) :> Node
let eval = Eval()
ast.Accept(eval)
printfn "%A" eval.Result
test1()
open Ext
let test2 () =
let ast = Sub(Add(Num(1), Num(2)), Mul(Num(3), Num(4))) :> Node
let show = Show()
ast.Accept(show)
printfn "%A" show.Result
test2()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment