Skip to content

Instantly share code, notes, and snippets.

@Tarmil
Created May 1, 2016 07:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Tarmil/384ed6b6c4b1cfe096b00ced71c7f6b7 to your computer and use it in GitHub Desktop.
Save Tarmil/384ed6b6c4b1cfe096b00ced71c7f6b7 to your computer and use it in GitHub Desktop.
A WebSharper macro to reduce boilerplate for lenses on records
namespace TestLensMacro
open WebSharper
open WebSharper.JavaScript
open WebSharper.JQuery
open WebSharper.UI.Next
open WebSharper.UI.Next.Client
module Extensions =
module M = WebSharper.Core.Macros
module Q = WebSharper.Core.Quotations
module R = WebSharper.Core.Reflection
type RFT = Microsoft.FSharp.Reflection.FSharpType
type BF = System.Reflection.BindingFlags
type private LensFieldMacro() =
let lensMethod =
lazy
typedefof<IRef<_>>.Assembly
.GetType("WebSharper.UI.Next.IRefExtension")
.GetMethod("IRef`1.Lens")
|> R.Method.Parse
let concrete x args : Q.Concrete<_> = { Entity = x; Generics = args }
interface M.IMacro with
member this.Translate(q, tr) =
match q with
| Q.CallOrCallModule ({ Generics = [a; t] }, [iref; Q.Lambda(id, Q.FieldGetRecord(Q.Var(id'), p)) as get]) when id = id' ->
let set =
let va = Q.Id.Create "a" a
let vt = Q.Id.Create "t" t
let fields =
RFT.GetRecordFields(a.Load(), BF.Public ||| BF.NonPublic)
|> Array.map (fun f ->
if f.Name = p.Entity.Name then
Q.Var vt
else
let p = R.Property.Parse f
Q.FieldGetRecord(Q.Var va, concrete p []))
|> List.ofArray
Q.Lambda(va, Q.Lambda(vt, Q.NewRecord(a, fields)))
Q.Call(concrete lensMethod.Value [a; t], [iref; get; set]) |> tr
| _ -> failwith "Argument to LensField should be of the form (fun x -> x.RecordField)"
type UI.Next.IRef<'A> with
[<Macro(typeof<LensFieldMacro>)>]
member this.LensField(f: 'A -> 'T) = X<IRef<'T>>
type UI.Next.Var<'A> with
[<Macro(typeof<LensFieldMacro>)>]
member this.LensField(f: 'A -> 'T) = X<IRef<'T>>
/// Example use
[<JavaScript>]
module Client =
open Extensions
type Person = { Name : string; Age: int }
let rvPerson = Var.Create { Name = "John Doe"; Age = 20 }
let rvName = rvPerson.LensField (fun x -> x.Name)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment