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

Commit 9e5780f

Browse files
committed
Merge pull request #10 from purescript-contrib/split
Split
2 parents 39e7dc0 + 3dfb12e commit 9e5780f

37 files changed

Lines changed: 500 additions & 2120 deletions

README.md

Lines changed: 101 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
# PureScript-Lens
22

3-
This is an indirect port of [@ekmett][@ekmett]'s [lens][lens] library in PureScript.
4-
These are just [van Laarhoven][van Laarhoven] lenses.
3+
This started as an indirect port of [@ekmett][@ekmett]'s [lens][lens] library in PureScript.
4+
It has since been broken apart and simplified substantially.
5+
The core idea is that these are just [van Laarhoven][van Laarhoven] lenses.
56
Read about them as they were called [functional references][functional references].
67
[SPJ][SPJ] gave a brilliant introductory talk about them [here][SPJ-talk].
78
If you're confused, I'd recommend [SPJ][SPJ]'s talk first.
89
[@ekmett][@ekmett] has talked about them many times, [here's one][ekmett-talk].
9-
For more documentation, you should look at [lens][lens].
10-
11-
__Disclaimer:__
12-
Not much is here at the moment, and it's got some problems.
13-
It's mostly just the `Getter` and `Setter` part of [lens][lens].
14-
The more basic examples work. But, not expect the full expressivity of the actual [lens][lens] library.
10+
For more documentation, you should look at [lens][lens] and any of the lens resources online.
1511

1612
## Installation
1713

@@ -25,91 +21,132 @@ to install it.
2521

2622
## Usage
2723

28-
You *should* be able to import just `Control.Lens` and have most of what you need. See each directory for a summary of the available functions.
24+
You *should* be able to import just `Optic.Lens` and have most of what you need.
25+
See each directory for a summary of the available functions.
2926

3027
There is no TemplateHaskell like syntax so you must define each lens individually.
28+
Or make use of [purescript-refractor][purescript-refractor], which has predefined lenses and prisms.
29+
30+
----------
3131

3232
### Warning
3333
Currently, [PureScript][PureScript] doesn't infer constraints [#202][#202].
3434
If you can fix it, please help out with it.
3535

3636
What this means for you is that you have to annotate your lens/prism/traversal/whatever with a type.
3737
This might sound or look hairy, but the types aren't that hard to figure out and it'll go quite a way to show you that there's no magic going on in this library.
38-
They're almost all just type synonyms actually.
38+
They're all just type synonyms actually.
39+
40+
----------
41+
42+
There are two main types in this library: `Lens` and `Prism`.
43+
Both propose a way for "getting" and "setting" values in a data type.
44+
`Lens` is for working with product types (`Tuple`, records, fields in a data type).
45+
`Prism` is for working with sum types (`Maybe`, `Either`, etc).
46+
Each type proposes some way to look at a specific part of a data type.
47+
48+
With `Lens`, it proposes a way to look at one portion of a product type.
49+
E.g. the first field of a tuple, or the `foo` field of a record.
3950

40-
One way to think of the `Lens` type is to think of it as saying you want a lens from some structure to a piece of that structure.
51+
With `Prism`, it proposes a way to look at one side of a sum type.
52+
E.g. the `Left` side of an `Either`, or the `Nothing` side of a `Maybe`.
53+
54+
For almost all of the types provided there are simple versions and more general versions. Using `Lens` as the example.
4155

4256
`LensP s a` is the simple type when you don't need to change the type of your structure.
4357

44-
`Lens s t a b` is the type when you need to change the type of your structure,
58+
`Lens s t a b` is the type when you may want to change the type of your structure.
4559

4660
For example:
4761

48-
```haskell
62+
```purescript
4963
data Foo = Bar String Number Boolean
5064
5165
fooNum :: LensP Foo Number
5266
fooNum = lens (\(Bar _ n _) -> n) (\(Bar s _ b) n -> Bar s n b)
5367
```
5468

69+
This type and implementation states that the function `fooNum` is a `Lens` from the data type `Foo` to the `Number` field of it.
70+
Since it is a simple type, it does not change the type of the `Number` field or change the type of `Foo`.
71+
5572
or
5673

57-
```haskell
58-
_foo :: forall b r a t s. Lens {foo :: a | r} {foo :: b | r} a b
59-
_foo = lens (\o -> o.foo) (\o x -> o{foo = x})
74+
```purescript
75+
foo :: forall b r a t s. Lens {foo :: a | r} {foo :: b | r} a b
76+
foo = lens (\o -> o.foo) (\o x -> o{foo = x})
6077
```
6178

62-
## Examples
79+
This type and implementation states that the function `foo` is a `Lens` from any record with at least a `foo` field of type `a` to any record with at least a `foo` field of type `b`.
6380

64-
N.B.
65-
`(~)` is an infix version of `Tuple`.
81+
So, what are the type synonyms? Some examples are:
6682

67-
It's available in `Control.Lens.Tuple`,
68-
so it should come when you import `Control.Lens`.
69-
It's right associative so beware of what structure it will create.
83+
```purescript
84+
type Lens s t a b = forall f. (Functor f) => (a -> f b) -> s -> f t
85+
type LensP s a = Lens s s a a
7086
71-
`(..)` is `(<<<)` purely for aesthetic reasons.
87+
type Prism s t a b = forall f p. (Applicative f, Choice p) => p a (f b) -> p s (f t)
88+
type PrismP s a = Prism s s a a
89+
```
7290

73-
`foo..bar..baz..quux` looks better and reads easier than `foo<<<bar<<<baz<<<quux`.
91+
These might seem scary, especially `Prism`, but if you squint at them properly, they look very familiar.
7492

75-
```haskell
76-
purescript-lens git:(master) psci src/**/*.purs bower_components/purescript-*/src/**/*.purs
77-
____ ____ _ _
78-
| _ \ _ _ _ __ ___/ ___| ___ _ __(_)_ __ | |_
79-
| |_) | | | | '__/ _ \___ \ / __| '__| | '_ \| __|
80-
| __/| |_| | | | __/___) | (__| | | | |_) | |_
81-
|_| \__,_|_| \___|____/ \___|_| |_| .__/ \__|
82-
|_|
93+
Let's take `Lens`, for example, and instantiate `s = [a], t = [b]`.
8394

84-
:? shows help
95+
Then we have some type: `forall f. (Functor f) => (a -> f b) -> [a] -> f [b]`.
8596

86-
Expressions are terminated using Ctrl+D
87-
> :i Control.Lens
88-
> ("hello" ~ "world")^._2
89-
90-
...if it's the first compile, lens imports most of the universe here...
97+
Looks pretty close to `map` (from `Data.Array`).
98+
In fact, if we instantiate the `Functor` with `Identity`,
99+
we get the type isomorphic to `map`: `(a -> Identity b) -> [a] -> Identity [b]`.
91100

92-
"world"
101+
With some simple unwrapping, we actually have the type of `map`.
93102

94-
> set _2 42 ("hello" ~ "world")
95-
96-
Tuple ("hello") (42)
103+
What about `Prism`?
97104

98-
> ("hello" ~ ("world" ~ "!!!"))^._2.._1
99-
100-
"world"
105+
Let's instantiate the `Choice` to `(->)`:
106+
`type Prism s t a b = forall f. (Applicative f) => (a -> f b) -> s -> f t`
107+
This looks pretty close to `traverse`.
101108

102-
> set (_2.._1) 42 ("hello" ~ "world" ~ "!!!")
103-
104-
Tuple ("hello") (Tuple (42) ("!!!"))
109+
So, there's a bunch of similarities to the `Functor` hierarchy, and that's one of the points of this library.
110+
What these types synonyms allow is the ability to use similar idioms that work in the `Functor` hierarchy on containers that are not polymorphic in one variable.
111+
112+
An example of this is if you have some type: `data Foo = Foo Number`.
113+
There's no way to define a `Functor` instance for `Foo`,
114+
so you cannot use `(<$>), (<*>), (>>=), (=>>), pure, extract` and friends.
115+
But, it should be easy to see that it would be trivial to "map" over the `Number` contained within a `Foo`.
116+
117+
If you can define a lens for `Foo`, you can do just that
118+
119+
```purescript
120+
module Foo where
121+
122+
import Optic.Core ((*~), LensP())
123+
124+
data Foo = Foo Number
125+
126+
_Foo :: LensP Foo Number -- forall f. (Functor f) => (Number -> f Number) -> Foo -> f Foo
127+
_Foo f (Foo n) = Foo <$> f n
128+
129+
doubleFoo :: Foo -> Foo
130+
doubleFoo = _Foo *~ 2
105131
```
106132

107-
Or, when using records.
133+
Now, this is not necessarily the least verbose option for such a trivial example,
134+
but it is definitely one of the more general options.
135+
We were able to reuse plain old functions.
136+
If we have some deeply nested structure, it is much less verbose for that situation.
108137

109-
N.B. There's no show instance for records, so we just check the type instead.
138+
## Examples
110139

111-
```haskell
112-
purescript-lens git:(master) psci src/**/*.purs bower_components/purescript-*/src/**/*.purs
140+
N.B. `(..)` is `(<<<)` purely for aesthetic reasons.
141+
142+
`foo..bar..baz..quux` looks better and reads easier than `foo<<<bar<<<baz<<<quux`.
143+
144+
----------
145+
146+
Using [purescript-refractor][purescript-refractor] for some pre-defined lenses/prisms, we can run this session.
147+
148+
```purescript
149+
➜ purescript-lens git:(split) psci bower_components/purescript-*/src/**/*.purs
113150
____ ____ _ _
114151
| _ \ _ _ _ __ ___/ ___| ___ _ __(_)_ __ | |_
115152
| |_) | | | | '__/ _ \___ \ / __| '__| | '_ \| __|
@@ -120,33 +157,20 @@ N.B. There's no show instance for records, so we just check the type instead.
120157
:? shows help
121158
122159
Expressions are terminated using Ctrl+D
123-
> :i Control.Lens
124-
> let obj = {foo: {bar: {baz: true}}}
160+
> :i Optic.Core
161+
> :i Optic.Refractor.Lens
162+
> :i Data.Tuple
163+
> (Tuple "Hello" "World")^._2
125164
126-
> let _foo = lens (\o -> o.foo) (\o x -> o{foo = x})
127-
128-
> let _bar = lens (\o -> o.bar) (\o x -> o{bar = x})
129-
130-
> let _baz = lens (\o -> o.baz) (\o x -> o{baz = x})
165+
"World"
166+
167+
> (Tuple "Hello" "World") # _2.~42
131168
132-
> :t _foo
133-
forall t13 t14 t7. Control.Lens.Type.Lens { foo :: t14 | t13 } { foo :: t7 | t13 } t14 t7
134-
> :t _bar
135-
forall t20 t26 t27. Control.Lens.Type.Lens { bar :: t27 | t26 } { bar :: t20 | t26 } t27 t20
136-
> :t _baz
137-
forall t33 t39 t40. Control.Lens.Type.Lens { baz :: t40 | t39 } { baz :: t33 | t39 } t40 t33
138-
> obj^._foo.._bar.._baz
169+
Tuple ("Hello") (42)
170+
171+
> (Tuple "Hello" (Tuple "World" "!!!")) # _2.._1.~42
139172
140-
true
141-
142-
> :t obj
143-
{ foo :: { bar :: { baz :: Prim.Boolean } } }
144-
> :t _foo.._bar.._baz.~5 $ obj
145-
{ foo :: { bar :: { baz :: Prim.Number } } }
146-
> :t _foo.~"wat" $ obj
147-
{ foo :: Prim.String }
148-
> :t _foo.._bar.~[1,2,3] $ obj
149-
{ foo :: { bar :: [Prim.Number] } }
173+
Tuple ("Hello") (Tuple (42) ("!!!"))
150174
```
151175

152176
## Contributing
@@ -167,6 +191,7 @@ don't hesitate to slap me around and explain why it's wrong.
167191
[functional references]: http://twanvl.nl/blog/haskell/cps-functional-references
168192
[lens]: https://github.com/ekmett/lens/
169193
[PureScript]: https://github.com/purescript/purescript/
194+
[purescript-refractor]: https://github.com/joneshf/purescript-refractor/
170195
[SPJ]: http://research.microsoft.com/en-us/people/simonpj/
171196
[SPJ-talk]: https://skillsmatter.com/skillscasts/4251-lenses-compositional-data-access-and-manipulation
172197
[van Laarhoven]: http://twanvl.nl/index

bower.json

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,11 @@
2424
"tests"
2525
],
2626
"dependencies": {
27-
"purescript-transformers": "~0.3.0",
28-
"purescript-foldable-traversable": "~0.1.4",
29-
"purescript-tuples": "~0.2.2",
30-
"purescript-identity": "~0.1.0",
31-
"purescript-profunctor": "~0.0.1",
32-
"purescript-distributive": "~0.2.0",
33-
"purescript-contravariant": "~0.0.1",
34-
"purescript-const": "~0.1.1",
35-
"purescript-maps": "~0.1.3",
36-
"purescript-sets": "~0.1.0",
37-
"purescript-arrows": "~0.1.1",
38-
"purescript-enums": "~0.2.4",
39-
"purescript-bifunctors": "~0.0.6"
40-
},
41-
"devDependencies": {
42-
"purescript-easy-ffi": "~1.0.1",
43-
"purescript-strongcheck": "~0.4.14"
27+
"purescript-profunctor": "joneshf/purescript-profunctors#master",
28+
"purescript-distributive": "joneshf/purescript-distributive#~0.2.0",
29+
"purescript-const": "~0.1.1",
30+
"purescript-identity": "~0.1.0",
31+
"purescript-either": "~0.1.4",
32+
"purescript-maybe": "~0.2.1"
4433
}
4534
}

gulpfile.js

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,29 @@
11
'use strict'
22

33
var gulp = require('gulp')
4+
, bump = require('gulp-bump')
5+
, filter = require('gulp-filter')
6+
, git = require('gulp-git')
47
, purescript = require('gulp-purescript')
58
, run = require('gulp-run')
69
, runSequence = require('run-sequence')
10+
, tagVersion = require('gulp-tag-version')
711
;
812

913
var paths =
1014
{ src: 'src/**/*.purs'
1115
, bowerSrc: [ 'bower_components/purescript-*/src/**/*.purs'
1216
]
1317
, dest: ''
14-
, docs: { 'Control.Lens': { dest: 'src/Control/README.md'
15-
, src: 'src/Control/Lens.purs'
16-
}
17-
, 'Control.Lens.*': { dest: 'src/Control/Lens/README.md'
18-
, src: 'src/Control/Lens/*.purs'
19-
}
18+
, docs: { 'Optic.*': { dest: 'src/Optic/README.md'
19+
, src: 'src/Optic/*.purs'
20+
}
2021
}
2122
, test: 'test/**/*.purs'
2223
};
2324

2425
var options =
25-
{ test: { main: 'Test.Control.Lens'
26+
{ test: { main: 'Test.Optic'
2627
}
2728
};
2829

@@ -39,17 +40,41 @@ var compile = function(compiler, src, opts) {
3940

4041
function docs (target) {
4142
return function() {
42-
var docgen = purescript.docgen();
43-
docgen.on('error', function(e) {
43+
var pscDocs = purescript.pscDocs();
44+
pscDocs.on('error', function(e) {
4445
console.error(e.message);
45-
docgen.end();
46+
pscDocs.end();
4647
});
4748
return gulp.src(paths.docs[target].src)
48-
.pipe(docgen)
49+
.pipe(pscDocs)
4950
.pipe(gulp.dest(paths.docs[target].dest));
5051
}
5152
}
5253

54+
gulp.task('tag', function() {
55+
return gulp.src(['bower.json', 'package.json'])
56+
.pipe(git.commit('Update versions.'))
57+
.pipe(filter('bower.json'))
58+
.pipe(tagVersion());
59+
});
60+
61+
// For whatever reason, these cannot be factored out...
62+
gulp.task('bump-major', function() {
63+
return gulp.src(['bower.json', 'package.json'])
64+
.pipe(bump({type: 'major'}))
65+
.pipe(gulp.dest('./'));
66+
});
67+
gulp.task('bump-minor', function() {
68+
return gulp.src(['bower.json', 'package.json'])
69+
.pipe(bump({type: 'minor'}))
70+
.pipe(gulp.dest('./'));
71+
});
72+
gulp.task('bump-patch', function() {
73+
return gulp.src(['bower.json', 'package.json'])
74+
.pipe(bump({type: 'patch'}))
75+
.pipe(gulp.dest('./'));
76+
});
77+
5378
gulp.task('make', function() {
5479
return compile(purescript.pscMake, [paths.src], {});
5580
});
@@ -58,10 +83,9 @@ gulp.task('browser', function() {
5883
return compile(purescript.psc, [paths.src], {});
5984
});
6085

61-
gulp.task('docs-Control.Lens', docs('Control.Lens'));
62-
gulp.task('docs-Control.Lens.*', docs('Control.Lens.*'));
86+
gulp.task('docs-Optic.*', docs('Optic.*'));
6387

64-
gulp.task('docs', ['docs-Control.Lens', 'docs-Control.Lens.*']);
88+
gulp.task('docs', ['docs-Optic.*']);
6589

6690
gulp.task('watch-browser', function() {
6791
gulp.watch(paths.src, function() {runSequence('browser', 'docs')});

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,12 @@
1818
},
1919
"devDependencies": {
2020
"gulp": "~3.8.6",
21+
"gulp-bump": "^0.1.11",
22+
"gulp-filter": "^1.0.2",
23+
"gulp-git": "^0.5.5",
2124
"gulp-purescript": "^0.0.9",
2225
"gulp-run": "~1.6.4",
26+
"gulp-tag-version": "^1.2.1",
2327
"run-sequence": "^0.3.6"
2428
}
2529
}

0 commit comments

Comments
 (0)