Skip to content

Commit 83b58b1

Browse files
committed
Prism examples
1 parent 9dd4432 commit 83b58b1

File tree

3 files changed

+234
-0
lines changed

3 files changed

+234
-0
lines changed

examples/.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/bower_components/
2+
/node_modules/
3+
/.pulp-cache/
4+
/output/
5+
/generated-docs/
6+
/.psc*
7+
/.purs*
8+
/.psa*

examples/bower.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "examples",
3+
"ignore": [
4+
"**/.*",
5+
"node_modules",
6+
"bower_components",
7+
"output"
8+
],
9+
"dependencies": {
10+
"purescript-prelude": "^3.3.0",
11+
"purescript-console": "^3.0.0",
12+
"purescript-profunctor-lenses": "^3.8.0",
13+
"purescript-record-show": "^0.4.0",
14+
"purescript-colors": "^4.3.0",
15+
"purescript-generics-rep": "^5.4.0",
16+
"purescript-numbers": "^5.0.0"
17+
},
18+
"devDependencies": {
19+
"purescript-psci-support": "^3.0.0"
20+
}
21+
}
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
module PrismsForSumTypes where
2+
3+
{-
4+
5+
If you want to try out examples, paste the following into the repl.
6+
7+
import Data.Maybe
8+
9+
import Data.Lens
10+
import Data.Lens as Lens
11+
import Data.Lens.Prism
12+
import Color as Color
13+
import Data.Record.ShowRecord (showRecord)
14+
import PrismsForSumTypes
15+
import Data.Number.Approximate
16+
17+
-}
18+
19+
import Prelude
20+
import Data.Maybe
21+
import Data.Either
22+
23+
import Data.Lens
24+
import Data.Lens as Lens
25+
import Color (Color)
26+
import Color as Color
27+
import Data.Tuple
28+
import Data.Generic.Rep
29+
import Data.Generic.Rep.Eq as GEq
30+
import Data.Generic.Rep.Show as GShow
31+
import Data.Record.ShowRecord (showRecord)
32+
import Data.Number.Approximate
33+
34+
{- The types in question -}
35+
36+
newtype Percent = Percent Number
37+
data Point = Point Number Number
38+
39+
data Fill
40+
= Solid Color
41+
| LinearGradient Color Color Percent
42+
| RadialGradient Color Color Point
43+
| NoFill
44+
45+
{- Some samples to work with -}
46+
47+
fillWhite :: Fill
48+
fillWhite = Solid Color.white
49+
50+
fillBlackToWhite :: Fill
51+
fillBlackToWhite = LinearGradient Color.black Color.white $ Percent 3.3
52+
53+
fillWhiteToBlack :: Fill
54+
fillWhiteToBlack = LinearGradient Color.white Color.black $ Percent 3.3
55+
56+
fillRadial :: Fill
57+
fillRadial = RadialGradient Color.white Color.black $ Point 1.0 3.4
58+
59+
60+
{- Eq and Show come in handy -}
61+
62+
derive instance genericPercent :: Generic Percent _
63+
instance eqPercent :: Eq Percent where
64+
eq = GEq.genericEq
65+
instance showPercent :: Show Percent where
66+
show (Percent f) = "(" <> show f <> "%)"
67+
68+
derive instance genericPoint :: Generic Point _
69+
instance eqPoint :: Eq Point where
70+
eq = GEq.genericEq
71+
instance showPoint :: Show Point where
72+
show (Point x y) = "(" <> show x <> ", " <> show y <> ")"
73+
74+
derive instance genericFill :: Generic Fill _
75+
instance eqFill :: Eq Fill where
76+
eq = GEq.genericEq
77+
instance showFill :: Show Fill where
78+
show x = GShow.genericShow x
79+
80+
81+
{- Making prisms with Maybe and `prism'`-}
82+
{- Basic usage -}
83+
84+
-- Two function arguments: a data constructor for the type in
85+
-- question, plus one that "substitutes" your desired case with `Just`
86+
-- and converts all other values to `Nothing`.
87+
88+
solidFocus :: Prism' Fill Color
89+
solidFocus = prism' constructor focus
90+
where
91+
constructor = Solid
92+
focus fill = case fill of
93+
Solid color -> Just color
94+
_ -> Nothing
95+
96+
-- In real life, you might abbreviate the above to this:
97+
98+
solidFocus' = prism' Solid case _ of
99+
Solid color -> Just color
100+
_ -> Nothing
101+
102+
-- ... but being painfully explicit is better for examples.
103+
104+
-- After building a prism, you focus in on a color with `preview`:
105+
106+
s1 = preview solidFocus (Solid Color.white) -- (Just rgba 255 255 255 1.0)
107+
s2 = preview solidFocus fillRadial -- Nothing
108+
109+
-- ... or you can create a Fill from a color with `review`:
110+
111+
s3 = review solidFocus Color.white
112+
-- (Solid rgba 255 255 255 1.0)
113+
114+
-- ... or you can ask whether a given value matches the prism:
115+
116+
s4 = is solidFocus fillWhite :: Boolean -- true
117+
s5 = isn't solidFocus fillWhite :: Boolean -- false
118+
119+
120+
121+
{- Making prisms with Either and `prism` -}
122+
123+
-- Since `LinearGradient` wraps multiple values, they need to be
124+
-- rewrapped for `preview`. I'll use a record.
125+
126+
127+
type LinearInterchange =
128+
{ color1 :: Color
129+
, color2 :: Color
130+
, percent :: Percent
131+
}
132+
133+
-- When making a prism with `prism`, the "focus" function returns
134+
-- either the selected value (as `Right`) or the entire argument (as
135+
-- `Left`).
136+
137+
linearFocus :: Prism' Fill LinearInterchange
138+
linearFocus = prism constructor focus
139+
where
140+
constructor {color1, color2, percent} =
141+
LinearGradient color1 color2 percent
142+
focus = case _ of
143+
LinearGradient color1 color2 percent ->
144+
Right {color1, color2, percent}
145+
fill ->
146+
Left fill
147+
148+
-- Even though made differently, this prism is used the same way:
149+
150+
l1 = preview linearFocus fillBlackToWhite # maybe "!" showRecord
151+
-- "{ color1: rgba 0 0 0 1.0, color2: rgba 255 255 255 1.0, percent: (3.3%) }"
152+
153+
l2 = review linearFocus { color1 : Color.black
154+
, color2 : Color.white
155+
, percent : Percent 33.3
156+
}
157+
158+
{- Constructing more specific prisms -}
159+
160+
-- `only` is used to look for a specific value:
161+
162+
whiteSolid = only (Solid Color.white)
163+
164+
o1 = is whiteSolid (Solid Color.white) :: Boolean -- true
165+
o2 = is whiteSolid (Solid Color.black) :: Boolean -- false
166+
o3 = is whiteSolid fillRadial :: Boolean -- false
167+
168+
169+
-- `nearly` is typically used to look for a specific case (like other
170+
-- prisms), but accepting only values that are close to some target
171+
-- value. It takes two values: a reference value (which, with sum types,
172+
-- serves to declare the focus case, and a predicate that determines
173+
-- whether the wrapped value(s) is close enough to what's desired.
174+
175+
-- In this example, we want to focus on solid colors that are "bright
176+
-- enough".
177+
178+
brightSolid :: Prism' Fill Unit
179+
brightSolid = nearly (Solid referenceColor) predicate
180+
where
181+
referenceColor = Color.graytone 0.8
182+
predicate = case _ of
183+
Solid color ->
184+
Color.brightness color >= Color.brightness referenceColor
185+
_ ->
186+
false
187+
188+
-- Because a `nearly` prism focuses on `Unit`, you get only two values
189+
-- from `preview`:
190+
191+
192+
n1 = preview brightSolid (Solid Color.white) -- (Just unit)
193+
194+
n2 = preview brightSolid (Solid Color.black) -- Nothing
195+
196+
n3 = preview brightSolid NoFill -- Nothing
197+
198+
199+
-- So you probably want to use `is` or `isn't`:
200+
201+
n4 = is brightSolid (Solid Color.white) :: Boolean -- true
202+
203+
-- You can recover the reference value with `review`:
204+
205+
n5 = review brightSolid unit -- (Solid rgba 204 204 204 1.0)

0 commit comments

Comments
 (0)