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
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>) =
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With