From 8ac7193485d4f26a870b3fab4bb5ac72c9e603ed Mon Sep 17 00:00:00 2001 From: gcanti Date: Fri, 31 Mar 2017 09:17:53 +0200 Subject: [PATCH 1/2] upgrade to latest fp-ts --- examples/Fold.ts | 4 ++-- examples/Iso.ts | 6 +++--- examples/Lens.ts | 12 ++++++------ examples/Optional.ts | 25 ++++++++++++++----------- examples/Prism.ts | 33 +++++++++++++++++++-------------- examples/Traversal.ts | 8 ++++---- package.json | 5 +++-- src/index.ts | 43 ++++++++++++++++++++++++++++++------------- test/Lens.ts | 34 ++++++++++++++++++++++++++++++++++ test/helpers.ts | 4 ++-- 10 files changed, 117 insertions(+), 57 deletions(-) create mode 100644 test/Lens.ts diff --git a/examples/Fold.ts b/examples/Fold.ts index 220c720..30ff4b2 100644 --- a/examples/Fold.ts +++ b/examples/Fold.ts @@ -1,9 +1,9 @@ import { Fold, fromFoldable } from '../src' -import * as arr from 'fp-ts/lib/Arr' +import * as arr from 'fp-ts/lib/Array' import { monoidProduct, monoidSum } from 'fp-ts/lib/Monoid' import { identity } from 'fp-ts/lib/function' -const xs = arr.to(['a', 'bb']) +const xs = ['a', 'bb'] const fold = fromFoldable(arr) console.log(fold.foldMap(monoidSum, s => s.length, xs)) diff --git a/examples/Iso.ts b/examples/Iso.ts index ee17a49..741d0ae 100644 --- a/examples/Iso.ts +++ b/examples/Iso.ts @@ -5,8 +5,8 @@ const mTokm = new Iso( km => km * 1000 ) -// console.log(mTokm.get(100)) // => 0.1 -// console.log(mTokm.reverseGet(1.2)) // => 1200 +console.log(mTokm.get(100)) // => 0.1 +console.log(mTokm.reverseGet(1.2)) // => 1200 const kmToMile = new Iso( km => km / 1.60934, @@ -16,4 +16,4 @@ const kmToMile = new Iso( // composition const mToMile = mTokm.compose(kmToMile) -// console.log(mToMile.get(100)) // => 0.06213727366498068 +console.log(mToMile.get(100)) // => 0.06213727366498068 diff --git a/examples/Lens.ts b/examples/Lens.ts index df8d13f..48d748c 100644 --- a/examples/Lens.ts +++ b/examples/Lens.ts @@ -21,7 +21,7 @@ export const employee: Employee = { const company = new Lens(s => s.company, (a, s) => ({ ...s, company: a })) -// console.log(JSON.stringify(company.get(employee), null, 2)) +console.log(JSON.stringify(company.get(employee), null, 2)) const address = new Lens(s => s.address, (a, s) => ({ ...s, address: a })) const street = new Lens(s => s.street, (a, s) => ({ ...s, street: a })) @@ -31,22 +31,22 @@ const name = new Lens(s => s.name, (a, s) => ({ ...s, name: a }) const streetLens = company.compose(address).compose(street) export const nameLens = streetLens.compose(name) -// console.log(JSON.stringify(nameLens.get(employee), null, 2)) +console.log(JSON.stringify(nameLens.get(employee), null, 2)) const employee2 = nameLens.modify(a => a.toUpperCase(), employee) -// console.log(JSON.stringify(employee2, null, 2)) +console.log(JSON.stringify(employee2, null, 2)) const employee3 = nameLens.set('low street', employee) -// console.log(JSON.stringify(employee3, null, 2)) +console.log(JSON.stringify(employee3, null, 2)) const numLens = streetLens.compose(new Lens( s => s.num, (a, s) => ({ ...s, num: a }) )) -// console.log(JSON.stringify(numLens.set(42, employee), null, 2)) +console.log(JSON.stringify(numLens.set(42, employee), null, 2)) // generation type PersonType = { @@ -58,4 +58,4 @@ const person: PersonType = { name: 'Giulio', age: 42 } const age = Lens.fromProp('age') -// console.log(age.set(43, person)) // => { name: 'Giulio', age: 43 } +console.log(age.set(43, person)) // => { name: 'Giulio', age: 43 } diff --git a/examples/Optional.ts b/examples/Optional.ts index 25b670f..d6eff0f 100644 --- a/examples/Optional.ts +++ b/examples/Optional.ts @@ -1,5 +1,5 @@ import { Lens, Optional } from '../src' -import { some, none } from 'fp-ts/lib/Option' +import { Option, some, none } from 'fp-ts/lib/Option' import { nameLens, employee } from './Lens' const head = new Optional( @@ -7,20 +7,23 @@ const head = new Optional( (a, s) => a + s.substring(1) ) -// console.log(head.getOption('')) // => None -// console.log(head.getOption('hello')) // => Some('h') -// console.log(head.set('H', '')) // => 'H' -// console.log(head.set('H', 'hello')) // => 'Hello' +console.log(head.getOption('')) // => None +console.log(head.getOption('hello')) // => Some('h') +console.log(head.set('H', '')) // => 'H' +console.log(head.set('H', 'hello')) // => 'Hello' const optional = nameLens.asOptional().compose(head) -// console.log(JSON.stringify(optional.modify(s => s.toUpperCase(), employee), null, 2)) +console.log(JSON.stringify(optional.modify(s => s.toUpperCase(), employee), null, 2)) -interface Person { name: string, surname: string | null | undefined } +interface Person { + name: string, + surname: Option +} -const surname = Optional.fromProp('surname') +const surname = Optional.fromProp('surname') -const p: Person = { name: 'Giulio', surname: null } +const p: Person = { name: 'Giulio', surname: none } -// console.log(surname.getOption(p)) -// console.log(surname.set('Canti', p)) +console.log(surname.getOption(p)) +console.log(surname.set(some('Canti'), p)) diff --git a/examples/Prism.ts b/examples/Prism.ts index 03d7176..611419d 100644 --- a/examples/Prism.ts +++ b/examples/Prism.ts @@ -1,31 +1,36 @@ import { Prism } from '../src' -import { Data1 } from 'fp-ts/lib/data' import { Option, some, none } from 'fp-ts/lib/Option' -class JStr extends Data1{} -class JNum extends Data1{} -class JObj extends Data1<{ [key: string]: Json }>{} +class JStr { + constructor(public readonly value: string) {} +} +class JNum { + constructor(public readonly value: number) {} +} +class JObj { + constructor(public readonly value: { [key: string]: Json }) {} +} type Json = null | JStr | JNum | JObj; const jStr = new Prism( - s => s instanceof JStr ? some(s.value0) : none, + s => s instanceof JStr ? some(s.value) : none, a => new JStr(a) ) -// console.log(jStr.getOption(new JStr('hello'))) -// console.log(jStr.getOption(new JNum(1))) +console.log(jStr.getOption(new JStr('hello'))) +console.log(jStr.getOption(new JNum(1))) // a function is applied only if there is a match const reverse = (s: string): string => s.split('').reverse().join('') -// console.log(jStr.modify(reverse, new JStr('hello'))) -// console.log(jStr.modify(reverse, new JNum(1))) -// console.log(jStr.modifyOption(reverse, new JStr('hello'))) -// console.log(jStr.modifyOption(reverse, new JNum(1))) +console.log(jStr.modify(reverse, new JStr('hello'))) +console.log(jStr.modify(reverse, new JNum(1))) +console.log(jStr.modifyOption(reverse, new JStr('hello'))) +console.log(jStr.modifyOption(reverse, new JNum(1))) // composizione const jNum = new Prism( - s => s instanceof JNum ? some(s.value0) : none, + s => s instanceof JNum ? some(s.value) : none, a => new JNum(a) ) const numberToInt = new Prism( @@ -35,5 +40,5 @@ const numberToInt = new Prism( const jInt = jNum.compose(numberToInt) -// console.log(jInt.getOption(new JNum(5.0))) -// console.log(jInt.getOption(new JNum(5.2))) +console.log(jInt.getOption(new JNum(5.0))) +console.log(jInt.getOption(new JNum(5.2))) diff --git a/examples/Traversal.ts b/examples/Traversal.ts index c5c7134..921eab7 100644 --- a/examples/Traversal.ts +++ b/examples/Traversal.ts @@ -1,13 +1,13 @@ import { Traversal, fromTraversable, Fold } from '../src' -import * as arr from 'fp-ts/lib/Arr' +import * as arr from 'fp-ts/lib/Array' const eachL = fromTraversable(arr) -const xs = arr.to([1, 2, 3, 4]) +const xs = [1, 2, 3, 4] -// console.log(eachL.set(0, xs)) // => [ 0, 0, 0, 0 ] +console.log(eachL.set(0, xs)) // => [ 0, 0, 0, 0 ] -// console.log(eachL.modify((n: number) => n + 1, xs)) // => [ 2, 3, 4, 5 ] +console.log(eachL.modify((n: number) => n + 1, xs)) // => [ 2, 3, 4, 5 ] const fold = eachL.asFold() diff --git a/package.json b/package.json index c83f8ad..2da4d68 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "typings": "lib/index.d.ts", "scripts": { "test": "mocha -r ts-node/register test/*.ts", - "build": "rm -rf lib/* && tsc && tsc -m es6 --outDir lib-jsnext" + "clean": "rm -rf lib/* && rm -rf lib-jsnext/*", + "build": "npm run clean && tsc && tsc -m es6 --outDir lib-jsnext" }, "repository": { "type": "git", @@ -24,7 +25,7 @@ }, "homepage": "https://github.com/gcanti/monocle-ts", "dependencies": { - "fp-ts": "^0.1.1" + "fp-ts": "^0.2.0" }, "devDependencies": { "@types/mocha": "^2.2.38", diff --git a/src/index.ts b/src/index.ts index 3fca9a4..64af5e7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ -import { HKT } from 'fp-ts/lib/HKT' +import { HKT, HKTS } from 'fp-ts/lib/HKT' import { StaticMonoid, monoidArray, monoidAll, monoidAny } from 'fp-ts/lib/Monoid' import { StaticApplicative } from 'fp-ts/lib/Applicative' -import { StaticFoldable, ops } from 'fp-ts/lib/Foldable' +import { StaticFoldable, foldMap } from 'fp-ts/lib/Foldable' import { StaticTraversable } from 'fp-ts/lib/Traversable' import * as option from 'fp-ts/lib/Option' import { Some, Option } from 'fp-ts/lib/Option' @@ -49,6 +49,21 @@ export class Iso { } } +export function lensFromPath(path: [K1, K2, K3, K4, K5, K6, K7, K8, K9, K10]): Lens +export function lensFromPath(path: [K1, K2, K3, K4, K5, K6, K7, K8, K9]): Lens +export function lensFromPath(path: [K1, K2, K3, K4, K5, K6, K7, K8]): Lens +export function lensFromPath(path: [K1, K2, K3, K4, K5, K6, K7]): Lens +export function lensFromPath(path: [K1, K2, K3, K4, K5, K6]): Lens +export function lensFromPath(path: [K1, K2, K3, K4, K5]): Lens +export function lensFromPath(path: [K1, K2, K3, K4]): Lens +export function lensFromPath(path: [K1, K2, K3]): Lens +export function lensFromPath(path: [K1, K2]): Lens +export function lensFromPath(path: [K1]): Lens +export function lensFromPath(path: Array) { + const lens = Lens.fromProp(path[0]) + return path.slice(1).reduce((acc, prop) => acc.compose(Lens.fromProp(prop)), lens) +} + /* Laws: 1. get(set(a, s)) = a @@ -69,6 +84,8 @@ export class Lens { ) } + static fromPath = lensFromPath + modify(f: (a: A) => A, s: S): S { return this.set(f(this.get(s)), s) } @@ -169,10 +186,10 @@ export class Optional { /** view a Options as a Traversal */ asTraversal(): Traversal { return new Traversal( - (applicative: StaticApplicative, f: (a: A) => HKT, s: S): HKT => + (applicative: StaticApplicative, f: (a: A) => HKT[F], s: S): HKT[F] => this.getOption(s).fold( () => applicative.of(s), - a => applicative.map(a => this.set(a, s), f(a)) + a => applicative.map((a: A) => this.set(a, s), f(a)) ) ) } @@ -181,7 +198,7 @@ export class Optional { export class Traversal { constructor( // Van Laarhoven representation - public modifyF: (applicative: StaticApplicative, f: (a: A) => HKT, s: S) => HKT + public modifyF: (applicative: StaticApplicative, f: (a: A) => HKT[F], s: S) => HKT[F] ){} modify(f: (a: A) => A, s: S): S { @@ -195,7 +212,7 @@ export class Traversal { /** compose a Traversal with a Traversal */ compose(ab: Traversal): Traversal { return new Traversal( - (applicative: StaticApplicative, f: (a: B) => HKT, s: S): HKT => + (applicative: StaticApplicative, f: (a: B) => HKT[F], s: S): HKT[F] => this.modifyF(applicative, a => ab.modifyF(applicative, f, a), s) ) } @@ -210,10 +227,10 @@ export class Traversal { } /** create a Traversal from a Traversable */ -export function fromTraversable(traversable: StaticTraversable): Traversal, A> { - return new Traversal, A>( - (applicative: StaticApplicative, f: (a: A) => HKT, s: HKT): HKT> => - traversable.traverse(applicative, f, s) +export function fromTraversable(traversable: StaticTraversable): Traversal[T], A> { + return new Traversal[T], A>( + (applicative: StaticApplicative, f: (a: A) => HKT[F], s: HKT[T]): HKT[T]>[F] => + traversable.traverse(applicative)(f, s) ) } @@ -258,9 +275,9 @@ export class Fold { } /** create a Fold from a Foldable */ -export function fromFoldable(fold: StaticFoldable): Fold, A> { +export function fromFoldable(fold: StaticFoldable): Fold[F], A> { return new Fold( - (monoid: StaticMonoid, f: (a: A) => M, s: HKT): M => - ops.foldMapS(fold, monoid, f, s) + (monoid: StaticMonoid, f: (a: A) => M, s: HKT[F]): M => + foldMap(fold, monoid, f, s) ) } diff --git a/test/Lens.ts b/test/Lens.ts new file mode 100644 index 0000000..0512044 --- /dev/null +++ b/test/Lens.ts @@ -0,0 +1,34 @@ +import { Lens } from '../src' +import * as assert from 'assert' + +interface Street { num: number, name: string } +interface Address { city: string, street: Street } +interface Company { name: string, address: Address } +interface Employee { name: string, company: Company } + +const employee: Employee = { + name: "john", + company: { + name: "awesome inc", + address: { + city: "london", + street: { + num: 23, + name: "high street" + } + } + } +} + +function capitalize(s: string): string { + return s.substring(0, 1).toUpperCase() + s.substring(1) +} + +describe('Lens', () => { + + it('fromPath', () => { + const lens = Lens.fromPath(['company', 'address', 'street', 'name']) + assert.strictEqual(lens.modify(capitalize, employee).company.address.street.name, 'High street') + }) + +}) diff --git a/test/helpers.ts b/test/helpers.ts index 3a7f2a6..86f078f 100644 --- a/test/helpers.ts +++ b/test/helpers.ts @@ -1,7 +1,7 @@ import * as assert from 'assert' -import { HKTOption, isSome, isNone } from 'fp-ts/lib/Option' +import { Option, isSome, isNone } from 'fp-ts/lib/Option' -export function eqOptions(x: HKTOption, y: HKTOption) { +export function eqOptions(x: Option, y: Option) { if (isSome(x) && isSome(y)) { assert.deepEqual(x.value, y.value) } else { From b7800cc481b8959e1f3d298c18871b5d95048ffe Mon Sep 17 00:00:00 2001 From: gcanti Date: Fri, 31 Mar 2017 19:22:57 +0200 Subject: [PATCH 2/2] bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2da4d68..d69522b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "monocle-ts", - "version": "0.0.4", + "version": "0.1.0", "description": "A porting of scala monocle library to TypeScript", "files": [ "lib",