Created
December 11, 2012 18:40
-
-
Save mausch/4260932 to your computer and use it in GitHub Desktop.
Lenses as category in F#
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
#r @"bin\debug\FsControl.Core.dll" // from https://github.com/gmpl/FsControl | |
open System | |
open FsControl.Core.Abstractions | |
// generic composition operators for all categories | |
let inline (>>) g f = Inline.instance (Category.Comp, f) g | |
let inline (<<) f g = Inline.instance (Category.Comp, f) g | |
// Lens definition | |
type Lens<'a,'b> = Lens of ('a -> 'b * ('b -> 'a)) | |
// Lens operations | |
module Lens = | |
let inline set (Lens l) v a = snd (l a) <| v | |
let inline get (Lens l) a = fst (l a) | |
let inline update (Lens l) u a = | |
let v, setter = l a | |
setter (u v) | |
let inline ofGetSet get set = | |
let l a = get a, set a | |
Lens l | |
let id<'a> : Lens<'a,'a> = ofGetSet id (fun a _ -> a) | |
let compose l1 l2 = | |
let getter = get l1 >> get l2 | |
let setter v x = update l1 (set l2 x) v | |
ofGetSet getter setter | |
// Lenses form a category | |
type Lens<'a,'b> with | |
static member inline instance (Category.Comp, l1, _) = | |
fun l2 -> Lens.compose l2 l1 | |
static member inline instance (Category.Id, _:Lens<'a,'a>) = | |
fun () -> Lens.id : Lens<'a,'a> | |
// example | |
type Person = { | |
Name: string | |
DateOfBirth: DateTime | |
} with | |
static member name = | |
Lens.ofGetSet (fun (x: Person) -> x.Name) (fun x v -> { x with Name = v }) | |
type Book = { | |
Title: string | |
Author: Person | |
} with | |
static member author = | |
Lens.ofGetSet (fun (x: Book) -> x.Author) (fun x v -> { x with Author = v }) | |
// compose lenses | |
let bookAuthorName = Book.author >> Person.name | |
let rayuela = | |
{ Book.Title = "Rayuela" | |
Author = { Person.Name = "Julio Cortázar" | |
DateOfBirth = DateTime(1914, 8, 26) } } | |
let authorName = rayuela |> Lens.get bookAuthorName | |
printfn "%s" authorName // Julio Cortázar | |
// (>>) is truly overloaded, you can still use it to compose functions as usual, since functions are categories | |
let stringLength (x: string) = x.Length | |
let addTwo = (+) 2 | |
let getStringLengthPlusTwo = stringLength >> addTwo | |
printfn "%d" <| getStringLengthPlusTwo "hello" // 7 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Do you want to include it in FSharpPlus?
I think it will fit perfectly there.