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
。当然,如果你定义它,那么你会得到一个不同含义的type
Expr

如果您想用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
),那么我认为您无法找到简单的解决方法。