views:

181

answers:

2

Is there any way in F# how to get a name of a variable passed into a function?

Example:

let velocity = 5
let fn v = v.ParentName
let name = fn velocity // this would return "velocity" as a string

Thank you in advance

EDIT:

Why this code does not work? It is matched as value, so I can not retrieve the "variable" name.

type Test() =
  let getName (e:Quotations.Expr) =
    match e with
      | Quotations.Patterns.PropertyGet (_, pi, _) -> pi.Name + " property"
      | Quotations.Patterns.Value(a) -> failwith "Value matched"
      | _ -> failwith "other matched"
  member x.plot v = v |> getName |> printfn "%s"

let o = new Test()

let display () =
  let variable = 5.
  o.plot <@ variable @>

let runTheCode fn = fn()

runTheCode display
+1  A: 

You might be able to achieve this with code quotations:

let name = fn <@ velocity @>

The fn function will be passed an Expr object, which it must cast to Quotations.Var (which it will only be if you pass a single variable) and extract the Name instance member.

Marcelo Cantos
Hi, thanks for your answer. How do I cast it?this:let velExpr = <@ velocity @>let casted = velExpr :?> Quotations.Vardoes not work
Oldrich Svec
+8  A: 

For completing Marcelo's answer, yes you can use quotations for this task:

open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns

let velocity = 5

let fn (e:Expr) =
  match e with
    | PropertyGet (e, pi, li) -> pi.Name
    | _ -> failwith "not a let-bound value"

let name = fn <@velocity@> 

printfn "%s" name

As you can see in the code, F# let-bound top definition values (functions or variables) are implemented as properties of a class.

I can't find anymore the link that shows how a piece of F# code could be rewritten in a functional way with C#. Seeing the code, it becomes obvious why you need a PropertyGet pattern.

Now if you want to evaluate the expression too, you will need to install F# powerpack and reference FSharp.PowerPack.Linq in your project.

It adds an EvalUntyped method on Expr class..

open Microsoft.FSharp.Linq.QuotationEvaluation

let velocity = 5

let fn (e:Expr) =
  match e with
    | PropertyGet (eo, pi, li) -> pi.Name, e.EvalUntyped
    | _ -> failwith "not a let-bound value"

let name, value = fn <@velocity@> 

printfn "%s %A" name value

If you need to do it for the method of an instance, here's how I would do it:

let velocity = 5

type Foo () =
  member this.Bar (x:int) (y:single) = x * x + int y

let extractCallExprBody expr =
  let rec aux (l, uexpr) =
    match uexpr with
     | Lambda (var, body) -> aux (var::l, body)
     | _ -> uexpr
  aux ([], expr)

let rec fn (e:Expr) =
  match e with
    | PropertyGet (e, pi, li) -> pi.Name
    | Call (e, mi, li) -> mi.Name
    | x -> extractCallExprBody x |> fn
    | _ -> failwith "not a valid pattern"

let name = fn <@velocity@> 
printfn "%s" name

let foo = new Foo()

let methodName = fn <@foo.Bar@>
printfn "%s" methodName

Just to come back on the code snippet showing usage of EvalUntyped, you can add an explicit type parameter for Expr and a downcast (:?>) if you want/need to keep things type-safe:

let fn (e:Expr<´T>) = //using ´ instead of ' to avoid colorization screw-up
  match e with
    | PropertyGet (eo, pi, li) -> pi.Name, (e.EvalUntyped() :?> ´T)
    | _ -> failwith "not a let-bound value"

let name, value = fn <@velocity@> //value has type int here
printfn "%s %d" name value
Stringer Bell
Hi, thank you a lot! One additional question. How do I get a value out of the expression?
Oldrich Svec
Thanks! And a final question :) If the fn is a member of a type (member this.fn = ...), how do I do it?
Oldrich Svec
Just to be curious, why do you need to retrieve the name of a value/member?
Stringer Bell
Well, I have created a type Matlab which communicates with Matlab :). And if you want to plot a variable, you call "member this.plot variables" and then I need to retrieve names of the variables and send them to Matlab. So I need to retrieve names of parameters of the member functions like plot etc.
Oldrich Svec
Hi,I have modified my first post, could you please take a look at it? Is it the correct way, how to post additional questions?
Oldrich Svec
Hi, I'm sorry I don't think this is doable. You can get the name of a value using quotations but only for top-level definition values. So you will have to take "let variable = 5." out of the display function and put it at the top-level.Yes, if you have additional questions you will have to edit your Q. You did right. But don't hesitate to ask a fresh new question.
Stringer Bell