hubFS: THE place for F#

. . . are you on The Hub?
Welcome to hubFS: THE place for F# Sign in | Join | Help
in Search

Record field names as function parameters

Last post 05-26-2008, 17:00 by tomasp. 3 replies.
Sort Posts: Previous Next
  •  05-22-2008, 18:30 5986

    Record field names as function parameters

    Hello all,

    I am evaluating F# as a target language for a domain specific language compiler. One feature that would be really useful for me is the possibility to pass a record together with a name for a record field to a function. The function could then for example return the value associated with the given field in the record. In pseudo-code:

    fun f record fieldname = record.fieldname;

    type AB = {a: bool;  b: bool;};
    let abtrue = {a=true;b=true};
    let selection = f abtrue a;

    When executing this I would hope get selection=true.

    Of course, this is not valid F# code because the compiler does not know the type of "record" in the definition of the function f. So my questions:

    1) is there any way of annotating the type of "record" and "fieldname" to get what I want (e.g. using some speciel "field" type)?

    2) if not, is reflection the best alternative? This would be a bit annoying since I then have to rely on run-time checking for something which, in principal, could be checked statically.

    Any suggestions would be greatly appreciated.

    Best,
    Michael.

  •  05-23-2008, 3:40 5990 in reply to 5986

    Re: Record field names as function parameters

    Hi,
    reflection is probably the best alternative (meaning that it will internaly use .NET reflection), but you can simplify the code by using F# reflection library and also use it together with F# quotations to get a surprising degree of compile-time checking.


    #light

    open System
    open Microsoft.FSharp.Quotations
    open Microsoft.FSharp.Quotations.Typed
    open Microsoft.FSharp.Quotations.Raw

    type SampleRec = {
      Str : string
      Num : int }
     
    // This function takes a quotation with a "hole" (which is represented as a function)
    // The type 'a is a type of the record and the type 'b is a type of the field, so we can
    // use this type information and return a function 'a -> 'b which takes a record value
    // and reads the field (you can of course remove the "unbox" function and it would
    // return just 'obj' type, which may be sometimes more useful)
    let getValueReader (prop:Expr<'a> -> Expr<'b>) =
      let rcTy = typeof<'a>
      // fill in the hole with some dummy quotation - we just want to get
      // the representation of quotation as "Expr" and not as a function
      let expr = prop (Typed.of_raw (MkHole rcTy))
      // Analyze the qutotation...
      match expr.Raw with
      | RecdGet (ty, nm, expr) ->
          // It represents an access to a record value, so we can use F# Reflection
          // to get a function "rdr" that reads a specified field of the record
          let rdr = Reflection.Value.GetRecordFieldReader (ty, nm)
          ((box >> rdr >> unbox) : 'a -> 'b)
      | _ ->
          // Not an access to a record field - fail
          failwith "Invalid expression - not reading record field!"
     
    // Create a 'reader' from a quotation
    // Note that these two are typed (SampleRec -> string / int)
    let rdS = getValueReader <@ (_ : SampleRec).Str @>  
    let rdN = getValueReader <@ (_ : SampleRec).Num @>     

    let rc = { Str = "Hello world!"; Num = 42 }
    let v1 = rdS rc
    let v2 = rdN rc

    printfn "Extracted: %s, %d" v1 v2
    Console.ReadLine() |> ignore

    BTW: This is a really interesting question, so thanks for it! I'm really interested in hearing more about your problem and whether this solution will be helpful for you.

    T.


    Tomas Petricek (Blog), C# MVP
    My book: Real-world Functional Programming in .NET
  •  05-25-2008, 23:07 6009 in reply to 5990

    Re: Record field names as function parameters

    Hi Tomas,

    Thanks a lot for your detailed solution and sorry for my late reply, it is great to learn some of these cool F# features.

    Thanks also for your interest. My idea was to translate a program in my domain specific language (for modelling biochemical systems) into an F# program which, when executed, would produce some target mathematical model (e.g. differential equations or Petri nets). My hope was that the DSL program would be well-typed iff the target F# program was well-typed.

    While my DSL shares some features with F# that could be elegantly addressed by such a translation, there are also some features which do not map neatly into F# (e.g. mechanisms for returning stuff from modules), and the requirement in F# to declare records and do so in a "non-compositional" manner also posed difficulties.

    On a more practical level, I wonder for future reference if there is an easy way to programatically control the F# type checker and compiler. This would be necessary in order to translate F# type errors back into error messages appropriate for the DSL.

    In any case, I have decided that the safest and most robust approach is to write a translator for my DSL in the traditional way. Sigh.

    Best regards,
    Michael.

  •  05-26-2008, 17:00 6014 in reply to 6009

    Re: Record field names as function parameters

    Hi Michael,
    that looks like an interesting project. Having some way for adding additioal checking to custom DSLs in F# (e.g. by extending the compiler) seems like very useful idea to me and I encountered situation where this would be useful too.

    Another possibility is using F# quotations - this lets you express the DSL in F# language (with te usual type cheking and usual function composiion etc.), while you can add some additional checking when processing the quotation. For example you could write:

    let expr = <@ for_all numbers (fun x -> x > 0) (* .. any F# code here .. *) @>
    verifyExpr expr
    runExpr expr

    The verifyExpr function could then analyze the quotation (some sort of F# AST) and ensure that all your additional rules hold. runExpr would then execute the DSL in some way (unfortunatelly it is not yet possible to compile it back to executable code and just run it). But I agree that having some mechanism for checking DLSs directly in F# would make things a lot easier. Using quotations for this may not be completely appropriate.

    BTW: You can find some quotation samples here: http://tomasp.net/blog/fsharp-quotation-samples.aspx

     


    Tomas Petricek (Blog), C# MVP
    My book: Real-world Functional Programming in .NET
View as RSS news feed in XML
Powered by Community Server, by Telligent Systems