diff --git a/doc/slide/code-reading-201601/SourceTreeJ.html b/doc/slide/code-reading-201601/SourceTreeJ.html index 13ac3397..b991646a 100644 --- a/doc/slide/code-reading-201601/SourceTreeJ.html +++ b/doc/slide/code-reading-201601/SourceTreeJ.html @@ -4,6 +4,7 @@ + Haskell Relational Record, 機能と構成 @@ -11,32 +12,50 @@ - - - - + + + + - - + +
@@ -50,68 +69,68 @@ code > span.er { color: #ff0000; font-weight: bold; }
-

Haskell Relational Record, 機能と構成

-

2016-01-31

-

Kei Hibino

+

Haskell Relational Record, 機能と構成

+

2016-01-31

+

Kei Hibino

DSL デザイン/機能

クエリの結合

$$\{ (x, y) | x \in X, y \in Y, \pi_1(x) = \pi_2(y) \}$$

-
-- Comprehension
+
-- Comprehension
 [ (x, y) | x <- xs, y <- ys, fst x == snd y ]
 
 -- List Monad
-do { x <- xs; y <- ys; fst x == snd y; return (x, y) }
+do { x <- xs; y <- ys; guard (fst x == snd y); return (x, y) }

結合クエリを List内包表記あるいは List Monad のように組み立てる

-
personAndBirthday :: Relation () (Person, Birthday)
+
personAndBirthday :: Relation () (Person, Birthday)
 personAndBirthday =  relation $ do
   p <- query person    -- Join product accumulated
   b <- query birthday
   on $ p ! Person.name' .=. b ! Birthday.name'
-  return $ p >< b
+ return $ p >< b

組み上がった結合式/Built joined query

-
personAndBirthday :: Relation () (Person, Birthday)
+
personAndBirthday :: Relation () (Person, Birthday)
 personAndBirthday =  relation $ do
   p <- query person
-  b <- query birthday  -- 結合積の集積
+  b <- query birthday  -- 結合積の集積
                        -- Join product accumulated
   on $ p ! Person.name' .=. b ! Birthday.name'
-  return $ p >< b
-
SELECT ALL T0.name AS f0, T0.age AS f1, T0.family AS f2,
+  return $ p >< b
+
SELECT ALL T0.name AS f0, T0.age AS f1, T0.family AS f2,
            T1.name AS f3, T1.day AS f4
       FROM EXAMPLE.person T0 INNER JOIN EXAMPLE.birthday T1
-        ON (T0.name = T1.name)
+ ON (T0.name = T1.name)

結合/Join

-
query :: (MonadQualify ConfigureQuery m, MonadQuery m)
+
query :: (MonadQualify ConfigureQuery m, MonadQuery m)
       => Relation () r
       -> m (Projection Flat r)
 
 -- Used for outer join
 queryMaybe :: (MonadQualify ConfigureQuery m, MonadQuery m)
            => Relation () r
-           -> m (Projection Flat (Maybe r))
+ -> m (Projection Flat (Maybe r))

query, queryMaybe は結合したテーブル式の Projection を返す

-
SELECT .. FROM ...
+
SELECT .. FROM ...
             -- Accumulating uniquely qualified
             -- ( like 'as T0', 'as T1' ... )
-            -- table forms of SQL FROM clause
+ -- table forms of SQL FROM clause

例 - 外部左結合/Left outer join

-
personAndBirthdayL :: Relation () (Person, Maybe Birthday)
+
personAndBirthdayL :: Relation () (Person, Maybe Birthday)
 personAndBirthdayL =  relation $ do
   p <- query person
   b <- queryMaybe birthday
   on $ just (p ! Person.name') .=. b ?! Birthday.name'
-  return $ p >< b
-
SELECT ALL T0.name AS f0, T0.age AS f1, T0.family AS f2,
+  return $ p >< b
+
SELECT ALL T0.name AS f0, T0.age AS f1, T0.family AS f2,
            T1.name AS f3, T1.day AS f4
       FROM EXAMPLE.person T0 LEFT JOIN EXAMPLE.birthday T1
-        ON (T0.name = T1.name)
+ ON (T0.name = T1.name)

Aggregation

-
groupBy :: MonadAggregate m
+
groupBy :: MonadAggregate m
         => Projection Flat r
         -- ^ Projection to add into group by
         -> m (Projection Aggregated r)
@@ -119,53 +138,53 @@ personAndBirthdayL =  relation $
 
 count :: Projection Flat a -> Projection Aggregated Int64
 max'  :: Ord a
-      => Projection Flat a -> Projection Aggregated (Maybe a)
+ => Projection Flat a -> Projection Aggregated (Maybe a)

groupByMonadAggregate の monad 制約 (MonadQuery より制限が強い)の下でのみ利用できる。

groupBy は集約した(Aggregated)文脈の型を持つ Projection の値を返す。

-
SELECT .. GROUP BY ...
+
SELECT .. GROUP BY ...
                 -- Accumulating keys
-                -- of SQL GROUP BY clause
+ -- of SQL GROUP BY clause

例 - 集約/Aggregation

-
agesOfFamilies :: Relation () (String, Maybe Int32)
+
agesOfFamilies :: Relation () (String, Maybe Int32)
 agesOfFamilies =  aggregateRelation $ do
   p <- query person
   gFam <- groupBy $ p ! Person.family'
           -- Specify grouping key
   return $ gFam >< sum' (p ! Person.age')
-          -- Aggregated results
-
SELECT ALL T0.family AS f0, SUM(T0.age) AS f1
+          -- Aggregated results
+
SELECT ALL T0.family AS f0, SUM(T0.age) AS f1
       FROM EXAMPLE.person T0
-  GROUP BY T0.family
+ GROUP BY T0.family

絞り込み/Restrict

-
restrict :: MonadRestrict c m
+
restrict :: MonadRestrict c m
          => Projection c (Maybe Bool)
          -> m ()
 
 wheres :: MonadRestrict Flat m
        => Projection Flat (Maybe Bool)
-       -> m ()
+ -> m ()

WHERE 節に絞り込み条件を加える

-
SELECT .. WHERE x AND y AND ...
+
SELECT .. WHERE x AND y AND ...
              -- Accumulating AND predicates
-             -- of SQL WHERE clause
+ -- of SQL WHERE clause

絞り込み/Restrict

-
restrict :: MonadRestrict c m
+
restrict :: MonadRestrict c m
          => Projection c (Maybe Bool)
          -> m ()
 
 having :: MonadRestrict Aggregated m
        => Projection Aggregated (Maybe Bool)
-       -> m ()
+ -> m ()

HAVING 節に絞り込み条件を加える。 集約した(Aggregated)文脈の型を持つ Projection のみが利用できる

-
SELECT .. HAVING x AND y AND ...
+
SELECT .. HAVING x AND y AND ...
               -- Accumulating AND predicates
-              -- of SQL HAVING clause
+ -- of SQL HAVING clause

例 - 絞り込み/Restriction

-
sameBirthdayHeisei' :: Relation () (Day, Int64)
+
sameBirthdayHeisei' :: Relation () (Day, Int64)
 sameBirthdayHeisei' =  aggregateRelation $ do
   p <- query person
   b <- query birthday
@@ -174,7 +193,7 @@ sameBirthdayHeisei' =  aggregateRelation ! Birthday.day' .>=. value (fromGregorian 1989 1 8)
   gbd <- groupBy $ b ! Birthday.day'
   having $ count (p ! Person.name') .>. value (1 :: Int64)
-  return $ gbd >< count (p ! Person.name')
+ return $ gbd >< count (p ! Person.name')

平成生まれで誕生日が同じ人を数える

-

絞り込み/Restriction

-
SELECT ALL T1.day AS f0, COUNT(T0.name) AS f1
+
SELECT ALL T1.day AS f0, COUNT(T0.name) AS f1
       FROM EXAMPLE.person T0 INNER JOIN EXAMPLE.birthday T1
         ON (T0.name = T1.name)
      WHERE (T1.day >= DATE '1989-01-08')
   GROUP BY T1.day
-    HAVING (COUNT(T0.name) > 1)
+ HAVING (COUNT(T0.name) > 1)

例 - 絞り込み/Restriction - let

-
sameBirthdayHeisei :: Relation () (Day, Int64)
+
sameBirthdayHeisei :: Relation () (Day, Int64)
 sameBirthdayHeisei =  aggregateRelation $ do
   p <- query person
   b <- query birthday
@@ -207,81 +225,81 @@ sameBirthdayHeisei =  aggregateRelation <- groupBy birthDay
   let personCount = count $ p ! Person.name'
   having $ personCount .>. value 1
-  return $ gbd >< personCount
+ return $ gbd >< personCount

Ordering

-
orderBy :: Monad m
+
orderBy :: Monad m
         => Projection c t
         -- ^ Ordering terms to add
         -> Order
         -- ^ Order direction -- Asc | Desc
         -> Orderings c m ()
-        -- ^ Result context with ordering
+ -- ^ Result context with ordering

文脈に合わせた Projection の型を持った値のみが利用できる。

-
SELECT .. ORDER BY ...
-                -- Accumulating terms of ORDER BY clause
+
SELECT .. ORDER BY ...
+                -- Accumulating terms of ORDER BY clause

例 - 順序付け/Ordering

-
personAndBirthdayO :: Relation () (Person, Birthday)
+
personAndBirthdayO :: Relation () (Person, Birthday)
 personAndBirthdayO =  relation $ do
   p <- query person
   b <- query birthday
   on $ p ! Person.name' .=. b ! Birthday.name'
   orderBy (b ! Birthday.day') Asc  -- Specify ordering key
   orderBy (p ! Person.name') Asc
-  return $ p >< b
+ return $ p >< b

orders by birthday and then name:

-
SELECT ALL T0.name AS f0, T0.age AS f1, T0.family AS f2,
+
SELECT ALL T0.name AS f0, T0.age AS f1, T0.family AS f2,
            T1.name AS f3, T1.day AS f4
       FROM EXAMPLE.person T0 INNER JOIN EXAMPLE.birthday T1
         ON (T0.name = T1.name)
-  ORDER BY T1.day ASC, T0.name ASC
+ ORDER BY T1.day ASC, T0.name ASC

例 - 順序付け/Ordering

-
SELECT ALL T0.name AS f0, T0.age AS f1, T0.family AS f2,
+
SELECT ALL T0.name AS f0, T0.age AS f1, T0.family AS f2,
            T1.name AS f3, T1.day AS f4
       FROM EXAMPLE.person T0 INNER JOIN EXAMPLE.birthday T1
         ON (T0.name = T1.name)
-  ORDER BY T1.day ASC, T0.name ASC
+ ORDER BY T1.day ASC, T0.name ASC

プレースホルダー/Placeholders

-
placeholder ::
+
placeholder ::
   (PersistableWidth t, SqlProjectable p, Monad m) =>
-  (p t -> m a) -> m (PlaceHolders t, a)
+ (p t -> m a) -> m (PlaceHolders t, a)

組み立てのモナドの式に placeholder を与える

例 - プレースホルダー/Placeholders

-
specifyPerson :: Relation String (Person, Birthday)
+
specifyPerson :: Relation String (Person, Birthday)
 specifyPerson =  relation' $ do
   pb <- query personAndBirthday -- Re-use predefined
   (ph, ()) <- placeholder
               (\ph' ->
                 wheres $ pb ! fst' ! Person.name' .=. ph')
-  return (ph, pb)
+ return (ph, pb)

名前をプレースホルダーで指定する:

-
SELECT ALL T2.f0 AS f0, T2.f1 AS f1, T2.f2 AS f2,
+
SELECT ALL T2.f0 AS f0, T2.f1 AS f1, T2.f2 AS f2,
            T2.f3 AS f3, T2.f4 AS f4
   FROM (SELECT ALL T0.name AS f0, T0.age AS f1, T0.family AS f2,
                T1.name AS f3, T1.day AS f4
               FROM EXAMPLE.person T0 INNER JOIN
                    EXAMPLE.birthday T1
                 ON (T0.name = T1.name)) T2
- WHERE (T2.f0 = ?)
+ WHERE (T2.f0 = ?)

例 - プレースホルダー/Placeholders

-
SELECT ALL T2.f0 AS f0, T2.f1 AS f1, T2.f2 AS f2,
+
SELECT ALL T2.f0 AS f0, T2.f1 AS f1, T2.f2 AS f2,
            T2.f3 AS f3, T2.f4 AS f4
   FROM (SELECT ALL T0.name AS f0, T0.age AS f1, T0.family AS f2,
                T1.name AS f3, T1.day AS f4
               FROM EXAMPLE.person T0 INNER JOIN
                    EXAMPLE.birthday T1
                 ON (T0.name = T1.name)) T2
- WHERE (T2.f0 = ?)
+ WHERE (T2.f0 = ?)

例 - ウィンドウ関数/Window function

ウィンドウを組み立てる:

Building windows:

-
ageRankOfFamilies :: Relation () ((Int64, String), Int32)
+
ageRankOfFamilies :: Relation () ((Int64, String), Int32)
 ageRankOfFamilies =  relation $ do
   my <- query myTable
   return $
@@ -289,34 +307,34 @@ ageRankOfFamilies =  relation $
       partitionBy $ my ! family'  -- Monad to build window
       orderBy (my ! age') Desc
     ><
-    my ! family' >< my ! age'
-
SELECT ALL
+    my ! family' >< my ! age'
+
SELECT ALL
        RANK() OVER (PARTITION BY T0.family
                     ORDER BY T0.age DESC) AS f0,
        T0.family AS f1, T0.age AS f2
-  FROM PUBLIC.my_table T0
+ FROM PUBLIC.my_table T0

例 - ウィンドウ関数/Window function

-
SELECT ALL
+
SELECT ALL
        RANK() OVER (PARTITION BY T0.family
                     ORDER BY T0.age DESC) AS f0,
        T0.family AS f1, T0.age AS f2
-  FROM PUBLIC.my_table T0
+ FROM PUBLIC.my_table T0

Map SQL Values to a Haskell Record

Mapping to records using Applicative style:

-
(|$|) :: (ProjectableFunctor p, ProductConstructor (a -> b))
+
(|$|) :: (ProjectableFunctor p, ProductConstructor (a -> b))
       => (a -> b)
       -> p a
       -> p b
 (|*|) :: ProjectableApplicative p
       => p (a -> b)
       -> p a
-      -> p b
+ -> p b

Record Mapping - Projections

Assign record types to an SQL projection:

-
personAndBirthdayT :: Relation () PersonAndBirthday
+
personAndBirthdayT :: Relation () PersonAndBirthday
 personAndBirthdayT =  relation $ do
   p <- query person
   b <- query birthday
@@ -326,26 +344,26 @@ personAndBirthdayT =  relation $
 
 (|$|) :: ProductConstructor (a -> b)
       => (a -> b) -> Projection c a -> Projection c b
-(|*|) :: Projection c (a -> b) -> Projection c a -> Projection c b
-
SELECT ALL T0.name AS f0, T0.age AS f1, T0.address AS f2,
+(|*|) :: Projection c (a -> b) -> Projection c a -> Projection c b
+
SELECT ALL T0.name AS f0, T0.age AS f1, T0.address AS f2,
            T1.name AS f3, T1.day AS f4
       FROM PUBLIC.person T0 INNER JOIN PUBLIC.birthday T1
-        ON (T0.name = T1.name)
+ ON (T0.name = T1.name)

Record Mapping - Column Selectors

Column selectors can be mapped to a record:

-
Birthday.day' :: Pi Birthday Day
+
Birthday.day' :: Pi Birthday Day
 
 uncurryPB :: Pi (Person, Birthday) PersonAndBirthday
 uncurryPB =  PersonAndBirthday |$| fst' |*| snd'
 
 (|$|) :: ProductConstructor (a -> b)
       => (a -> b) -> Pi r a -> Pi r b
-(|*|) :: Pi r (a -> b) -> Pi r a -> Pi r b
+(|*|) :: Pi r (a -> b) -> Pi r a -> Pi r b

Record Mapping - Placeholders

Placeholders can be mapped to a record:

-
personAndBirthdayP2 :: Relation Person PersonAndBirthday
+
personAndBirthdayP2 :: Relation Person PersonAndBirthday
 personAndBirthdayP2 =  relation' $ do
   p <- query person
   b <- query birthday
@@ -358,23 +376,23 @@ personAndBirthdayP2 =  relation' $(|$|) :: ProductConstructor (a -> b)
       => (a -> b) -> Placeholders a -> Placeholders b
-(|*|) :: Placeholders (a -> b) -> Placeholders a -> Placeholders b
+(|*|) :: Placeholders (a -> b) -> Placeholders a -> Placeholders b

Generated SQL:

-
SELECT ALL T0.name AS f0, T0.age AS f1, T0.address AS f2,
+
SELECT ALL T0.name AS f0, T0.age AS f1, T0.address AS f2,
            T1.name AS f3, T1.day AS f4
       FROM PUBLIC.person T0 INNER JOIN PUBLIC.birthday T1
-        ON (((T0.name = ?) AND (T0.age = ?)) AND (T0.address = ?))
+ ON (((T0.name = ?) AND (T0.age = ?)) AND (T0.address = ?))

Record Mapping - Placeholders

Generated SQL:

-
SELECT ALL T0.name AS f0, T0.age AS f1, T0.address AS f2,
+
SELECT ALL T0.name AS f0, T0.age AS f1, T0.address AS f2,
            T1.name AS f3, T1.day AS f4
       FROM PUBLIC.person T0 INNER JOIN PUBLIC.birthday T1
-        ON (((T0.name = ?) AND (T0.age = ?)) AND (T0.address = ?))
+ ON (((T0.name = ?) AND (T0.age = ?)) AND (T0.address = ?))

Record Mapping - Record Placeholders

Record-typed placeholder:

-
placeholder :: (PersistableWidth t, Monad m)
+
placeholder :: (PersistableWidth t, Monad m)
             => (Projection c t -> m a) -> m (PlaceHolders t, a)
 
 personAndBirthdayP :: Relation Person PersonAndBirthday
@@ -382,12 +400,12 @@ personAndBirthdayP =  relation' $<- query person
   b <- query birthday
   (ph, ()) <- placeholder (\ph' -> wheres $ p .=. ph')
-  return $ (ph, PersonAndBirthday |$| p |*| b)
+ return $ (ph, PersonAndBirthday |$| p |*| b)

row value of Placeholders:

-
SELECT ALL T0.name AS f0, T0.age AS f1, T0.address AS f2,
+
SELECT ALL T0.name AS f0, T0.age AS f1, T0.address AS f2,
            T1.name AS f3, T1.day AS f4
       FROM PUBLIC.person T0 INNER JOIN PUBLIC.birthday T1
-        ON ((T0.name, T0.age, T0.address) = (?, ?, ?))
+ ON ((T0.name, T0.age, T0.address) = (?, ?, ?))

HRR の構成

HRR を構成する Hackage

@@ -495,7 +513,6 @@ personAndBirthdayP = relation' $ -

Monad Transformer の利用