问题
I am trying to write a typed abstract syntax tree datatype that can represent function application.
So far I have
type Expr<'a> =
| Constant of 'a
| Application of Expr<'b -> 'a> * Expr<'b> // error: The type parameter 'b' is not defined
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?
回答1:
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.
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.
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)
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
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)?
来源:https://stackoverflow.com/questions/12552759/typed-abstract-syntax-tree-with-function-application