F# 带函数应用程序的类型化抽象语法树
我正在尝试编写一个类型化的抽象语法树数据类型,它可以表示 函数应用 到目前为止我有F# 带函数应用程序的类型化抽象语法树,f#,dsl,abstract-syntax-tree,F#,Dsl,Abstract Syntax Tree,我正在尝试编写一个类型化的抽象语法树数据类型,它可以表示 函数应用 到目前为止我有 type Expr<'a> = | Constant of 'a | Application of Expr<'b -> 'a> * Expr<'b> // error: The type parameter 'b' is not defined type Expr*Expr一般来说,F#type系统的表达能力不足以(直接)将类型化抽象语法树定义为
type Expr<'a> =
| Constant of 'a
| Application of Expr<'b -> 'a> * Expr<'b> // error: The type parameter 'b' is not defined
type Expr*Expr一般来说,F#type系统的表达能力不足以(直接)将类型化抽象语法树定义为示例中的语法树。这可以使用F#中不支持的工具来完成(尽管Haskell和OCaml中提供了这些工具)。在F#中有这样的语言会很好,但我认为这会使语言更复杂一些
从技术上讲,编译器正在抱怨,因为没有定义类型变量'b
。当然,如果你定义它,那么你会得到一个不同含义的typeExpr
如果您想用F#来表达这一点,您必须使用基于接口的变通方法(接口可以有泛型方法,它为您提供了一种表达约束的方法,如exists'b
,您需要在此处使用该方法)。这可能很快就会变得非常丑陋,因此我认为这不是一个好的方法,但它看起来会像这样:
// Represents an application that returns 'a but consists
// of an argument 'b and a function 'b -> 'a
type IApplication<'a> =
abstract Appl<'b> : Expr<'b -> 'a> * Expr<'b> -> unit
and Expr<'a> =
// Constant just stores a value...
| Constant of 'a
// An application is something that we can call with an
// implementation (handler). The function then calls the
// 'Appl' method of the handler we provide. As this method
// is generic, it will be called with an appropriate type
// argument 'b that represents the type of the argument.
| Application of (IApplication<'a> -> unit)
let rec eval<'T> : Expr<'T> -> 'T = function
| Constant(v) -> v // Just return the constant
| Application(f) ->
// We use a bit of dirty mutable state (to keep types simpler for now)
let res = ref None
// Call the function with a 'handler' that evaluates function application
f { new IApplication<'T> with
member x.Appl<'A>(efunc : Expr<'A -> 'T>, earg : Expr<'A>) =
// Here we get function 'efunc' and argument 'earg'
// The type 'A is the type of the argument (which can be
// anything, depending on the created AST)
let f = eval<'A -> 'T> efunc
let a = eval<'A> earg
res := Some <| (f a) }
res.Value.Value
计算表达式的函数可以这样编写:
// Represents an application that returns 'a but consists
// of an argument 'b and a function 'b -> 'a
type IApplication<'a> =
abstract Appl<'b> : Expr<'b -> 'a> * Expr<'b> -> unit
and Expr<'a> =
// Constant just stores a value...
| Constant of 'a
// An application is something that we can call with an
// implementation (handler). The function then calls the
// 'Appl' method of the handler we provide. As this method
// is generic, it will be called with an appropriate type
// argument 'b that represents the type of the argument.
| Application of (IApplication<'a> -> unit)
let rec eval<'T> : Expr<'T> -> 'T = function
| Constant(v) -> v // Just return the constant
| Application(f) ->
// We use a bit of dirty mutable state (to keep types simpler for now)
let res = ref None
// Call the function with a 'handler' that evaluates function application
f { new IApplication<'T> with
member x.Appl<'A>(efunc : Expr<'A -> 'T>, earg : Expr<'A>) =
// Here we get function 'efunc' and argument 'earg'
// The type 'A is the type of the argument (which can be
// anything, depending on the created AST)
let f = eval<'A -> 'T> efunc
let a = eval<'A> earg
res := Some <| (f a) }
res.Value.Value
let rec eval->'T=函数
|常量(v)->v//只需返回常量即可
|申请(f)->
//我们使用了一点脏的可变状态(暂时让类型更简单)
设res=ref无
//使用评估函数应用程序的“处理程序”调用函数
f{新的IAApplication(efunc:Expr,earg:Expr-efunc)
让a=evalA清楚透彻地解释,谢谢!我会看看是否能清楚地解释我想要实现的目标,但同时感谢您保持类型选项的打开。@TimC很乐意提供帮助。我认为只要您需要模拟存在类型,这种方法就会行得通。如果您需要真正的GADT(如果案例类型因不同案例而不同,即Lambda
将属于某种类型Expr
),那么我认为您无法找到简单的解决方法。