Skip to content

Instantly share code, notes, and snippets.

@Eluss
Created June 27, 2019 22:38
Show Gist options
  • Save Eluss/31bd886cda4efea404d29e4dc242372a to your computer and use it in GitHub Desktop.
Save Eluss/31bd886cda4efea404d29e4dc242372a to your computer and use it in GitHub Desktop.

Revisions

  1. Eluss created this gist Jun 27, 2019.
    173 changes: 173 additions & 0 deletions lenses.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,173 @@
    struct Dog {
    var name: String
    }

    struct Person {
    var name: String
    var age: Int
    var dog: Dog
    }

    var dog = Dog(name: "Bark")
    var person = Person(name: "Foo", age: 12, dog: dog)

    struct Lens<Whole, Part> {
    let get: (Whole) -> Part
    let set: (Part) -> (Whole) -> Whole
    }

    extension Person {

    enum lens {
    static let dog: Lens<Person, Dog> = Lens(
    get: { person in return person.dog },
    set: { newDog in
    return { person in
    var newPerson = person
    newPerson.dog = newDog
    return newPerson
    }

    })

    static let age: Lens<Person, Int> = Lens(
    get: { person in return person.age},
    set: { newAge in
    return { person in
    var newPerson = person
    newPerson.age = newAge
    return newPerson
    }

    })

    static let name: Lens<Person, String> = Lens.init(
    get: { whole in return whole.name },
    set: { newPart in {
    oldWhole in
    var whole = oldWhole
    whole.name = newPart
    return whole
    }})


    }
    }

    extension Dog {
    enum lens {
    static let name: Lens<Dog, String> = Lens.init(
    get: { whole in return whole.name },
    set: { newPart in {
    oldWhole in
    var whole = oldWhole
    whole.name = newPart
    return whole
    }})

    }
    }

    //print(Person.lens.dog.get(person))
    //let newDog = Dog(name: "Reksio")
    //let function: (Person) -> Person = Person.lens.dog.set(newDog)
    //print(function(person))

    ///
    ///
    ///

    precedencegroup PipelineOperator {
    associativity: left
    }
    infix operator |> : PipelineOperator

    func |> (lhs: Person, rhs: (Person) -> Person) -> Person {
    return rhs(lhs)
    }

    let newPerson = person
    |> Person.lens.name.set("Astek")
    |> Person.lens.age.set(30)
    |> Person.lens.dog.set(Dog(name: "Kik"))

    //print(newPerson)

    ///

    precedencegroup SetOperator {
    associativity: none
    higherThan: PipelineOperator
    }
    infix operator .~ : SetOperator
    infix operator %~ : SetOperator

    extension Lens {
    static func .~ (lhs: Lens<Whole, Part>, rhs: Part) -> (Whole) -> Whole {
    return { whole in
    return lhs.set(rhs)(whole)
    }

    }

    static func %~ (lhs: Lens<Whole, Part>, rhs: @escaping (Part) -> Part) -> (Whole) -> Whole {
    return { whole in
    let part = lhs.get(whole)
    let newPart = rhs(part)
    let newWhole = lhs.set(newPart)(whole)
    return newWhole
    }
    }
    }


    let newPerson2 = person
    |> Person.lens.name .~ "Eliasz"
    |> Person.lens.age %~ { age in return age * 2}
    |> Person.lens.dog .~ Dog(name: "Olo")

    print(newPerson2)

    //Lens<A, B> + Lens<B, C> = Lens<A, C>
    precedencegroup ChainOperator {
    associativity: left
    higherThan: SetOperator
    }
    infix operator .. : ChainOperator

    //Whole
    // Part
    // Subpart
    extension Lens {
    static func .. <Subpart>(lhs: Lens<Whole, Part>, rhs: Lens<Part, Subpart>) -> Lens<Whole, Subpart> {
    return Lens<Whole, Subpart>.init(
    get: { whole in
    let part = lhs.get(whole)
    let subpart = rhs.get(part)
    return subpart
    },
    set: { newSubpart in
    return { whole in
    let part = lhs.get(whole)
    let newPart = rhs.set(newSubpart)(part)
    let newWhole = lhs.set(newPart)(whole)
    return newWhole
    }
    })
    }
    }

    let dogsName: Lens<Person, String> = Person.lens.dog..Dog.lens.name

    extension Person {
    enum deepLens {
    static let dogsName = Person.lens.dog..Dog.lens.name
    }
    }

    let newPerson3 = person
    |> Person.lens.name .~ "Test"
    |> Person.lens.age %~ { age in return age * 2}
    |> Person.deepLens.dogsName .~ "XX"

    print(newPerson3)