Skip to content

Commit 863c1c2

Browse files
committed
Probably a decent draft
1 parent 8743e76 commit 863c1c2

File tree

2 files changed

+73
-49
lines changed

2 files changed

+73
-49
lines changed

examples/README.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,19 @@ top of an example module will have a comment like this:
1414
1515
import PrismsForSumTypes
1616
import Data.Lens
17-
17+
...
1818
-}
1919
```
2020

21+
(Tip: because each line is self-contained, you don't need to use `:paste`.)
22+
2123
The modules contain both definitions of optics and sample uses. The
2224
sample uses are executable code that look like this:
2325

2426
```purescript
2527
s1 :: Maybe Color
26-
s1 = preview solidFocus (Solid Color.white) -- (Just rgba 255 255 255 1.0)
28+
s1 = preview solidFocus (Solid Color.white)
29+
-- (Just rgba 255 255 255 1.0)
2730
```
2831

2932
Why?
@@ -32,14 +35,19 @@ Why?
3235
`:paste`. (I usually don't copy the `s1 =` because I prefer seeing
3336
the results immediately. All types in the examples implement `show`,
3437
making that painless.)
35-
38+
3639
2. The sample uses are executable code so that the compiler checks
3740
them and -- more importantly -- so that they stand out from the
38-
surrounding commentary. That makes an example more easily scannable
39-
when you're refreshing your memory about a particular usage
41+
surrounding commentary. That makes the files more easily scannable
42+
when you're refreshing your memory about a particular function
4043
(assuming you use a syntax highlighter).
4144

4245
3. The name-value bindings (like `s1`) are clutter, but required by the
4346
compiler. Most of the type annotations are also clutter, but are required
4447
to prevent compiler warnings in, for example, `pulp build`.
4548

49+
4. The expected value is on a new line, rather than appended to the
50+
end of the second line because it's visually easier to scan down a line
51+
than scan right for a `--` pattern.
52+
53+
"Comfort is the key." -- Prof. Dawn Marick, DVM, MS, DACVIM (LAIM)

examples/src/PrismsForSumTypes.purs

Lines changed: 60 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ import Data.Either (Either(..))
3131

3232
newtype Percent = Percent Number
3333
data Point = Point Number Number
34-
35-
data Fill
34+
35+
data Fill -- think of a paint program filling a shape
3636
= Solid Color
3737
| LinearGradient Color Color Percent
3838
| RadialGradient Color Color Point
@@ -50,7 +50,7 @@ fillRadial :: Fill
5050
fillRadial = RadialGradient Color.white Color.black $ Point 1.0 3.4
5151

5252

53-
{------ Eq and Show come in handy ------}
53+
{------ Eq and Show come in handy ------}
5454

5555
derive instance genericPercent :: Generic Percent _
5656
instance eqPercent :: Eq Percent where
@@ -72,11 +72,10 @@ instance showFill :: Show Fill where
7272

7373

7474
{------ Making prisms with Maybe and `prism'` ------}
75-
{------ Basic usage: `preview`, `review`, `is`, and `isn't` ------}
7675

77-
-- Two function arguments: a data constructor for the type in
78-
-- question, plus one that converts your desired case to a
79-
-- `Just <wrapped values>` or `Nothing`.
76+
-- `prism'` (note the apostrophe) takes two functions. One is a data
77+
-- constructor for the type in question. The other converts your
78+
-- desired case to a `Just <wrapped values>` or `Nothing`.
8079

8180
solidFocus :: Prism' Fill Color
8281
solidFocus = prism' constructor focus
@@ -89,19 +88,24 @@ solidFocus = prism' constructor focus
8988
-- In real life, you might abbreviate the above to this:
9089

9190
solidFocus' :: Prism' Fill Color
92-
solidFocus' = prism' Solid case _ of
91+
solidFocus' = prism' Solid case _ of
9392
Solid color -> Just color
9493
_ -> Nothing
9594

9695
-- ... but being painfully explicit is better for examples.
9796

97+
98+
{------ Basic usage: `preview`, `review`, `is`, and `isn't` ------}
99+
98100
-- After building a prism, you focus in on a color with `preview`:
99101

100102
s1 :: Maybe Color
101-
s1 = preview solidFocus (Solid Color.white) -- (Just rgba 255 255 255 1.0)
103+
s1 = preview solidFocus (Solid Color.white)
104+
-- (Just rgba 255 255 255 1.0)
102105

103106
s2 :: Maybe Color
104-
s2 = preview solidFocus fillRadial -- Nothing
107+
s2 = preview solidFocus fillRadial
108+
-- Nothing
105109

106110
-- ... or you can create a Fill from a color with `review`:
107111

@@ -112,28 +116,32 @@ s3 = review solidFocus Color.white
112116
-- ... or you can ask whether a given value matches the prism:
113117

114118
s4 :: Boolean
115-
s4 = is solidFocus (Solid Color.white) :: Boolean -- true
119+
s4 = is solidFocus (Solid Color.white) :: Boolean
120+
-- true
116121

117122
s5 :: Boolean
118-
s5 = isn't solidFocus (Solid Color.white) :: Boolean -- false
119-
123+
s5 = isn't solidFocus (Solid Color.white) :: Boolean
124+
-- false
120125

121126

122127
{------ Making prisms with Either and `prism` ------}
123128

124-
-- Since `LinearGradient` wraps multiple values, they need to be
125-
-- rewrapped for `preview`. I'll use a record.
126-
129+
-- Since `LinearGradient` wraps two colors and a percent, they need to
130+
-- be bundled together into a single value for `preview` to
131+
-- return. I'll use a record:
127132

128133
type LinearInterchange =
129134
{ color1 :: Color
130135
, color2 :: Color
131136
, percent :: Percent
132137
}
133138

134-
-- When making a prism with `prism`, the "focus" function returns
135-
-- either the selected value (as `Right`) or the entire argument (as
136-
-- `Left`).
139+
-- (In real life, you probably wouldn't have to give such a record a
140+
-- type alias.)
141+
142+
-- When making a prism with `prism` (no apostrophe), the "focus"
143+
-- function returns either the selected value (as a `Right`) or the
144+
-- entire argument (as a `Left`).
137145

138146
linearFocus :: Prism' Fill LinearInterchange
139147
linearFocus = prism constructor focus
@@ -142,11 +150,11 @@ linearFocus = prism constructor focus
142150
LinearGradient color1 color2 percent
143151
focus = case _ of
144152
LinearGradient color1 color2 percent ->
145-
Right {color1, color2, percent}
146-
fill ->
147-
Left fill
148-
149-
-- Even though made differently, this prism is used the same way:
153+
Right {color1, color2, percent}
154+
otherFill ->
155+
Left otherFill
156+
157+
-- Even though made differently, this `linearFocus` is used the same way:
150158

151159
l1 :: String
152160
l1 = preview linearFocus fillBlackToWhite # maybe "!" showRecord
@@ -158,6 +166,7 @@ l2 = review linearFocus { color1 : Color.black
158166
, percent : Percent 33.3
159167
}
160168

169+
161170
{------ Constructing more specific prisms ------}
162171

163172
-- `only` is used to check for a specific value:
@@ -166,54 +175,61 @@ whiteToBlackFocus :: Prism' Fill Unit
166175
whiteToBlackFocus = only fillWhiteToBlack
167176

168177
o1 :: Boolean
169-
o1 = is whiteToBlackFocus fillWhiteToBlack :: Boolean -- true
178+
o1 = is whiteToBlackFocus fillWhiteToBlack :: Boolean
179+
-- true
170180

171181
o2 :: Boolean
172-
o2 = is whiteToBlackFocus fillBlackToWhite :: Boolean -- false
182+
o2 = is whiteToBlackFocus fillBlackToWhite :: Boolean
183+
-- false
173184

174185
o3 :: Boolean
175-
o3 = is whiteToBlackFocus fillRadial :: Boolean -- false
176-
186+
o3 = is whiteToBlackFocus fillRadial :: Boolean
187+
-- false
177188

178189
-- `nearly` is typically used to look for a specific case (like other
179-
-- prisms), but accepting only values that are close to some target
180-
-- value. It takes two values: a reference value (which, with sum types,
181-
-- serves to declare the focus case, and a predicate that determines
182-
-- whether the wrapped value(s) is close enough to what's desired.
190+
-- prisms), but also accepts only values that are close to some target
191+
-- value. It takes two values: a reference value, and a predicate that
192+
-- determines whether the wrapped value(s) are close enough to the
193+
-- reference. Note that the predicate takes the "whole" type (here,
194+
-- `Fill`), not the unwrapped values inside the case you care about.
183195

184196
-- In this example, we want to focus on solid colors that are "bright
185-
-- enough".
197+
-- enough."
186198

187199
brightSolidFocus :: Prism' Fill Unit
188200
brightSolidFocus = nearly (Solid referenceColor) predicate
189201
where
190202
referenceColor = Color.graytone 0.8
191203
predicate = case _ of
192-
Solid color ->
204+
Solid color ->
193205
Color.brightness color >= Color.brightness referenceColor
194206
_ ->
195207
false
196208

197-
-- Because a `nearly` prism focuses on `Unit`, you get only two values
198-
-- from `preview`:
199-
209+
-- Because a `nearly` prism focuses into `Unit`, you can get only two
210+
-- values from `preview`:
200211

201212
n1 :: Maybe Unit
202-
n1 = preview brightSolidFocus (Solid Color.white) -- (Just unit)
213+
n1 = preview brightSolidFocus (Solid Color.white)
214+
-- (Just unit)
203215

204216
n2 :: Maybe Unit
205-
n2 = preview brightSolidFocus (Solid Color.black) -- Nothing
217+
n2 = preview brightSolidFocus (Solid Color.black)
218+
-- Nothing
206219

207220
n3 :: Maybe Unit
208-
n3 = preview brightSolidFocus NoFill -- Nothing
221+
n3 = preview brightSolidFocus NoFill
222+
-- Nothing
223+
209224

210-
211-
-- So you probably want to use `is` or `isn't`:
225+
-- ... so you probably want to use `is` or `isn't`:
212226

213227
n4 :: Boolean
214-
n4 = is brightSolidFocus (Solid Color.white) :: Boolean -- true
228+
n4 = is brightSolidFocus (Solid Color.white) :: Boolean
229+
-- true
215230

216231
-- You can recover the reference value with `review`:
217232

218233
n5 :: Fill
219-
n5 = review brightSolidFocus unit -- (Solid rgba 204 204 204 1.0)
234+
n5 = review brightSolidFocus unit
235+
-- (Solid rgba 204 204 204 1.0)

0 commit comments

Comments
 (0)