Skip to content

Instantly share code, notes, and snippets.

@mausch
Created December 11, 2012 18:40
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mausch/4260932 to your computer and use it in GitHub Desktop.
Save mausch/4260932 to your computer and use it in GitHub Desktop.
Lenses as category in F#
#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
@gusty
Copy link

gusty commented Nov 1, 2013

Do you want to include it in FSharpPlus?
I think it will fit perfectly there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment