带函数应用的类型化抽象语法树 [英] Typed abstract syntax tree with function application

查看:30
本文介绍了带函数应用的类型化抽象语法树的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试编写一个可以表示的类型化抽象语法树数据类型功能应用.

I am trying to write a typed abstract syntax tree datatype that can represent function application.

到目前为止我有

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"这样的东西——我是否错误地处理了这个问题?

I don't think there is a way in F# to write something like 'for all b' on that last line - am I approaching this problem wrongly?

推荐答案

一般来说,F# 类型系统的表达能力不足以(直接)定义类型化抽象语法树作为示例中的类型树.这可以使用 F# 不支持的 广义代数数据类型 (GADT) 来完成(尽管它们在 Haskell 和 OCaml 中可用).在 F# 中使用它会很好,但我认为它使语言更复杂一些.

In general, the F# type system is not expressive enough to (directly) define a typed abstract syntax tree as the one in your example. This can be done using generalized algebraic data types (GADTs) which are not supported in F# (although they are available in Haskell and OCaml). It would be nice to have this in F#, but I think it makes the language a bit more complex.

从技术上讲,编译器抱怨是因为类型变量 'b 没有定义.但是当然,如​​果你定义它,那么你会得到类型 Expr<'a, 'b> ,它有不同的含义.

Technically speaking, the compiler is complaining because the type variable 'b is not defined. But of course, if you define it, then you get type Expr<'a, 'b> which has a different meaning.

如果您想在 F# 中表达这一点,则必须使用基于接口的解决方法(接口可以具有通用方法,这为您提供了一种表达约束的方法,例如 exists 'b你需要在这里).这可能很快就会变得非常丑陋,所以我认为这不是一个好方法,但它看起来像这样:

If you wanted to express this in F#, you'd have to use a workaround based on interfaces (an interface can have generic method, which give you a way to express constraint like exists 'b which you need here). This will probably get very ugly very soon, so I do not think it is a good approach, but it would look something like this:

// 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的表达式树,你可以这样写:

To represent an expression tree of (fun (n:int) -> string n) 42, you could write something like:

let expr = 
  Application(fun appl -> 
    appl.Appl(Constant(fun (n:int) -> string n), 
              Constant(42)))

计算表达式的函数可以这样写:

A function to evaluate the expression can be written like this:

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 类型.你能不能多写一点关于你项目的总体目标(也许还有其他的好方法)?

As I said, this is a bit really extreme workaround, so I do not think it is a good idea to actually use it. I suppose the F# way of doing this would be to use untyped Expr type. Can you write a bit more about the overall goal of your project (perhaps there is another good approach)?

这篇关于带函数应用的类型化抽象语法树的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆