我正在尝试编写一个可以表示函数应用程序的类型化抽象语法树数据类型。
到目前为止我有
type Expr<'a> =
| Constant of 'a
| Application of Expr<'b -> 'a> * Expr<'b> // error: The type parameter 'b' is not defined
我认为 F# 中没有办法在最后一行写出类似“for all b”的东西——我是不是错误地处理了这个问题?
我正在尝试编写一个可以表示函数应用程序的类型化抽象语法树数据类型。
到目前为止我有
type Expr<'a> =
| Constant of 'a
| Application of Expr<'b -> 'a> * Expr<'b> // error: The type parameter 'b' is not defined
我认为 F# 中没有办法在最后一行写出类似“for all b”的东西——我是不是错误地处理了这个问题?
通常,F# 类型系统的表达能力不足以(直接)将类型化抽象语法树定义为示例中的树。这可以使用F# 中不支持的广义代数数据类型 (GADT)来完成(尽管它们在 Haskell 和 OCaml 中可用)。在 F# 中使用它会很好,但我认为它会使语言更复杂一些。
从技术上讲,编译器抱怨是因为类型变量'b
没有定义。但是,当然,如果您定义它,那么您会得到Expr<'a, 'b>
具有不同含义的类型。
如果您想在 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)
要表示 的表达式树(fun (n:int) -> string n) 42
,您可以编写如下内容:
let expr =
Application(fun appl ->
appl.Appl(Constant(fun (n:int) -> string n),
Constant(42)))
评估表达式的函数可以这样编写:
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
正如我所说,这是一个非常极端的解决方法,所以我认为实际使用它不是一个好主意。我想 F# 这样做的方法是使用无类型的Expr
类型。你能多写一些关于你项目的总体目标的内容吗(也许还有另一种好方法)?