@@ -1,46 +1,8 @@
5− <meta charset="utf-8">
7− @import url(https://fonts.googleapis.com/css?family=PT+Sans+Narrow);
8− @import url(https://fonts.googleapis.com/css?family=Montserrat:400,700,400italic);
9− @import url(https://fonts.googleapis.com/css?family=Ubuntu+Mono:400,700,400italic);
11− body { font-family: 'Montserrat'; }
13− font-family: 'PT Sans Narrow';
18− background-color: #ccc;
22− .remark-code, .remark-inline-code { font-family: 'Ubuntu Mono'; }
34− <textarea id="source">
41_A bridge between SQL and Haskell_
3_A bridge between SQL and Haskell_
43−.signature[2018/09/12 - by @Raveline]
5+by [@Raveline](https://github.com/raveline)
@@ -62,9 +24,6 @@ Using `postgresql-simple` you have a typical "Trial, error, despair" workflow:
68
27
@@ -85,9 +44,6 @@ However, it's not an ORM. There's no caching, lazy loading - you retain control
91# Part I. Schema & migration
47# Part I. Schema & migration
@@ -107,16 +63,13 @@ type Schema = '[ "mp" ::: 'Table MemberOfParliament
107 , "groupp" ::: 'Table ParliamentGroup ]
63 , "groupp" ::: 'Table ParliamentGroup ]
113−We need the `DataKinds` extension to be able to express heterogenous lists containing
68+> _note_ We need the `DataKinds` extension to be able to express heterogenous lists containing
114−specific types like this one.
69+> specific types like this one.
117−You can perfectly call your table "group" and not "groupp" even though it is a
71+> You can perfectly call your table "group" and not "groupp" even though it is a
118−keyword in SQL - Squeal queries will be properly escaped.
72+> keyword in SQL - Squeal queries will be properly escaped.
@@ -139,17 +92,13 @@ type ParliamentaryGroup =
140- Let's split constraints and column to study the syntax a bit more.
93- Let's split constraints and column to study the syntax a bit more.
145−We are using `:::` and `:=>` to quickly express associations when writing
97+> _note_ We are using `:::` and `:=>` to quickly express associations when writing
146−our schema. We need the `TypeOperators` extension.]
98+> our schema. We need the `TypeOperators` extension.]
150−class: center, middle
153
102
@@ -195,10 +144,10 @@ type MpCols =
196 * the type (obviously).
145 * the type (obviously).
200−.note[*
149+> _note_ GHC is already helping. If I named the "mp_id" column differently,
201−GHC is already helping. If I named the "mp_id" column differently, GHC would yell because I promised a primary key constraint on a column named `mp_id`, so there must be one.]
150+> GHC would yell because I promised a primary key constraint on a column named `mp_id`, so there must be one.]
@@ -208,7 +157,7 @@ GHC is already helping. If I named the "mp_id" column differently, GHC would yel
209- But before we play with this schema, we need to implement it.
158- But before we play with this schema, we need to implement it.
213```haskell
162```haskell
214setup :: Definition '[] Schema
163setup :: Definition '[] Schema
@@ -240,15 +189,14 @@ setup =
241- You use `>>>` to compose table creation.
190- You use `>>>` to compose table creation.
243−- Note that the compiler will catch any mistype between Schema and
192+> _note_ The compiler will catch any mistype between Schema and
244−definition; wrong nullability, wrong type, wrong name, etc.
193+> definition; wrong nullability, wrong type, wrong name, etc.
249−You'll also need `OverloadedLabels`, for naming stuff.
197+> _note_ You'll also need `OverloadedLabels`, for naming stuff.
250−This is mostly to avoid having to write manual proxies all
198+> This is mostly to avoid having to write manual proxies all
251−the time and for convenience.]
199+> the time and for convenience.
@@ -283,8 +231,8 @@ tearDown :: Definition Schema '[]
283tearDown = dropTable #mp >>> dropTable #groupp
231tearDown = dropTable #mp >>> dropTable #groupp
286−- Note that GHC will also detect the _proper_ order of what you typed in
234+> _note_ GHC will also detect the _proper_ order of what you typed in
287− downgrade and upgrade should there be any conflict (with foreign keys).
235+> downgrade and upgrade should there be any conflict (with foreign keys).
289```haskell
237```haskell
290initDB :: Migration IO '[] Schema
238initDB :: Migration IO '[] Schema
@@ -314,16 +262,10 @@ main = do
317−class: center, middle
320
265
324−class: center, middle
327# Part II. Insertions
269# Part II. Insertions
@@ -369,9 +311,8 @@ data MemberOfParliament =
369 , lastName :: Text }
311 , lastName :: Text }
372−- You'll note that we didn't use anything from Squeal.
314+> _note_ We didn't use anything from Squeal.
374−- The model can be entirely separated from the persistence layer.
315+> The model can be entirely separated from the persistence layer.
@@ -386,7 +327,7 @@ type GroupInsertionParams = '[ 'NotNull 'PGuuid
387- Params are not named, but they are indexed. You just need nullability and type.
328- Params are not named, but they are indexed. You just need nullability and type.
391```haskell
332```haskell
392groupInsertion :: Manipulation Schema GroupInsertionParams '[]
333groupInsertion :: Manipulation Schema GroupInsertionParams '[]
@@ -395,7 +336,7 @@ groupInsertion =
395 :* Set (param @2) `as` #name )
336 :* Set (param @2) `as` #name )
400- `TypeApplication` lets us use the index of parameters (counting from 1).
341- `TypeApplication` lets us use the index of parameters (counting from 1).
@@ -410,19 +351,19 @@ _Inserting a Member of Parliament_
411- We could create a naive query that takes MP uuid, first name, last name and group uuid...
352- We could create a naive query that takes MP uuid, first name, last name and group uuid...
415- But that's boring. So let's use the `INSERT INTO ... SELECT`.
356- But that's boring. So let's use the `INSERT INTO ... SELECT`.
419- We will build a query that will return as constants our MP's uuid, first name and last name...
360- We will build a query that will return as constants our MP's uuid, first name and last name...
423- ... and fetch the uuid of a group given the name of the group.
364- ... and fetch the uuid of a group given the name of the group.
427- There's a `insertQuery` utility function for that. All we have to do is write the `select` !
368- There's a `insertQuery` utility function for that. All we have to do is write the `select` !
@@ -572,16 +513,11 @@ insertParliament = traverse_ insertGroup
575−class: center, middle
578
516
583−class: center, middle
585# Part III. Selects
521# Part III. Selects
@@ -653,7 +589,7 @@ type BaseParliamentSelection =
653 , "m" ::: TableToRow MpCols ]
589 , "m" ::: TableToRow MpCols ]
656−- Note that we've also put everything with table aliases: "g" and "m".
592+> _note_ We've also put everything with table aliases: "g" and "m".
@@ -831,16 +767,10 @@ getGroupMembers =
834−class: center, middle
837
770
841−class: center, middle
844# Conclusion
774# Conclusion
@@ -860,17 +790,5 @@ count: false
860- Typesafe. 'nuff said.
790- Typesafe. 'nuff said.
863−class: center, middle
866# Thank you !
794# Thank you !
870− <script src="https://remarkjs.com/downloads/remark-latest.min.js">
873− var slideshow = remark.create({countIncrementalSlides: false});