Skip to content

Commit

Permalink
Merge pull request #3 from gcanti/fp-ts0.2
Browse files Browse the repository at this point in the history
upgrade to latest fp-ts
  • Loading branch information
gcanti authored Mar 31, 2017
2 parents 3741818 + b7800cc commit b8e05f4
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 58 deletions.
4 changes: 2 additions & 2 deletions examples/Fold.ts
Original file line number Diff line number Diff line change
@@ -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.URI, string>(arr)

console.log(fold.foldMap(monoidSum, s => s.length, xs))
Expand Down
6 changes: 3 additions & 3 deletions examples/Iso.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ const mTokm = new Iso<number, number>(
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<number, number>(
km => km / 1.60934,
Expand All @@ -16,4 +16,4 @@ const kmToMile = new Iso<number, number>(
// composition
const mToMile = mTokm.compose(kmToMile)

// console.log(mToMile.get(100)) // => 0.06213727366498068
console.log(mToMile.get(100)) // => 0.06213727366498068
12 changes: 6 additions & 6 deletions examples/Lens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const employee: Employee = {

const company = new Lens<Employee, Company>(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<Company, Address>(s => s.address, (a, s) => ({ ...s, address: a }))
const street = new Lens<Address, Street>(s => s.street, (a, s) => ({ ...s, street: a }))
Expand All @@ -31,22 +31,22 @@ const name = new Lens<Street, string>(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<Street, number>(
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 = {
Expand All @@ -58,4 +58,4 @@ const person: PersonType = { name: 'Giulio', age: 42 }

const age = Lens.fromProp<PersonType, 'age'>('age')

// console.log(age.set(43, person)) // => { name: 'Giulio', age: 43 }
console.log(age.set(43, person)) // => { name: 'Giulio', age: 43 }
25 changes: 14 additions & 11 deletions examples/Optional.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
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<string, string>(
s => s.length > 0 ? some(s[0]) : none,
(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<string>
}

const surname = Optional.fromProp<Person, 'surname', string>('surname')
const surname = Optional.fromProp<Person, 'surname'>('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))
33 changes: 19 additions & 14 deletions examples/Prism.ts
Original file line number Diff line number Diff line change
@@ -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<string>{}
class JNum extends Data1<number>{}
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<Json, string>(
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<Json, number>(
s => s instanceof JNum ? some(s.value0) : none,
s => s instanceof JNum ? some(s.value) : none,
a => new JNum(a)
)
const numberToInt = new Prism<number, number>(
Expand All @@ -35,5 +40,5 @@ const numberToInt = new Prism<number, number>(

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)))
8 changes: 4 additions & 4 deletions examples/Traversal.ts
Original file line number Diff line number Diff line change
@@ -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.URI, number>(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()

Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand All @@ -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",
Expand All @@ -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",
Expand Down
43 changes: 30 additions & 13 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -49,6 +49,21 @@ export class Iso<S, A> {
}
}

export function lensFromPath<T, K1 extends keyof T, K2 extends keyof T[K1], K3 extends keyof T[K1][K2], K4 extends keyof T[K1][K2][K3], K5 extends keyof T[K1][K2][K3][K4], K6 extends keyof T[K1][K2][K3][K4][K5], K7 extends keyof T[K1][K2][K3][K4][K5][K6], K8 extends keyof T[K1][K2][K3][K4][K5][K6][K7], K9 extends keyof T[K1][K2][K3][K4][K5][K6][K7][K8], K10 extends keyof T[K1][K2][K3][K4][K5][K6][K7][K8][K9]>(path: [K1, K2, K3, K4, K5, K6, K7, K8, K9, K10]): Lens<T, T[K1][K2][K3][K4][K5][K6][K7][K8][K9][K10]>
export function lensFromPath<T, K1 extends keyof T, K2 extends keyof T[K1], K3 extends keyof T[K1][K2], K4 extends keyof T[K1][K2][K3], K5 extends keyof T[K1][K2][K3][K4], K6 extends keyof T[K1][K2][K3][K4][K5], K7 extends keyof T[K1][K2][K3][K4][K5][K6], K8 extends keyof T[K1][K2][K3][K4][K5][K6][K7], K9 extends keyof T[K1][K2][K3][K4][K5][K6][K7][K8]>(path: [K1, K2, K3, K4, K5, K6, K7, K8, K9]): Lens<T, T[K1][K2][K3][K4][K5][K6][K7][K8][K9]>
export function lensFromPath<T, K1 extends keyof T, K2 extends keyof T[K1], K3 extends keyof T[K1][K2], K4 extends keyof T[K1][K2][K3], K5 extends keyof T[K1][K2][K3][K4], K6 extends keyof T[K1][K2][K3][K4][K5], K7 extends keyof T[K1][K2][K3][K4][K5][K6], K8 extends keyof T[K1][K2][K3][K4][K5][K6][K7]>(path: [K1, K2, K3, K4, K5, K6, K7, K8]): Lens<T, T[K1][K2][K3][K4][K5][K6][K7][K8]>
export function lensFromPath<T, K1 extends keyof T, K2 extends keyof T[K1], K3 extends keyof T[K1][K2], K4 extends keyof T[K1][K2][K3], K5 extends keyof T[K1][K2][K3][K4], K6 extends keyof T[K1][K2][K3][K4][K5], K7 extends keyof T[K1][K2][K3][K4][K5][K6]>(path: [K1, K2, K3, K4, K5, K6, K7]): Lens<T, T[K1][K2][K3][K4][K5][K6][K7]>
export function lensFromPath<T, K1 extends keyof T, K2 extends keyof T[K1], K3 extends keyof T[K1][K2], K4 extends keyof T[K1][K2][K3], K5 extends keyof T[K1][K2][K3][K4], K6 extends keyof T[K1][K2][K3][K4][K5]>(path: [K1, K2, K3, K4, K5, K6]): Lens<T, T[K1][K2][K3][K4][K5][K6]>
export function lensFromPath<T, K1 extends keyof T, K2 extends keyof T[K1], K3 extends keyof T[K1][K2], K4 extends keyof T[K1][K2][K3], K5 extends keyof T[K1][K2][K3][K4]>(path: [K1, K2, K3, K4, K5]): Lens<T, T[K1][K2][K3][K4][K5]>
export function lensFromPath<T, K1 extends keyof T, K2 extends keyof T[K1], K3 extends keyof T[K1][K2], K4 extends keyof T[K1][K2][K3]>(path: [K1, K2, K3, K4]): Lens<T, T[K1][K2][K3][K4]>
export function lensFromPath<T, K1 extends keyof T, K2 extends keyof T[K1], K3 extends keyof T[K1][K2]>(path: [K1, K2, K3]): Lens<T, T[K1][K2][K3]>
export function lensFromPath<T, K1 extends keyof T, K2 extends keyof T[K1]>(path: [K1, K2]): Lens<T, T[K1][K2]>
export function lensFromPath<T, K1 extends keyof T>(path: [K1]): Lens<T, T[K1]>
export function lensFromPath(path: Array<any>) {
const lens = Lens.fromProp<any, any>(path[0])
return path.slice(1).reduce((acc, prop) => acc.compose(Lens.fromProp<any, any>(prop)), lens)
}

/*
Laws:
1. get(set(a, s)) = a
Expand All @@ -69,6 +84,8 @@ export class Lens<S, A> {
)
}

static fromPath = lensFromPath

modify(f: (a: A) => A, s: S): S {
return this.set(f(this.get(s)), s)
}
Expand Down Expand Up @@ -169,10 +186,10 @@ export class Optional<S, A> {
/** view a Options as a Traversal */
asTraversal(): Traversal<S, A> {
return new Traversal<S, A>(
<F>(applicative: StaticApplicative<F>, f: (a: A) => HKT<F, A>, s: S): HKT<F, S> =>
<F extends HKTS>(applicative: StaticApplicative<F>, f: (a: A) => HKT<A>[F], s: S): HKT<S>[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))
)
)
}
Expand All @@ -181,7 +198,7 @@ export class Optional<S, A> {
export class Traversal<S, A> {
constructor(
// Van Laarhoven representation
public modifyF: <F>(applicative: StaticApplicative<F>, f: (a: A) => HKT<F, A>, s: S) => HKT<F, S>
public modifyF: <F extends HKTS>(applicative: StaticApplicative<F>, f: (a: A) => HKT<A>[F], s: S) => HKT<S>[F]
){}

modify(f: (a: A) => A, s: S): S {
Expand All @@ -195,7 +212,7 @@ export class Traversal<S, A> {
/** compose a Traversal with a Traversal */
compose<B>(ab: Traversal<A, B>): Traversal<S, B> {
return new Traversal<S, B>(
<F>(applicative: StaticApplicative<F>, f: (a: B) => HKT<F, B>, s: S): HKT<F, S> =>
<F extends HKTS>(applicative: StaticApplicative<F>, f: (a: B) => HKT<B>[F], s: S): HKT<S>[F] =>
this.modifyF(applicative, a => ab.modifyF(applicative, f, a), s)
)
}
Expand All @@ -210,10 +227,10 @@ export class Traversal<S, A> {
}

/** create a Traversal from a Traversable */
export function fromTraversable<T, A>(traversable: StaticTraversable<T>): Traversal<HKT<T, A>, A> {
return new Traversal<HKT<T, A>, A>(
<F>(applicative: StaticApplicative<F>, f: (a: A) => HKT<F, A>, s: HKT<T, A>): HKT<F, HKT<T, A>> =>
traversable.traverse(applicative, f, s)
export function fromTraversable<T extends HKTS, A>(traversable: StaticTraversable<T>): Traversal<HKT<A>[T], A> {
return new Traversal<HKT<A>[T], A>(
<F extends HKTS>(applicative: StaticApplicative<F>, f: (a: A) => HKT<A>[F], s: HKT<A>[T]): HKT<HKT<A>[T]>[F] =>
traversable.traverse<F>(applicative)<A, A>(f, s)
)
}

Expand Down Expand Up @@ -258,9 +275,9 @@ export class Fold<S, A> {
}

/** create a Fold from a Foldable */
export function fromFoldable<F, A>(fold: StaticFoldable<F>): Fold<HKT<F, A>, A> {
export function fromFoldable<F extends HKTS, A>(fold: StaticFoldable<F>): Fold<HKT<A>[F], A> {
return new Fold(
<M>(monoid: StaticMonoid<M>, f: (a: A) => M, s: HKT<F, A>): M =>
ops.foldMapS(fold, monoid, f, s)
<M>(monoid: StaticMonoid<M>, f: (a: A) => M, s: HKT<A>[F]): M =>
foldMap(fold, monoid, f, s)
)
}
34 changes: 34 additions & 0 deletions test/Lens.ts
Original file line number Diff line number Diff line change
@@ -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<Employee, 'company', 'address', 'street', 'name'>(['company', 'address', 'street', 'name'])
assert.strictEqual(lens.modify(capitalize, employee).company.address.street.name, 'High street')
})

})
4 changes: 2 additions & 2 deletions test/helpers.ts
Original file line number Diff line number Diff line change
@@ -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<A>(x: HKTOption<A>, y: HKTOption<A>) {
export function eqOptions<A>(x: Option<A>, y: Option<A>) {
if (isSome(x) && isSome(y)) {
assert.deepEqual(x.value, y.value)
} else {
Expand Down

0 comments on commit b8e05f4

Please sign in to comment.