Skip to content

Instantly share code, notes, and snippets.

@gatlin
Last active June 5, 2025 19:20
Show Gist options
  • Select an option

  • Save gatlin/264d49cf322aef31beccf680093bef38 to your computer and use it in GitHub Desktop.

Select an option

Save gatlin/264d49cf322aef31beccf680093bef38 to your computer and use it in GitHub Desktop.

Revisions

  1. gatlin revised this gist Jun 30, 2016. 1 changed file with 0 additions and 169 deletions.
    169 changes: 0 additions & 169 deletions 2-lenses.js
    Original file line number Diff line number Diff line change
    @@ -1,59 +1,4 @@
    /*
    * (!) You can go to https://repl.it/languages/javascript, paste all this code
    * in there, and try it out for yourself!
    */

    'use strict';
    /*
    *
    * What is a lens?
    *
    * A lens in programming, much like a lens in the real world, allows you to
    * focus in on a small part of a larger whole and then do something with or to
    * that part.
    *
    * You may also think of it as breaking a piece out, manipulating the piece,
    * and then reconstructing the whole.
    *
    * That "something" can be changing the value or merely passing the value
    * elsewhere to be read.
    *
    * Thus, lenses are both getters *and* setters. Here's an example of the code
    * we'll be implementing:
    *
    * let foo = {
    * bar: 5,
    * baz: [ 'quux', 'axolotl' ]
    * };
    *
    * let barL = lens('bar');
    * let bazL = lens('baz');
    * let bazAt = (index) => bazL.compose(lens(index));
    *
    * let axolotl = get(bazAt(1))(foo); // 'axolotl'
    * set(barL)(7)(foo);
    * set(bazAt(1))('potrzebie')(foo);
    *
    * console.log(foo); // { bar: 7, baz: ['quux','potrzebie'] }
    *
    * Some key things to note:
    *
    * - Lenses are composable; and
    * - Lenses can be used to both set *and* get parts of an object.
    *
    * More technically, a lens is a function of three arguments:
    *
    * 1. Some structure (eg an Object, an Array, etc);
    * 2. An index specifying some piece of the structure; and
    * 3. A function which transforms that piece
    *
    * The lens then returns a new structure, with its transformed piece inside.
    *
    * First, let's define some things we know we'll need, to wit:
    *
    * - A way to replace part of a structure at a certain key with a new value;
    * - A way to compose functions.
    */

    if(!Function.prototype.compose) {
    Function.prototype.compose = function(g) {
    @@ -68,26 +13,6 @@ let replace = function(key, value, thing) {
    thing[key] = value;
    return thing;
    };

    /*
    * Now, on to lenses. A lens breaks a part from a whole, passes the part to a
    * function, and puts the result back into the whole. Intuitively, then,
    * whether or not a lens is used as a setter or a getter rests on that function
    * we pass in.
    *
    * This function we pass to the lens must return something we can put back in
    * the whole -- or at the very least, lead us to something which fits the bill.
    *
    * My strategy will be to write two functions which **both** take a value and
    * return an object with two properties:
    *
    * - a reference to the wrapped-up value; and
    * - a function to let us transform that value.
    *
    * Some of you may have already guessed it, but I'm basically defining two
    * functors:
    */

    let Setter = (x) => Object.seal({
    value: x,
    map: (f) => Setter(f(x))
    @@ -100,105 +25,11 @@ let Getter = (x) => Object.seal({

    let extract = (s) => s.value;

    let setTest = Setter(5);
    console.log(extract(setTest.map((x) => x*2))); // 10

    let getTest = Getter(5);
    console.log(extract(getTest.map((x) => x*2))); // 5

    /*
    * The key here is uniformity: both Setter and Getter wrap up a value and allow
    * me to at least pretend I'm transforming the value inside. Setter lets me do
    * it; Getter doesn't. Simple.
    *
    * The uniform way to transform the value inside is the `map()` method.
    *
    * Armed with these two functions, we can finally write the lens function:
    */

    let lens = (key) => (fn) => (s) => fn(s[key]).map((v) => replace(key, v, s));

    /*
    * Note that I'm writing this function in *curried* style. This is mostly for
    * stylistic reasons but it does make the code more readable and usable later
    * on. You'll see.
    *
    * So now we can create lenses!
    */

    let myself = {
    name: 'gatlin',
    age: 27,
    dogs: [{
    name: 'louie', breed: 'pug'
    }, {
    name: 'pugzy', breed: 'pug'
    }]
    };

    let nameL = lens('name'); // <-- this is why we curried the function
    let ageL = lens('age');
    let dogsL = lens('dogs');
    let dogAt = (idx) => dogsL.compose(lens(idx));

    let rename = (thing, newName) => set(nameL)(newName)(thing);

    /*
    * So how do we *use* these fancy new lenses? Lets first tackle reading, or
    * "getting", a value from a structure using a lens:
    */

    let get = (l) => (s) => extract.compose(l(Getter))(s);

    /*
    * This is a function which takes a lens and a structure. The `l` variable is a
    * lens, which means that it is going to need a transformation function and
    * then a value. so `l(Getter)` returns a function that needs a structure given
    * to it (which would be `s`). However, ultimately we don't want a `Getter`
    * object, we want the value inside it. So we compose `extract` with all this
    * to retrieve the final value.
    *
    * As for setting, that's a special case of a more general operation:
    * transforming a part of a whole. Replacement is one kind of transformation.
    * For historical reasons let's define this more general kind of mutation as
    * `over`:
    */

    let over = (l) => (f) => (s) => extract.compose(l(Setter.compose(f)))(s);

    /*
    * Let's unpack this. `over` requires a lens, a transformation function, and a
    * structure. We compose `Setter` and our transformation function, creating a
    * `Setter` containing the new value we want to put back in the whole. Then we
    * pass this resulting function to our lens `l`, creating a new function asking
    * for a structure. And again, as with `Getter`, we ultimately want the object
    * back, not the `Setter` object, so we compose `extract` with all this.
    *
    * Pause and re-read this a few times if you need to; I'll wait.
    *
    * So actually *setting* a value is a special case of `over` where our
    * transformation function takes the value we want to set, the original value,
    * and returns the value we want to set. This function is called `constant`:
    */

    let constant = (x) => (y) => x;
    let set = (l) => (v) => (s) => over(l)(constant(v))(s);

    /*
    * Now let's try out our lenses!
    */

    console.log(myself);
    rename(myself, 'Gatlin');
    console.log(get(nameL)(myself));

    // Lenses compose!
    set(
    dogAt(0).compose(nameL)
    )('Louie')(myself);

    // But we can also retrieve a part and use existing lens-based functions on them!
    let pugzy = get(dogAt(1))(myself);
    rename(pugzy,'Pugzy');

    console.log(myself);
  2. gatlin revised this gist Jun 30, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion 1-js-lenses.md
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    What is a lens?
    ---
    ===

    *Note: you can copy the file `2-lenses.js` below over to [repl.it][1] and
    play along from home!*
  3. gatlin revised this gist Jun 30, 2016. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions 1-js-lenses.md
    Original file line number Diff line number Diff line change
    @@ -1,8 +1,8 @@
    What is a lens?
    ---

    *Note: you can copy the file `2-lenses.js` below this over to [repl.it][1]
    and play along from home!*
    *Note: you can copy the file `2-lenses.js` below over to [repl.it][1] and
    play along from home!*

    [1]: https://repl.it/languages/javascript

  4. gatlin revised this gist Jun 30, 2016. 2 changed files with 212 additions and 0 deletions.
    212 changes: 212 additions & 0 deletions 1-js-lenses.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,212 @@
    What is a lens?
    ---

    *Note: you can copy the file `2-lenses.js` below this over to [repl.it][1]
    and play along from home!*

    [1]: https://repl.it/languages/javascript

    A lens in programming, much like a lens in the real world, allows you to
    focus in on a small part of a larger whole and then do something with or to
    that part.

    You may also think of it as breaking a piece out, manipulating the piece,
    and then reconstructing the whole.

    That "something" can be changing the value or merely passing the value
    elsewhere to be read.

    Thus, lenses are both getters *and* setters. Here's an example of the code
    we'll be implementing:

    ```javascript
    let foo = {
    bar: 5,
    baz: [ 'quux', 'axolotl' ]
    };

    let barL = lens('bar');
    let bazL = lens('baz');
    let bazAt = (index) => bazL.compose(lens(index));

    let axolotl = get(bazAt(1))(foo); // 'axolotl'
    set(barL)(7)(foo);
    set(bazAt(1))('potrzebie')(foo);

    console.log(foo); // { bar: 7, baz: ['quux','potrzebie'] }
    ```

    Some key things to note:

    - Lenses are composable; and
    - Lenses can be used to both set *and* get parts of an object.

    More technically, a lens is a function of three arguments:

    1. Some structure (eg an Object, an Array, etc);
    2. An index specifying some piece of the structure; and
    3. A function which transforms that piece

    The lens then returns a new structure, with its transformed piece inside.

    First, let's define some things we know we'll need, to wit:

    - A way to replace part of a structure at a certain key with a new value;
    - A way to compose functions.

    ```javascript
    if(!Function.prototype.compose) {
    Function.prototype.compose = function(g) {
    let f = this;
    return function(x) {
    return f(g(x));
    };
    };
    }

    let replace = function(key, value, thing) {
    thing[key] = value;
    return thing;
    };
    ```

    Now, on to lenses. A lens breaks a part from a whole, passes the part to a
    function, and puts the result back into the whole. Intuitively, then,
    whether or not a lens is used as a setter or a getter rests on that function
    we pass in.

    This function we pass to the lens must return something we can put back in
    the whole -- or at the very least, lead us to something which fits the bill.

    My strategy will be to write two functions which **both** take a value and
    return an object with two properties:

    - a reference to the wrapped-up value; and
    - a function to let us transform that value.

    Some of you may have already guessed it, but I'm basically defining two
    functors:

    ```javascript
    let Setter = (x) => Object.seal({
    value: x,
    map: (f) => Setter(f(x))
    });

    let Getter = (x) => Object.seal({
    value: x,
    map: (f) => Getter(x)
    });

    let extract = (s) => s.value;

    let setTest = Setter(5);
    console.log(extract(setTest.map((x) => x*2))); // 10

    let getTest = Getter(5);
    console.log(extract(getTest.map((x) => x*2))); // 5
    ```

    The key here is uniformity: both Setter and Getter wrap up a value and allow
    me to at least pretend I'm transforming the value inside. Setter lets me do
    it; Getter doesn't. Simple.

    The uniform way to transform the value inside is the `map()` method.

    Armed with these two functions, we can finally write the lens function:


    ```javascript
    let lens = (key) => (fn) => (s) => fn(s[key]).map((v) => replace(key, v, s));
    ```

    Note that I'm writing this function in *curried* style. This is mostly for
    stylistic reasons but it does make the code more readable and usable later
    on. You'll see.

    So now we can create lenses!

    ```javascript
    let myself = {
    name: 'gatlin',
    age: 27,
    dogs: [{
    name: 'louie', breed: 'pug'
    }, {
    name: 'pugzy', breed: 'pug'
    }]
    };

    let nameL = lens('name'); // <-- this is why we curried the function
    let ageL = lens('age');
    let dogsL = lens('dogs');
    let dogAt = (idx) => dogsL.compose(lens(idx));

    let rename = (thing, newName) => set(nameL)(newName)(thing);
    ```


    So how do we *use* these fancy new lenses? Lets first tackle reading, or
    "getting", a value from a structure using a lens:

    ```javascript
    let get = (l) => (s) => extract.compose(l(Getter))(s);
    ```


    This is a function which takes a lens and a structure. The `l` variable is a
    lens, which means that it is going to need a transformation function and
    then a value. so `l(Getter)` returns a function that needs a structure given
    to it (which would be `s`). However, ultimately we don't want a `Getter`
    object, we want the value inside it. So we compose `extract` with all this
    to retrieve the final value.

    As for setting, that's a special case of a more general operation:
    transforming a part of a whole. Replacement is one kind of transformation.
    For historical reasons let's define this more general kind of mutation as
    `over`:

    ```javascript
    let over = (l) => (f) => (s) => extract.compose(l(Setter.compose(f)))(s);
    ```

    Let's unpack this. `over` requires a lens, a transformation function, and a
    structure. We compose `Setter` and our transformation function, creating a
    `Setter` containing the new value we want to put back in the whole. Then we
    pass this resulting function to our lens `l`, creating a new function asking
    for a structure. And again, as with `Getter`, we ultimately want the object
    back, not the `Setter` object, so we compose `extract` with all this.

    Pause and re-read this a few times if you need to; I'll wait.

    So actually *setting* a value is a special case of `over` where our
    transformation function takes the value we want to set, the original value,
    and returns the value we want to set. This function is called `constant`:

    ```javascript
    let constant = (x) => (y) => x;
    let set = (l) => (v) => (s) => over(l)(constant(v))(s);
    ```

    Now let's try out our lenses!

    ```javascript
    console.log(myself); // "{ name: 'gatlin', age ... }"
    rename(myself, 'Gatlin');
    console.log(get(nameL)(myself)); // "Gatlin"

    set(
    // Lenses compose!
    dogAt(0).compose(nameL)
    )('Louie')(myself); // And now the dog has a capitalized name

    // But we can also retrieve a part and use existing lens-based functions on them!
    let pugzy = get(dogAt(1))(myself);
    rename(pugzy,'Pugzy');

    console.log(myself); // Run it for yourself!
    ```

    Anyway that's the basics of lenses. There's a whole rabbit hole you can get
    lost in if you want with this, but this should be enough to interest you.

    File renamed without changes.
  5. gatlin revised this gist Jun 30, 2016. 1 changed file with 30 additions and 13 deletions.
    43 changes: 30 additions & 13 deletions lenses.js
    Original file line number Diff line number Diff line change
    @@ -8,14 +8,39 @@
    *
    * What is a lens?
    *
    * A lens, basically, takes a structure and breaks out a part from the whole,
    * allowing you to do something with or to the part before reconstructing the
    * whole. Much like a tangible optical lens it lets you focus on some smaller
    * part of a larger object.
    * A lens in programming, much like a lens in the real world, allows you to
    * focus in on a small part of a larger whole and then do something with or to
    * that part.
    *
    * You may also think of it as breaking a piece out, manipulating the piece,
    * and then reconstructing the whole.
    *
    * That "something" can be changing the value or merely passing the value
    * elsewhere to be read.
    *
    * Thus, lenses are both getters *and* setters. Here's an example of the code
    * we'll be implementing:
    *
    * let foo = {
    * bar: 5,
    * baz: [ 'quux', 'axolotl' ]
    * };
    *
    * let barL = lens('bar');
    * let bazL = lens('baz');
    * let bazAt = (index) => bazL.compose(lens(index));
    *
    * let axolotl = get(bazAt(1))(foo); // 'axolotl'
    * set(barL)(7)(foo);
    * set(bazAt(1))('potrzebie')(foo);
    *
    * console.log(foo); // { bar: 7, baz: ['quux','potrzebie'] }
    *
    * Some key things to note:
    *
    * - Lenses are composable; and
    * - Lenses can be used to both set *and* get parts of an object.
    *
    * More technically, a lens is a function of three arguments:
    *
    * 1. Some structure (eg an Object, an Array, etc);
    @@ -24,14 +49,6 @@
    *
    * The lens then returns a new structure, with its transformed piece inside.
    *
    * Lenses have several very neat properties.
    *
    * First, a lens defined for part of some structure may be used as both a
    * setter and a getter. This means you only need to define a lens once to use
    * it for reading and writing.
    *
    * Because lenses are functions, they may be composed.
    *
    * First, let's define some things we know we'll need, to wit:
    *
    * - A way to replace part of a structure at a certain key with a new value;
    @@ -184,4 +201,4 @@ set(
    let pugzy = get(dogAt(1))(myself);
    rename(pugzy,'Pugzy');

    console.log(myself);
    console.log(myself);
  6. gatlin renamed this gist Jun 30, 2016. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  7. gatlin created this gist Jun 30, 2016.
    187 changes: 187 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,187 @@
    /*
    * (!) You can go to https://repl.it/languages/javascript, paste all this code
    * in there, and try it out for yourself!
    */

    'use strict';
    /*
    *
    * What is a lens?
    *
    * A lens, basically, takes a structure and breaks out a part from the whole,
    * allowing you to do something with or to the part before reconstructing the
    * whole. Much like a tangible optical lens it lets you focus on some smaller
    * part of a larger object.
    *
    * That "something" can be changing the value or merely passing the value
    * elsewhere to be read.
    *
    * More technically, a lens is a function of three arguments:
    *
    * 1. Some structure (eg an Object, an Array, etc);
    * 2. An index specifying some piece of the structure; and
    * 3. A function which transforms that piece
    *
    * The lens then returns a new structure, with its transformed piece inside.
    *
    * Lenses have several very neat properties.
    *
    * First, a lens defined for part of some structure may be used as both a
    * setter and a getter. This means you only need to define a lens once to use
    * it for reading and writing.
    *
    * Because lenses are functions, they may be composed.
    *
    * First, let's define some things we know we'll need, to wit:
    *
    * - A way to replace part of a structure at a certain key with a new value;
    * - A way to compose functions.
    */

    if(!Function.prototype.compose) {
    Function.prototype.compose = function(g) {
    let f = this;
    return function(x) {
    return f(g(x));
    };
    };
    }

    let replace = function(key, value, thing) {
    thing[key] = value;
    return thing;
    };

    /*
    * Now, on to lenses. A lens breaks a part from a whole, passes the part to a
    * function, and puts the result back into the whole. Intuitively, then,
    * whether or not a lens is used as a setter or a getter rests on that function
    * we pass in.
    *
    * This function we pass to the lens must return something we can put back in
    * the whole -- or at the very least, lead us to something which fits the bill.
    *
    * My strategy will be to write two functions which **both** take a value and
    * return an object with two properties:
    *
    * - a reference to the wrapped-up value; and
    * - a function to let us transform that value.
    *
    * Some of you may have already guessed it, but I'm basically defining two
    * functors:
    */

    let Setter = (x) => Object.seal({
    value: x,
    map: (f) => Setter(f(x))
    });

    let Getter = (x) => Object.seal({
    value: x,
    map: (f) => Getter(x)
    });

    let extract = (s) => s.value;

    let setTest = Setter(5);
    console.log(extract(setTest.map((x) => x*2))); // 10

    let getTest = Getter(5);
    console.log(extract(getTest.map((x) => x*2))); // 5

    /*
    * The key here is uniformity: both Setter and Getter wrap up a value and allow
    * me to at least pretend I'm transforming the value inside. Setter lets me do
    * it; Getter doesn't. Simple.
    *
    * The uniform way to transform the value inside is the `map()` method.
    *
    * Armed with these two functions, we can finally write the lens function:
    */

    let lens = (key) => (fn) => (s) => fn(s[key]).map((v) => replace(key, v, s));

    /*
    * Note that I'm writing this function in *curried* style. This is mostly for
    * stylistic reasons but it does make the code more readable and usable later
    * on. You'll see.
    *
    * So now we can create lenses!
    */

    let myself = {
    name: 'gatlin',
    age: 27,
    dogs: [{
    name: 'louie', breed: 'pug'
    }, {
    name: 'pugzy', breed: 'pug'
    }]
    };

    let nameL = lens('name'); // <-- this is why we curried the function
    let ageL = lens('age');
    let dogsL = lens('dogs');
    let dogAt = (idx) => dogsL.compose(lens(idx));

    let rename = (thing, newName) => set(nameL)(newName)(thing);

    /*
    * So how do we *use* these fancy new lenses? Lets first tackle reading, or
    * "getting", a value from a structure using a lens:
    */

    let get = (l) => (s) => extract.compose(l(Getter))(s);

    /*
    * This is a function which takes a lens and a structure. The `l` variable is a
    * lens, which means that it is going to need a transformation function and
    * then a value. so `l(Getter)` returns a function that needs a structure given
    * to it (which would be `s`). However, ultimately we don't want a `Getter`
    * object, we want the value inside it. So we compose `extract` with all this
    * to retrieve the final value.
    *
    * As for setting, that's a special case of a more general operation:
    * transforming a part of a whole. Replacement is one kind of transformation.
    * For historical reasons let's define this more general kind of mutation as
    * `over`:
    */

    let over = (l) => (f) => (s) => extract.compose(l(Setter.compose(f)))(s);

    /*
    * Let's unpack this. `over` requires a lens, a transformation function, and a
    * structure. We compose `Setter` and our transformation function, creating a
    * `Setter` containing the new value we want to put back in the whole. Then we
    * pass this resulting function to our lens `l`, creating a new function asking
    * for a structure. And again, as with `Getter`, we ultimately want the object
    * back, not the `Setter` object, so we compose `extract` with all this.
    *
    * Pause and re-read this a few times if you need to; I'll wait.
    *
    * So actually *setting* a value is a special case of `over` where our
    * transformation function takes the value we want to set, the original value,
    * and returns the value we want to set. This function is called `constant`:
    */

    let constant = (x) => (y) => x;
    let set = (l) => (v) => (s) => over(l)(constant(v))(s);

    /*
    * Now let's try out our lenses!
    */

    console.log(myself);
    rename(myself, 'Gatlin');
    console.log(get(nameL)(myself));

    // Lenses compose!
    set(
    dogAt(0).compose(nameL)
    )('Louie')(myself);

    // But we can also retrieve a part and use existing lens-based functions on them!
    let pugzy = get(dogAt(1))(myself);
    rename(pugzy,'Pugzy');

    console.log(myself);