Skip to content

Instantly share code, notes, and snippets.

@cameronpresley
Last active April 26, 2018 15:45
Embed
What would you like to do?
F# Lenses
module Optics
open Microsoft.FSharp.Reflection
open System.Reflection
let get<'b> name r : 'b option =
let fields = r.GetType()
|> FSharpType.GetRecordFields
|> Array.where(fun x -> x.Name = name)
|> Array.map(fun x -> x.GetValue(r))
match fields |> Array.length with
| 1 -> (fields |> Array.exactlyOne) :?> 'b |> Some
| _ -> None
let private removeUntilPeriod (n:string) : string =
if not (n.Contains(".")) then n
else n |> Seq.skipWhile(fun c -> c <> '.') |> Seq.skip 1 |> System.String.Concat
let rec set name (value:'a) (r:'b) : 'b =
let replacementStrategy (x:PropertyInfo) : obj =
if x.Name = name then value :> obj
elif not(name.StartsWith(x.Name)) then x.GetValue(r)
else
let name' = removeUntilPeriod name
let r' = x.GetValue(r)
set name' value r'
let values =
r.GetType()
|> FSharpType.GetRecordFields
|> Array.map replacementStrategy
FSharpValue.MakeRecord (r.GetType(), values) :?> 'b
// Learn more about F# at http://fsharp.org
open System
type Record = {x:int; y:int}
type Nested = {number:int; record:Record}
[<EntryPoint>]
let main argv =
let record = {x=10; y=20}
printfn "Creating a record with x=%i and y=%i" record.x record.y
record |> Optics.get "x" |> printfn "x is %A\n"
printfn "Updating record.x from 10 to 200."
printfn "Before: %A" record
record |> Optics.set "x" 200 |> printfn "After: %A\n"
printfn "Creating a nested record"
let n = {number=50; record=record}
printfn "%A\n" n
printfn "Updating record.x from 10 to 200"
n |> Optics.set "record.x" 200 |> printfn "%A"
0 // return an integer exit code
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment