-
Notifications
You must be signed in to change notification settings - Fork 1
/
Functions.hs
146 lines (119 loc) · 3.44 KB
/
Functions.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
{-# LANGUAGE DuplicateRecordFields #-}
module Wakame.Examples.Functions where
import Prelude
import GHC.Generics
import Wakame
-- * Data types which is used in this module
data Point = Point { x :: Double, y :: Double }
deriving (Eq, Show, Generic)
data Position = Position { x :: !Double, y :: !Double }
deriving (Eq, Show, Generic)
data Point3d = Point3d { x :: Double, y :: Double, z :: Double }
deriving (Eq, Show, Generic)
data Tniop = Tniop { y :: Double, x :: Double }
deriving (Eq, Show, Generic)
-- | An instance of @Point@ data type.
pt :: Point
pt = Point 1.2 8.3
-- | Review of @Generic@
-- Let's review the basic functionality of an instance of @Generic@ typeclass.
--
-- `from` function converts a data to its generic representation.
-- >>> from pt
-- M1 {unM1 = M1 {unM1 = M1 {unM1 = K1 {unK1 = 1.2}} :*: M1 {unM1 = K1 {unK1 = 8.3}}}}
--
-- You can construct a representation value by hand.
-- >>> rep = M1 $ M1 $ M1 (K1 1.2) :*: M1 (K1 8.3)
--
-- From the representation value, `to` function converts back to @Point@.
-- >>> to rep :: Point
-- Point {x = 1.2, y = 8.3}
--
-- Since @Position@ has the same representation, `to` works as well.
-- >>> to rep :: Position
-- Position {x = 1.2, y = 8.3}
--
-- And even so to Tuple.
-- >>> to rep :: (Double, Double)
-- (1.2,8.3)
--
-- It is sensitive to the order of elements.
-- >>> to rep :: Tniop
-- Tniop {y = 1.2, x = 8.3}
-- * Examples of record value manipulation
-- | Round trip of Point to/from Row
-- >>> fromRow @Point $ toRow pt
-- Point {x = 1.2, y = 8.3}
-- | Converting Point to Point3d by adding z field
-- >>> fromRow @Point3d $ union (toRow pt) (toRow (keyed @"z" 42.0))
-- Point3d {x = 1.2, y = 8.3, z = 42.0}
-- | Interfacing optional fields
--
-- `f` fills absent fileds with `0.0`.
-- >>> f $ toRow (keyed @"x" 3.5)
-- Point {x = 3.5, y = 0.0}
--
-- Ignores extra fields.
-- >>> f $ toRow (keyed @"y" 4.3, keyed @"z" 1.6)
-- Point {x = 0.0, y = 4.3}
--
-- Converts from another type of data.
-- >>> f $ toRow $ Point3d 3.5 4.3 1.6
-- Point {x = 3.5, y = 4.3}
--
-- Returns fully default value when `()` is given.
-- >>> f $ toRow ()
-- Point {x = 0.0, y = 0.0}
--
-- Works nicely no matter how the order of fields is.
-- >>> f $ toRow $ Tniop 4.3 3.5
-- Point {x = 3.5, y = 4.3}
f ::
( Merge props OfPoint OfPoint
) => Row props -> Point
f props = fromRow $ merge props def
where
def :: Row OfPoint
def = toRow $ Point 0.0 0.0
type OfPoint = Of Point
-- | Filling common fields if existing
--
-- >>> g (Person "Luke" "1979-01-01") (Timestamp "2020-04-01" "2020-04-02") :: Person
-- Person {name = "Luke", created_at = "2020-04-01"}
--
-- >>> g (Point 3.5 4.3) (Timestamp "2020-04-01" "2020-04-02") :: Point
-- Point {x = 3.5, y = 4.3}
data Timestamp =
Timestamp
{ created_at :: String
, updated_at :: String
}
deriving (Eq, Show, Generic)
data Person =
Person
{ name :: String
, created_at :: String
}
deriving (Eq, Show, Generic)
g ::
( IsRow a
, IsRow b
, IsRow c
, Merge (Of b) (Of a) (Of c)
) => a -> b -> c
g x y = fromRow $ nub $ union (toRow y) (toRow x)
-- | Rejecting certain fields
--
-- `h` is the same as `g` except rejecting the first argument of having a field "y".
-- >>> h (Point 3.5 4.3) (Timestamp "2020-04-01" "2020-04-02") :: Point
-- ...
-- ... Couldn't match type ...
-- ...
h ::
( IsRow a
, IsRow b
, IsRow c
, Merge (Of b) (Of a) (Of c)
, Lacks "y" (Of a)
) => a -> b -> c
h x y = fromRow $ nub $ union (toRow y) (toRow x)