diff --git a/package.json b/package.json index 13b58eee..8f826ec0 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "rxjs": "^6.2.1" }, "dependencies": { + "@microstates/lens": "1.0.0-beta.2", "funcadelic": "^0.5.0", "symbol-observable": "^1.2.0" }, diff --git a/rollup.config.js b/rollup.config.js index 23dced82..419c63a7 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -7,7 +7,8 @@ const replace = require('rollup-plugin-replace'); const external = [ "funcadelic", - "symbol-observable" + "symbol-observable", + "@microstates/lens" ]; const babelPlugin = babel({ diff --git a/src/lens.js b/src/lens.js deleted file mode 100644 index e75e8afe..00000000 --- a/src/lens.js +++ /dev/null @@ -1,83 +0,0 @@ -import { Functor, map, Semigroup } from 'funcadelic'; - -import { childAt } from './tree'; - -class Box { - static get of() { - return (...args) => new this(...args); - } - - static unwrap(box) { - return box.value; - } - - constructor(value) { - this.value = value; - } -} - -const { unwrap } = Box; - -class Id extends Box {} - -class Const extends Box {} - -Functor.instance(Id, { - map(fn, id) { - return Id.of(fn(id.value)); - } -}); - -Functor.instance(Const, { - map(fn, constant) { - return constant; - } -}); - -export function compose(f, g) { - return (...x) => f(g(...x)); -} - -export function view(lens, context) { - let get = compose(unwrap, lens(Const.of)); - return get(context); -} - -export function over(lens, fn, context) { - let update = compose(unwrap, lens(compose(Id.of, fn))); - return update(context); -} - -export function set(lens, value, context) { - return over(lens, () => value, context); -} - -export function Lens(get, set) { - return f => context => { - return map(value => set(value, context), f(get(context))); - }; -} - -export const transparent = Lens(x => x, y => y); - -export function At(property, container) { - let get = context => context != null ? childAt(property, context) : undefined; - let set = (part, whole) => { - let context = whole == null ? (Array.isArray(container) ? [] : {}) : whole; - if (part === context[property]) { - return context; - } else if (Array.isArray(context)) { - let clone = context.slice(); - clone[Number(property)] = part; - return clone; - } else { - return Semigroup.for(Object).append(context, {[property]: part}); - } - }; - - return Lens(get, set); -} - -export function Path(path) { - return path.reduce((lens, key) => compose(lens, At(key)), transparent); -} diff --git a/src/meta.js b/src/meta.js index 805d0f50..87f630e9 100644 --- a/src/meta.js +++ b/src/meta.js @@ -1,4 +1,4 @@ -import { At, compose, transparent, over, view } from './lens'; +import { At, compose, transparent, over, view } from '@microstates/lens'; export class Meta { static symbol = Symbol('Meta'); diff --git a/src/microstates.js b/src/microstates.js index 92013f5c..dd2e310a 100644 --- a/src/microstates.js +++ b/src/microstates.js @@ -1,5 +1,5 @@ import { append, stable, map } from 'funcadelic'; -import { set } from './lens'; +import { set } from '@microstates/lens'; import { Meta, mount, metaOf, valueOf, sourceOf } from './meta'; import { methodsOf } from './reflection'; import dsl from './dsl'; diff --git a/src/pathmap.js b/src/pathmap.js index 5941c083..dc54e98f 100644 --- a/src/pathmap.js +++ b/src/pathmap.js @@ -1,6 +1,6 @@ +import { view, Path } from '@microstates/lens'; import { methodsOf } from './reflection'; import { create } from './microstates'; -import { view, Path } from './lens'; import { valueOf, Meta } from './meta'; import { defineChildren } from './tree'; import { stable } from 'funcadelic'; diff --git a/src/storage.js b/src/storage.js index 980b152f..19b8b406 100644 --- a/src/storage.js +++ b/src/storage.js @@ -1,4 +1,4 @@ -import { view, set, Path } from './lens'; +import { view, set, Path } from '@microstates/lens'; export default class Storage { constructor(value, observe = x => x) { diff --git a/src/tree.js b/src/tree.js index 7b630cff..e3126fa9 100644 --- a/src/tree.js +++ b/src/tree.js @@ -5,14 +5,6 @@ import CachedProperty from './cached-property'; export const Tree = type(class { static name = 'Tree'; - childAt(key, parent) { - if (parent[Tree.symbol]) { - return this(parent).childAt(key, parent); - } else { - return parent[key]; - } - } - defineChildren(fn, parent) { if (parent[Tree.symbol]) { return this(parent).defineChildren(fn, parent); @@ -24,4 +16,4 @@ export const Tree = type(class { } }); -export const { childAt, defineChildren } = Tree.prototype; +export const { defineChildren } = Tree.prototype; diff --git a/src/types/array.js b/src/types/array.js index f31fbfb9..ad368d05 100644 --- a/src/types/array.js +++ b/src/types/array.js @@ -1,8 +1,8 @@ -import { At, set } from '../lens'; +import { at, At, set, Context } from '@microstates/lens'; import { mount, valueOf } from '../meta'; import { create } from '../microstates'; import parameterized from '../parameterized'; -import { Tree, childAt } from '../tree'; +import { Tree } from '../tree'; export default parameterized(T => class ArrayType { static T = T; @@ -88,7 +88,7 @@ export default parameterized(T => class ArrayType { get done() { return next.done; }, get value() { if (!next.done) { - return childAt(index, array); + return at(index, array); } else { return undefined; } @@ -100,16 +100,18 @@ export default parameterized(T => class ArrayType { static initialize() { - Tree.instance(this, { - childAt(key, array) { + Context.instance(this, { + at(key, array) { if (typeof key === 'number') { let value = valueOf(array)[key]; return mount(array, create(T, value), key); } else { return array[key]; } - }, + } + }); + Tree.instance(this, { defineChildren(fn, array) { let generate = array[Symbol.iterator]; return Object.defineProperty(array, Symbol.iterator, { diff --git a/src/types/object.js b/src/types/object.js index f7518c00..9e2df766 100644 --- a/src/types/object.js +++ b/src/types/object.js @@ -1,10 +1,10 @@ import { append, filter, map } from 'funcadelic'; +import { at, Context } from '@microstates/lens'; import { reduce, query } from '../query'; import parameterized from '../parameterized'; import { valueOf, mount } from '../meta'; import { create } from '../microstates'; -import { Tree, childAt } from '../tree'; - +import { Tree } from '../tree'; export default parameterized(T => class ObjectType { static T = T; @@ -59,7 +59,7 @@ export default parameterized(T => class ObjectType { get done() { return next.done; }, get value() { if (!next.done) { - return new Entry(next.value, childAt(next.value, object)); + return new Entry(next.value, at(next.value, object)); } else { return undefined; } @@ -70,15 +70,19 @@ export default parameterized(T => class ObjectType { } static initialize() { - Tree.instance(this, { - childAt(key, object) { + + Context.instance(this, { + at(key, object) { if (typeof key !== 'string') { return object[key]; } else { let value = valueOf(object)[key]; return mount(object, create(T, value), key); } - }, + } + }); + + Tree.instance(this, { defineChildren(fn, object) { let generate = object[Symbol.iterator]; return Object.defineProperty(object, Symbol.iterator, { diff --git a/tests/lens.test.js b/tests/lens.test.js deleted file mode 100644 index eddfee2a..00000000 --- a/tests/lens.test.js +++ /dev/null @@ -1,25 +0,0 @@ -/* global describe, it, beforeEach */ -import expect from 'expect'; -import { compose, view, set, At } from '../src/lens'; - -describe('At', function() { - let lens; - beforeEach(function() { - lens = compose(At(0, []), At("hello")); - }); - - it('instantiates objects of the correct type at each path', function() { - expect(set(lens, "world", undefined)).toEqual([{hello: 'world'}]); - }); - - it('set-get: view retrievs what set put in', function() { - let cookie = {}; - let object = set(lens, cookie, undefined); - expect(view(lens, object)).toBe(cookie); - }); - - it('get-set: If you set focus to the same value it has, the whole does not change', function() { - let object = set(lens, "world", undefined); - expect(set(lens, "world", object)).toBe(object); - }); -}); diff --git a/yarn.lock b/yarn.lock index 23d552be..f0896c14 100644 --- a/yarn.lock +++ b/yarn.lock @@ -662,6 +662,13 @@ "@types/istanbul-lib-coverage" "^1.1.0" "@types/yargs" "^12.0.9" +"@microstates/lens@1.0.0-beta.2": + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/@microstates/lens/-/lens-1.0.0-beta.2.tgz#b43badbe038a90f95f13f4ae57a34fddd380c356" + integrity sha512-WrPBgPut3h8c7/Yl1tIaA8Fw4TV80mx3EXJGvumBhx15GE+z31HB+31loz9+I4dbGq6xC4Xi7rhMXdiBqRK6Ug== + dependencies: + funcadelic "^0.5.7" + "@types/estree@0.0.39": version "0.0.39" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" @@ -1814,7 +1821,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -funcadelic@^0.5.0: +funcadelic@^0.5.0, funcadelic@^0.5.7: version "0.5.7" resolved "https://registry.yarnpkg.com/funcadelic/-/funcadelic-0.5.7.tgz#e76f6a9028985531924c1159dc1c930f5b00acb2" integrity sha512-kwKSJVAd2FNFmBgku81FC6VJCQdgcT/gtwG9z7H7FxL4NlDKsbdGqLwtjbDlYgR77t+NuT8phBLkmLSKsg1J5w==