Typed abstract syntax tree with function application

后端 未结 1 1602
悲哀的现实
悲哀的现实 2021-01-03 02:07

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

So far I have

type Expr<\'a> =
    | Consta         


        
相关标签:
1条回答
  • 2021-01-03 02:42

    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)?

    0 讨论(0)
提交回复
热议问题