Skip to content

Instantly share code, notes, and snippets.

@cassiemeharry
Created August 24, 2012 23:43
Show Gist options
  • Save cassiemeharry/3457351 to your computer and use it in GitHub Desktop.
Save cassiemeharry/3457351 to your computer and use it in GitHub Desktop.
Thoughts on Roy Lenses

Lens Types

Looking into the Haskell/JS hybrid Roy, and one of the proposed features are lenses. They are basically sugar over attribute getters and setters, and look like this:

set .x 3 {x: 1, y: 2} == {x: 3, y: 2}
get .name {title: "Mr.", name: "Bob"} == "Bob"

This is much like Python's getattr and setattr, except that these are static (no dynamic property names, required for typechecking) and composable:

let person = {
    name: "Bob",
    "address": {
        "number": 123,
        "street": "Candyland Rd.",
    },
}

let streetLens = (compose .address .street)

get streetLens person == "Candyland Rd."

# Shouldn't compile
# let badLens = (compose .address .doesntexist)
# get badLens person

My Proposal

The following definitions should be placed in the Prelude, with the Lens type constructor hidden to prevent dynamically created lenses.

type Lens = Lens name :: String

composeLens :: Lens -> Lens -> Lens
let composeLens (Lens a) (Lens b) =
    Lens (a + "." + b)

getLens :: Lens -> a -> b
macro getLens attr obj =
    [| obj&(attr) |]

setLens :: Lens -> b -> a -> b
macro setLens attr value obj =
    [| obj&(attr) = value |]

The following rewrites must be done by the compiler:

.attr
    --> Lens "attr"
get .attr obj
    --> get (Lens "attr") obj
    --> getLens (Lens "attr") obj
    --> obj.attr
set .attr value obj
    --> set (Lens "attr") value obj
    --> setLens (Lens "attr") value obj
    --> obj.attr = value

I'm not 100% sure about this method, because having an actual Lens type could lead to run-time typing, which is exactly what Roy is trying to avoid. This is potentially alleviated by not exporting the Lens constructor in the Prelude and leaving it just to the sugar. At that point, however, should we really have a system like this underneath?

What's important here though is that something like get .attr should carry type information that requires the object it's passed have that attribute. I'm not sure how to represent that in Haskell type notation, and probably isn't carried in the system I'm proposing.

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