@@ -3,7 +3,8 @@
33### Version 0.4
44
55Version 0.4 of strengthens Squeal's type system, adds
6−support for multidimensional arrays, improves `with` statements,
6+support for multidimensional arrays, improves support
7+for container type, improves `with` statements,
78improves runtime exceptions, accomodates SQL's three-valued logic,
89adds subquery expressions, and adds table and view type expressions.
910
@@ -146,6 +147,120 @@ void . withConnection "host=localhost port=5432 dbname=exampledb" $ do
146147True
147148```
148149
150+**Containers**
151+
152+Squeal aims to provide a correspondence between Haskell types and Postgres types.
153+In particular, Haskell ADTs with nullary constructors can correspond to
154+Postgres enum types and Haskell record types can correspond to Postgres
155+composite types. However, it's not always obvious that that's how a user
156+will choose to store values of those types. So Squeal 0.4 introduces newtypes
157+whose purpose is to specify how a user wants to store values of a type.
158+
159+```Haskell
160+newtype Json hask = Json {getJson :: hask}
161+newtype Jsonb hask = Jsonb {getJsonb :: hask}
162+newtype Composite record = Composite {getComposite :: record}
163+newtype Enumerated enum = Enumerated {getEnumerated :: enum}
164+```
165+
166+Let's see an example:
167+
168+```Haskell
169+>>> data Schwarma = Beef | Lamb | Chicken deriving (Eq, Show, GHC.Generic)
170+>>> instance SOP.Generic Schwarma
171+>>> instance SOP.HasDatatypeInfo Schwarma
172+>>>
173+>>> data Person = Person {name :: Text, age :: Int32} deriving (Eq, Show, GHC.Generic)
174+>>> instance SOP.Generic Person
175+>>> instance SOP.HasDatatypeInfo Person
176+>>> instance Aeson.FromJSON Person
177+>>> instance Aeson.ToJSON Person
178+```
179+
180+We can create the equivalent Postgres types directly from their Haskell types.
181+
182+```Haskell
183+>>> :{
184+type Schema =
185+ '[ "schwarma" ::: 'Typedef (PG (Enumerated Schwarma))
186+ , "person" ::: 'Typedef (PG (Composite Person))
187+ ]
188+:}
189+
190+>>> :{
191+let
192+ setup :: Definition '[] Schema
193+ setup =
194+ createTypeEnumFrom @Schwarma #schwarma >>>
195+ createTypeCompositeFrom @Person #person
196+:}
197+```
198+
199+Let's demonstrate how to associate our Haskell types `Schwarma` and `Person`
200+with enumerated, composite or json types in Postgres. First create a Haskell
201+`Row` type using the `Enumerated`, `Composite` and `Json` newtypes as fields.
202+
203+```
204+>>> :{
205+data Row = Row
206+ { schwarma :: Enumerated Schwarma
207+ , person1 :: Composite Person
208+ , person2 :: Json Person
209+ } deriving (Eq, GHC.Generic)
210+:}
211+
212+>>> instance Generic Row
213+>>> instance HasDatatypeInfo Row
214+>>> :{
215+let
216+ input = Row
217+ (Enumerated Chicken)
218+ (Composite (Person "Faisal" 24))
219+ (Json (Person "Ahmad" 48))
220+:}
221+```
222+
223+Once again, define a round trip query.
224+
225+```Haskell
226+>>> :{
227+let
228+ roundTrip :: Query Schema (TuplePG Row) (RowPG Row)
229+ roundTrip = values_ $
230+ parameter @1 (typedef #schwarma) `as` #schwarma :*
231+ parameter @2 (typedef #person) `as` #person1 :*
232+ parameter @3 json `as` #person2
233+:}
234+```
235+
236+Finally, we can drop our type definitions.
237+
238+```Haskell
239+>>> :{
240+let
241+ teardown :: Definition Schema '[]
242+ teardown = dropType #schwarma >>> dropType #person
243+:}
244+```
245+
246+Now let's run it.
247+
248+```Haskell
249+>>> :{
250+let
251+ session = do
252+ result <- runQueryParams roundTrip input
253+ Just output <- firstRow result
254+ liftBase . print $ input == output
255+in
256+ void . withConnection "host=localhost port=5432 dbname=exampledb" $
257+ define setup
258+ & pqThen session
259+ & pqThen (define teardown)
260+:}
261+True
262+```
263+
149264**With**
150265
151266Squeal 0.3 supported WITH statements but there's a couple problems with them.