Skip to content
This repository was archived by the owner on Oct 24, 2023. It is now read-only.

Deftype #11

Open
wants to merge 23 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
dcb7bff
Added `val`
mstade Apr 18, 2014
f123004
Improved `variadic` tests and change behavior of `rest`
mstade Apr 18, 2014
5d0c7f8
Added `and` for logical testing
mstade Apr 18, 2014
ea41b2e
Added `get` for getting the value of a property
mstade Apr 24, 2014
507a876
Fix a bug in `get` and improve tests
mstade Apr 24, 2014
340be59
Added `defprop` for adding properties to an object
mstade Apr 24, 2014
de6115b
Added `when`
mstade Apr 24, 2014
91afbc1
Fixed thunk errors
mstade Apr 24, 2014
8083b49
Fixed a bug in `apply` and improved tests
mstade Apr 24, 2014
353c0a9
When calling `val` with a function, pass additional arguments
mstade Apr 24, 2014
3130c0b
Fixed indent messup
mstade Apr 24, 2014
045031e
Fixed `is` when checking for booleans, and improved tests.
mstade Apr 25, 2014
859d570
Fixed a bug when calling `is` with identical args
mstade Apr 26, 2014
21f3be9
Changed how property binding works in `defprop`
mstade Apr 26, 2014
e0e36de
Added `describe` for describing object properties
mstade Apr 28, 2014
5996d05
Added `extend` for creating derivations of objects
mstade Apr 29, 2014
8ccd711
Added `merge` for merging object properties
mstade Apr 29, 2014
542a5a7
Changed `describe` to take a variable number of properties
mstade Apr 29, 2014
cceb342
Changed `describe` to work like `seq` over maps
mstade Apr 29, 2014
421111e
Added some magic sauce to `partial`
mstade Apr 29, 2014
d6f7842
Fix `get` tests
mstade Jul 27, 2014
065afe3
Re-added missing `assert`
mstade Jul 27, 2014
f062623
Fixed `extend` after changes to `describe`
mstade Jul 28, 2014
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions lib/and.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module.exports = require('./variadic')(and)

function and(x, rest) {
if (isEmpty(arguments)) return true

var result = val(x)

if (isnt(result) || isnt(rest)) return result

rest.every(function(x) {
return is(result = val(x))
})

return result
}

const isEmpty = require('./isEmpty')
, isnt = require('./isnt')
, val = require('./val')
, is = require('./is')
3 changes: 1 addition & 2 deletions lib/apply.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ module.exports = apply

function apply(fn, args) {
if (isnt(Function, fn)) throw new TypeError('First argument must be a function.')

fn.apply(this, args)
return fn.apply(this, args)
}

const isnt = require('./isnt')
15 changes: 15 additions & 0 deletions lib/assert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module.exports = assert

function assert(x, message, type) {
if (isnt(x)) {
message = val(message)
isnt(message) && (message = "Assertion failed.")
throw is(Error, message)? message : Error(message)
}

return x
}

const isnt = require("./isnt")
, val = require("./val")
, is = require("./is")
52 changes: 52 additions & 0 deletions lib/defprop.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
module.exports = defprop

function defprop(target, fields, opts) {
if (isnt(target)) throw TypeError("Target must exist.")
if (isnt(fields)) throw TypeError("Fields must exist.")

opts || (opts = {})

const names = Object.getOwnPropertyNames(fields)

const isMutable = get(opts, "^writable", false)
, isEnum = get(opts, "^enumerable", false)
, isConf = get(opts, "^configurable", false)
, scope = get(opts, "^bind", false)

return Object.defineProperties(target,
names.reduce(function(p, k) {
const prop = describe(fields, k)
, opt = $(get, opts[k])

if (isnt(prop.get) && isnt(prop.set)) {
prop.writable = opt("writable", isMutable)

when(is(Function, prop.value) && opt("bind", scope), function(scope) {
prop.value = prop.value.bind(scope)
})
} else {
when(prop.get && opt("writable") && !prop.set, function() {
throw TypeError("The accessor `"+k+"` can't be writable without a setter.")
})

when(opt("bind", scope), function(scope) {
prop.get && (prop.get = prop.get.bind(scope))
prop.set && (prop.set = prop.set.bind(scope))
})
}

prop.enumerable = opt("enumerable", isEnum)
prop.configurable = opt("configurable", isConf)

return (p[k] = prop), p
}, {})
)
}

const describe = Object.getOwnPropertyDescriptor
, isEmpty = require("./isEmpty")
, isnt = require("./isnt")
, when = require("./when")
, get = require("./get")
, is = require("./is")
, $ = require("./partial")
28 changes: 28 additions & 0 deletions lib/describe.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module.exports = require("./variadic")(describe)

function describe(obj, fields) {
assert(is(Object.prototype, obj), TypeError("Can't describe non-object: " + src(obj)))

const descprop = $(Object.getOwnPropertyDescriptor, obj)

if (is(fields)) {
const hasprop = obj.hasOwnProperty.bind(obj)
, props = []

return fields.map(function(name) {
assert(hasprop(name), TypeError("Can't describe non-existant property: " + name))
return [name, descprop(name)]
})
} else {
return names(obj).map(function(name) {
return [name, descprop(name)]
}, {})
}
}

const assert = require("./assert")
, names = Object.getOwnPropertyNames
, each = require("./each")
, src = require("./src")
, is = require("./is")
, $ = require("./partial")
23 changes: 23 additions & 0 deletions lib/extend.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module.exports = require("./variadic")(extend)

function extend(base, rest) {
assert(isExtendable(base), TypeError("Base must be an extendable object."))
assert(is(rest), TypeError("At least one extension object must be defined."))

const traits = describe(apply(merge, rest)).reduce(function(traits, prop) {
traits[prop[0]] = prop[1]
return traits
}, {})

return Object.create(base, traits)
}

const describe = require("./describe")
, assert = require("./assert")
, apply = require("./apply")
, merge = require("./merge")
, comp = require("./comp")
, is = require("./is")
, $ = require("./partial")

const isExtendable = $(is, Object.prototype)
11 changes: 11 additions & 0 deletions lib/get.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module.exports = get

function get(map, key, notFound) {
if (isnt(map)) return notFound
if (!map.hasOwnProperty(key)) return notFound

return map[key]
}

const isnt = require('./isnt')
, is = require('./is')
4 changes: 3 additions & 1 deletion lib/is.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ function is(c, x) {
return !not(c)
}

if (c === x) return true

const cs = lowerCase(c)

if (cs === 'null') {
Expand All @@ -18,7 +20,7 @@ function is(c, x) {
} else if (type(c) === 'string') {
return cs === type(x)
} else {
return is(x) && x.constructor === c || c.isPrototypeOf(x)
return (x != null && x.constructor === c) || c.isPrototypeOf(x)
}
}

Expand Down
18 changes: 18 additions & 0 deletions lib/merge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module.exports = require("./variadic")(merge)

function merge(objects) {
if (isEmpty(objects)) return

return create(Object.prototype, objects.reduce(function(fields, t) {
names(t).forEach(function(k) {
fields[k] = describe(t, k)
})

return fields
}, {}))
}

const describe = Object.getOwnPropertyDescriptor
, isEmpty = require("./isEmpty")
, create = Object.create
, names = Object.getOwnPropertyNames
23 changes: 14 additions & 9 deletions lib/partial.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
module.exports = partial
const $ = module.exports = require("./variadic")(partial)

function partial(fun, args) {
if (arguments.length === 1) return fun
function partial(fun, part) {
if (isnt(part)) return fun

const part = [].slice.call(arguments, 1)
return variadic(function(rest) {
const args = part.map(function(arg) {
return arg === $? rest.shift() : arg
})

return function partial() {
const rest = [].slice.call(arguments)
return fun.apply(this, part.concat(rest))
}
}
return fun.apply(this, args.concat(rest))
})
}

const variadic = require("./variadic")
, each = require("./each")
, isnt = require("./isnt")
14 changes: 4 additions & 10 deletions lib/thunk.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
module.exports = require('./variadic')(thunk)

function thunk(fn, rest) {
if (isnt(Function, fn)) {
rest = [fn]
fn = identity
}
if (isnt(Function, fn)) throw new TypeError('Expected `fn` to be a function.')

if (rest.length > 1) {
if (is(rest)) {
return function thunk() {
return fn.apply(this, rest)
}
} else if (rest.length) {
return function thunk() {
return fn.call(this, rest[0])
}
} else {
return function thunk() {
return fn.call(this)
Expand All @@ -23,4 +16,5 @@ function thunk(fn, rest) {

const identity = require('./identity')
, slice = require('./slice')
, isnt = require('./isnt')
, isnt = require('./isnt')
, is = require('./is')
8 changes: 8 additions & 0 deletions lib/val.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = require('./variadic')(val)

function val(x, rest) {
return is(Function, x)? apply(x, rest) : x
}

const apply = require('./apply')
, is = require('./is')
58 changes: 42 additions & 16 deletions lib/variadic.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,49 @@ module.exports = variadic

function variadic(fn) {
if (isnt(Function, fn)) throw new TypeError('Parameter `fn` must be a function.')

const argc = fn.length - 1

const argc = fn.length - 1

if (argc) {
return function variadic() {
const head = slice(arguments, 0, argc)
, tail = slice(arguments, argc)

return fn.apply(this, head.concat([tail]))
}
} else {
return function variadic() {
const rest = slice(arguments)
return fn.call(this, rest)
}
switch (argc) {
case -1 : return fn
case 0 : return function (rest) { return apply(fn, this, argc, slice(arguments)) }
case 1 : return function (a, rest) { return apply(fn, this, argc, slice(arguments)) }
case 2 : return function (a, b, rest) { return apply(fn, this, argc, slice(arguments)) }
case 3 : return function (a, b, c, rest) { return apply(fn, this, argc, slice(arguments)) }
case 4 : return function (a, b, c, d, rest) { return apply(fn, this, argc, slice(arguments)) }
case 5 : return function (a, b, c, d, e, rest) { return apply(fn, this, argc, slice(arguments)) }
case 6 : return function (a, b, c, d, e, f, rest) { return apply(fn, this, argc, slice(arguments)) }
case 7 : return function (a, b, c, d, e, f, g, rest) { return apply(fn, this, argc, slice(arguments)) }
case 8 : return function (a, b, c, d, e, f, g, h, rest) { return apply(fn, this, argc, slice(arguments)) }
case 9 : return function (a, b, c, d, e, f, g, h, i, rest) { return apply(fn, this, argc, slice(arguments)) }
case 10 : return function (a, b, c, d, e, f, g, h, i, j, rest) { return apply(fn, this, argc, slice(arguments)) }
case 11 : return function (a, b, c, d, e, f, g, h, i, j, k, rest) { return apply(fn, this, argc, slice(arguments)) }
case 12 : return function (a, b, c, d, e, f, g, h, i, j, k, l, rest) { return apply(fn, this, argc, slice(arguments)) }
case 13 : return function (a, b, c, d, e, f, g, h, i, j, k, l, m, rest) { return apply(fn, this, argc, slice(arguments)) }
case 14 : return function (a, b, c, d, e, f, g, h, i, j, k, l, m, n, rest) { return apply(fn, this, argc, slice(arguments)) }
case 15 : return function (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, rest) { return apply(fn, this, argc, slice(arguments)) }
case 16 : return function (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, rest) { return apply(fn, this, argc, slice(arguments)) }
case 17 : return function (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, rest) { return apply(fn, this, argc, slice(arguments)) }
case 18 : return function (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, rest) { return apply(fn, this, argc, slice(arguments)) }
case 19 : return function (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, rest) { return apply(fn, this, argc, slice(arguments)) }
case 20 : return function (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, rest) { return apply(fn, this, argc, slice(arguments)) }
case 21 : return function (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, rest) { return apply(fn, this, argc, slice(arguments)) }
case 22 : return function (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, rest) { return apply(fn, this, argc, slice(arguments)) }
case 23 : return function (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, rest) { return apply(fn, this, argc, slice(arguments)) }
case 24 : return function (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, rest) { return apply(fn, this, argc, slice(arguments)) }
case 25 : return function (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, rest) { return apply(fn, this, argc, slice(arguments)) }
default : return function (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, rest) { return apply(fn, this, argc, slice(arguments)) }
}
}

const slice = require('./slice')
, isnt = require('./isnt')
function apply(fn, host, argc, argv) {
const rest = slice(argv, argc)
argv = slice(argv, 0, argc)
return isEmpty(rest)? fn.apply(host, argv) : fn.apply(host, argv.concat([rest]))
}

const isEmpty = require('./isEmpty')
, slice = require('./slice')
, isnt = require('./isnt')
, min = require('./min')
, max = Math.max
9 changes: 9 additions & 0 deletions lib/when.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module.exports = when

function when(cond, body) {
const it = val(cond)
return is(it)? val(body, it) : null
}

const val = require('./val')
, is = require('./is')
Loading