Skip to content

Instantly share code, notes, and snippets.

@jj-jabb
Created September 14, 2013 14:05
Show Gist options
  • Save jj-jabb/6562312 to your computer and use it in GitHub Desktop.
Save jj-jabb/6562312 to your computer and use it in GitHub Desktop.
JavaScript immutable List and Lens implementation
var List = (function() {
function List(src) {
this._list = null
this._length = 0
if(src instanceof List) {
this._list = src._list
this._length = src._length
}
}
List.fromArray = function(src) {
var list = new List()
for (var i = src.length - 1; i >= 0; i--) {
list = list.push(src[i])
}
return list;
}
List.prototype = {
head: function () {
return this._list === null ? null : this._list.head;
},
tail: function () {
return this._list === null ? null : this._list.tail;
},
push: function (value) {
var result = new List(this)
result._list = { head: value, tail: this._list === null ? null : this }
result._length = result._length + 1
return result
},
length: function () {
return this._length
},
map: function (fn) {
var arr = []
var list = this
while(list !== null) {
arr.push(fn(list.head()))
list = list.tail()
}
return List.fromArray(arr)
}
}
return List
})();
var Lens = (function() {
function copy(src) {
if (src === null || typeof src !== "object") return obj;
if (src instanceof Date) {
var date = new Date()
date.setTime(src.getTime())
return date
}
// Lists are immutable
if (src instanceof List) return src
if (src instanceof Array) return List.fromArray(src)
if (src instanceof Object) {
var obj = {};
for (var attribute in src) {
if (src.hasOwnProperty(attribute)) {
obj[attribute] = src[attribute]
}
}
return obj
}
}
function Lens(fieldName) {
this._fieldName = fieldName;
}
Lens.copyObject = copy
Lens.prototype = {
get: function(model) {
if (!model) {
return this.get
}
return model[this._fieldName]
},
set: function(value, model) {
var self = this;
if (!model) {
return function(m) {
return self.set(value, m)
}
}
var result = copy(model)
result[this._fieldName] = value
return result
},
modify: function(fn, model) {
var self = this;
if (!model) {
return function(m) {
return self.modify(fn, m)
}
}
return this.set(fn(this.get(model)), model)
},
compose: function(lens) {
return new ComposedLens(this, lens)
}
}
function ComposedLens(lens1, lens2) {
this._lens1 = lens1;
this._lens2 = lens2;
}
ComposedLens.prototype = {
get: function(model) {
if (!model) {
return this.get
}
return this._lens2.get(this._lens1.get(model))
},
set: function(value, model) {
var self = this
if (!model) {
return function(m) {
return self.set(value, m)
}
}
return this._lens1.modify(this._lens2.set(value), model)
},
modify: Lens.prototype.modify,
compose: Lens.prototype.compose
}
return Lens
})();
// some simple tests
var sqr = function(x) { return x * x }
var l0 = List.fromArray([10, 11])
var l1 = l0.map(sqr)
// need to figure out how to push into a list (i.e. author could have a list of cars)
var car = {
color: "red"
}
var author = {
name: "bob",
car: car
}
var book = {
title: "blarg",
author: author
}
var bookTitle = new Lens("title")
var bookAuthor = new Lens("author")
var authorName = new Lens("name")
var authorCar = new Lens("car")
var carColor = new Lens("color")
var bookAuthorName = bookAuthor.compose(authorName)
var bookAuthorCarColor = bookAuthor.compose(authorCar).compose(carColor);
var title = bookTitle.get(book)
var bookb = bookTitle.set("zombies", book)
var bookc = bookAuthorName.set("joe", book)
var bookd = bookAuthorCarColor.set("blue", book)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment