haskell-relational-record/doc/slide/haskell-hackathon-201412/HRR.txt

230 lines
5.5 KiB
Plaintext
Raw Normal View History

2014-12-21 10:16:54 +03:00
% Relational Record Released!
% 2014-12-20
% Kei Hibino
Basics
-----
Building join like List Comprehension or List Monad
$$\{ (x, y) | x \in X, y \in Y, \pi_1(x) = \pi_2(y) \}$$
~~~~~ {#mycode .haskell}
[ (x, y) | x <- xs, y <- ys, fst x == snd y ] -- Comprehension
2016-09-17 10:00:14 +03:00
do { x <- xs; y <- ys; guard (fst x == snd y); return (x, y) } -- List Monad
2014-12-21 10:16:54 +03:00
personAndBirthday :: Relation () (Person, Birthday)
personAndBirthday = relation $ do
p <- query person -- Join product accumulated
b <- query birthday
wheres $ p ! Person.name' .=. b ! Birthday.name'
return $ p >< b
~~~~~
Outer Join
-----
Outer joining. Not matched records is mapped Maybe type.
~~~~~ {#mycode .haskell}
personAndBirthdayL :: Relation () (Person, Maybe Birthday)
personAndBirthdayL = relation $ do
p <- query person
b <- queryMaybe birthday -- Maybe not match
wheres $ just (p ! Person.name') .=. b ?! Birthday.name'
return $ p >< b
~~~~~
Aggregation
-----
* groupBy action only used in aggregated context Monad.
* Only aggregated context Monad with aggregated context result type is allowed to pass aggregateRelation.
~~~~~ {#mycode .haskell}
agesOfFamilies :: Relation () (String, Maybe Int32)
agesOfFamilies = aggregateRelation $ do
my <- query myTable
gFam <- groupBy $ my ! family' -- Specify grouping key
return $ gFam >< sum' (my ! age') -- Aggregated results
~~~~~
Ordering
-----
Ordering key can be incrementally accumulated.
~~~~~ {#mycode .haskell}
personAndBirthdayO :: Relation () (Person, Birthday)
personAndBirthdayO = relation $ do
p <- query person
b <- query birthday
wheres $ p ! Person.name' .=. b ! Birthday.name'
orderBy (b ! Birthday.day') Asc -- Specify ordering key
orderBy (p ! Person.name') Asc
return $ p >< b
~~~~~
Ordering
-----
Ordering key context type (not aggregated or aggregated) is checked.
~~~~~ {#mycode .haskell}
agesOfFamiliesO :: Relation () (String, Maybe Int32)
agesOfFamiliesO = aggregateRelation $ do
my <- query myTable
gFam <- groupBy $ my ! family'
let s = sum' (my ! age')
orderBy s Desc -- Only aggregated value is allowd to pass
orderBy gFam Asc
return $ gFam >< s
~~~~~
Placeholders
-----
Can embed placeholder with typed.
~~~~~ {#mycode .haskell}
specifyPerson :: Relation String (Person, Birthday)
specifyPerson = relation' $ do
pb <- query personAndBirthday -- Re-use pre-defined Relation
(ph, ()) <- placeholder (\ph' -> wheres $ pb ! fst' ! Person.name' .=. ph')
return (ph, pb)
~~~~~
Window Function
-----
Monadic style window building.
~~~~~ {#mycode .haskell}
ageRankOfFamilies :: Relation () ((Int64, String), Int32)
ageRankOfFamilies = relation $ do
my <- query myTable
return $
rank `over` do
partitionBy $ my ! family' -- Monad to build window
orderBy (my ! age') Desc
><
my ! family'
><
my ! age'
~~~~~
Record Mapping
-----
Applicative style
~~~~~ {#mycode .haskell}
(|$|) :: (a -> b) -> p a -> p b
(|*|) :: p (a -> b) -> p a -> p b
~~~~~
Record Mapping
-----
Assign record type to SQL projection
~~~~~ {#mycode .haskell}
personAndBirthdayT :: Relation () PersonAndBirthday
personAndBirthdayT = relation $ do
p <- query person
b <- query birthday
wheres $ p ! Person.name' .=. b ! Birthday.name'
return $ PersonAndBirthday |$| p |*| b -- Build record phantom type
(|$|) :: (a -> b) -> Projection c a -> Projection c b
(|*|) :: Projection c (a -> b) -> Projection c a -> Projection c b
~~~~~
Record Mapping
-----
Projection path can be map to record.
~~~~~ {#mycode .haskell}
Birthday.day' :: Pi Birthday Day
uncurryPB :: Pi (Person, Birthday) PersonAndBirthday
uncurryPB = PersonAndBirthday |$| fst' |*| snd'
(|$|) :: (a -> b) -> Pi r a -> Pi r b
(|*|) :: Pi r (a -> b) -> Pi r a -> Pi r b
~~~~~
Record Mapping
-----
Placeholder can be map to record.
~~~~~ {#mycode .haskell}
placeholder3 f =
placeholder (\p0 -> placeholder (\p1 -> placeholder (\p2 -> f p0 p1 p2)))
personAndBirthdayP2 :: Relation ((String, Int32), String) PersonAndBirthday
personAndBirthdayP2 = relation' $ do
p <- query person
b <- query birthday
(ph0, (ph1, (ph2, ()))) <-
placeholder3 (\ph0' ph1' ph2' ->
wheres $
(Person |$| p ! Person.name' |*| p ! Person.age' |*| p ! Person.address')
.=.
(Person |$| ph0' |*| ph1' |*| ph2') )
return $ (ph0 >< ph1 >< ph2, PersonAndBirthday |$| p |*| b)
~~~~~
Record Mapping
-----
Record typed placeholder.
~~~~~ {#mycode .haskell}
personAndBirthdayP :: Relation Person PersonAndBirthday
personAndBirthdayP = relation' $ do
p <- query person
b <- query birthday
(ph, ()) <- placeholder (\ph' -> wheres $ p .=. ph')
return $ (ph, PersonAndBirthday |$| p |*| b)
~~~~~
Relational Record and Opaleye
-----
* Notation differs
* Opaleye -- Arrow notation
* Relational Record -- Monad
* Not aggregated approach is same
* Differs on Aggregation, Ordering, Placeholder and Record mapping
Relational Record and Opaleye?
-----
Easy to wrap HRR combinators against arrow notations.
~~~~~ {#mycode .haskell}
personAndJoinA :: QuerySimple () (Projection Flat (Person, Birthday))
personAndJoinA = proc () -> do
p <- query -< person
b <- query -< birthday
wheres -< p ! Person.name' .=. b ! Birthday.name'
returnA -< p >< b
personAndBirthdayOP :: Query (PersonColumn, BirthdayColumn)
personAndBirthdayOP = proc () -> do
p <- personQuery -< ()
b <- birthdayQuery -< ()
restrict -< Person.name p .== Birthday.name b
returnA -< (p, b)
~~~~~
Discussion
-----