Skip to content
This repository has been 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

Deftype #11

wants to merge 23 commits into from

Conversation

mstade
Copy link
Owner

@mstade mstade commented Apr 24, 2014

Type definitions in JavaScript is a mess. This branch is an effort to come up with some sort of semantic typing, where relationships between objects can be defined by leveraging JS's prototypes.

These are not an effort to implement classes.

Returns `x()` if `x` is a function; otherwise returns `x`.
Previously, `rest` was always defined and passed, even if the
original call didn't supply enough arguments to reach `rest`.
This poses some problems when you expect the length of
`arguments` to match that of the original call to the variadic
function, so now `rest` is only supplied if there are actual
arguments to fill it up with. This means it'll be undefined
otherwise, and `arguments` will be identical to the original
call.

Additionally, I changed `variadic` so that it'll correctly
report `length` for the wrapped function, provided the wrapped
function `length` is 26 or below. I'd be hard pressed to
remember ever seeing a function with more than even 10
parameters, but still. This isn't an ideal solution of course,
but probably hits home 99.99% of the time which is enough.
Will return the last value if all values are logically true;
or the first logically false value.
Very useful function for getting a property value out of an
object. If the object is undefined, will return `notFound`
which defaults to `undefined`. If the property doesn't exist
on the target object, will return `notFound` as well.
Would return `notFound` even if `map` had `key`, but where the
value was logically false.
This is a wrapper around `Object.defineProperties` which makes
it easier to add immutable and non-enumerable properties to
objects. Just like `Object.defineProperties`, this function
will mutate the object in place.

It *is* possible to make the properties both mutable and
enumerable (but not removable) – however this is by design
made to be uncomfortable.

Accessor functions or values that are functions will have
their scope bound to a private scope, which is an extension of
the target object. This allows functions to share data
internally, without fear of exposing anything to the outside
world. A caveat here is that subsequent calls to `defprop`
will create a *different* private scope, with the assumption
being that such calls imply that the properties are unrelated.
I may want to rethink this at some point.
If the first argument to `when` is logically true, or if the
first argument is a function and the result of calling it is
logically true, then `when` will return the value of the
second argument; otherwise it returns null.
`apply` always returned `undefined`, which seemed a tad wrong.
Fixed this and made sure the tests cover that case.
If `val` is called with a function, then any arguments past
the first will be passed along to the function.
No idea where the tabs came from.
@coveralls
Copy link

Coverage Status

Coverage decreased (-0.58%) when pulling 045031e on deftype into 85adb2f on master.

@mstade
Copy link
Owner Author

mstade commented Apr 25, 2014

Well that's nifty tooling, ain't it?

`is(x, x)` could return false if `x` is an object
It was a bit too magical to make up a magical scope, so now it
has to be specified by providing an object.
Added global options for all properties as well, making it a
bit easier to deal with property options. They can still be
overridden for each field however.
Extending objects means creating a new object instance, where
the prototype is set to be the base, and the returned
instance has the properties of any additional objects passed.

If there are multiple objects passed, their properties will
be merged with any conflicts resolved by picking the
rightmost candidate.
Creates a new object instance, derived from `Object.prototype`
and with the merged properties of all passed objects. Property
conflicts are resolved by picking the rightmost candidate.

Properties are merged by looking at the actual property
descriptors and not just values, meaning things like accessor
functions and property metadata such as whether it's
enumerable, writable, or configurable, are passed along. Thus
a merge is about as identical to the input objects as it can
get.
This makes it easy to filter properties from the description.
Also, this change makes the output consistent, whereas before
it would exclude the property name if it was given to the
function. This is awkward when using things like `seq` to loop
over descriptions.
`describe` now returns an array of arrays, where each array is
a key/value pair. I considered flattening this, but figured
that might be tricky to deal with. Might revisit later.
I find partial application from the left or right isn't enough
to cover all cases, and puts a bit of an unfair burden on
function definitions to consider such applications. So I added
a think to `partial` where if you set one of the arguments of
a partial application to be the `partial` function itself,
that acts as a signal to `partial` to say that the specific
argument hasn't been supplied yet, and should come later when
applying the function.

Is-a very nice.
@mstade
Copy link
Owner Author

mstade commented May 2, 2014

Design note: do not implement internal mutable state for methods and get/set accessors. This is too much of a foot-gun to be useful, and instead any mutability should probably be opt-in and encapsulated on a field-by-field basis; i.e. mutable fields get their own internal state, separate an unreachable from all other fields (unless the field explicitly returns this.)

Methods and get/set accessors should probably be bound to the type instance, but only if that instance is fully immutable – that is, not only are fields immutable, but the object is closed for extensions as well. Otherwise, this is a bit too much rope as well.

@mstade mstade mentioned this pull request May 4, 2014
@mstade
Copy link
Owner Author

mstade commented Jul 26, 2014

Ditch types in favor of tags. Tags should work somewhat like Clojure's derive. The idea is that by creating a tag, which is just a name (NB: there is considerable overlap wi ES6 symbols here) you can use that to link things to semantics. More work should be done on the rationale behind this, but I like it and it nicely separates the concern of semantics from these types that are really just sugar around structural validation.

@mstade mstade closed this Jul 26, 2014
@mstade
Copy link
Owner Author

mstade commented Jul 28, 2014

Reopening this since there's a ton of useful stuff on the branch. However, ditch deftype still, or extract that work into another branch if there's any reason to keep tinkering with it.

@mstade mstade reopened this Jul 28, 2014
@coveralls
Copy link

Coverage Status

Coverage decreased (-1.03%) when pulling f062623 on deftype into 85adb2f on master.

@coveralls
Copy link

Coverage Status

Coverage decreased (-1.03%) when pulling f062623 on deftype into 85adb2f on master.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants