Created
May 1, 2016 07:12
-
-
Save Tarmil/384ed6b6c4b1cfe096b00ced71c7f6b7 to your computer and use it in GitHub Desktop.
A WebSharper macro to reduce boilerplate for lenses on records
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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