@@ -63,6 +63,14 @@ import Data.Functor.Apply ( Apply, (<.>) )
63import Data.Functor.Bind ( Bind, (>>-) )
63import Data.Functor.Bind ( Bind, (>>-) )
66+-- | @MaybeTable t@ is the table @t@, but as the result of an outer join. If
67+-- the outer join fails to match any rows, this is essentialy @Nothing@, and if
68+-- the outer join does match rows, this is like @Just@. Unfortunately, SQL
69+-- makes it impossible to distinguish whether or not an outer join matched any
70+-- rows based generally on the row contents - if you were to join a row
71+-- entirely of nulls, you can't distinguish if you matched an all null row, or
72+-- if the match failed. For this reason @MaybeTable@ contains an extra field -
73+-- a "nullTag" - to track whether or not the outer join produced any rows.
66type MaybeTable :: Type -> Type
74type MaybeTable :: Type -> Type
67data MaybeTable a = MaybeTable
75data MaybeTable a = MaybeTable
68 { tag :: Expr (Maybe MaybeTag)
76 { tag :: Expr (Maybe MaybeTag)
@@ -75,6 +83,8 @@ instance Apply MaybeTable where
75 MaybeTable tag f <.> MaybeTable tag' a = MaybeTable (tag <> tag') (f a)
83 MaybeTable tag f <.> MaybeTable tag' a = MaybeTable (tag <> tag') (f a)
86+-- | Has the same behavior as the @Applicative@ instance for @Maybe@. See also:
87+-- 'traverseMaybeTable'.
78instance Applicative MaybeTable where
88instance Applicative MaybeTable where
79 (<*>) = (<.>)
89 (<*>) = (<.>)
80 pure = justTable
90 pure = justTable
@@ -85,6 +95,8 @@ instance Bind MaybeTable where
85 MaybeTable tag' b -> MaybeTable (tag <> tag') b
95 MaybeTable tag' b -> MaybeTable (tag <> tag') b
98+-- | Has the same behavior as the @Monad@ instance for @Maybe@. See also:
88instance Monad MaybeTable where
100instance Monad MaybeTable where
89 (>>=) = (>>-)
101 (>>=) = (>>-)
@@ -152,26 +164,38 @@ instance
152 ) => Recontextualize from to (MaybeTable a) (MaybeTable b)
164 ) => Recontextualize from to (MaybeTable a) (MaybeTable b)
167+-- | Check if a @MaybeTable@ is absent of any row.. Like 'isNothing'.
155isNothingTable :: MaybeTable a -> Expr Bool
168isNothingTable :: MaybeTable a -> Expr Bool
156isNothingTable (MaybeTable tag _) = isNull tag
169isNothingTable (MaybeTable tag _) = isNull tag
172+-- | Check if a @MaybeTable@ contains a row. Like 'isJust'.
159isJustTable :: MaybeTable a -> Expr Bool
173isJustTable :: MaybeTable a -> Expr Bool
160isJustTable (MaybeTable tag _) = isNonNull tag
174isJustTable (MaybeTable tag _) = isNonNull tag
177+-- | Perform case analysis on a 'MaybeTable'. Like 'maybe'.
163maybeTable :: Table DB b => b -> (a -> b) -> MaybeTable a -> b
178maybeTable :: Table DB b => b -> (a -> b) -> MaybeTable a -> b
164maybeTable b f ma@(MaybeTable _ a) = bool (f a) b (isNothingTable ma)
179maybeTable b f ma@(MaybeTable _ a) = bool (f a) b (isNothingTable ma)
182+-- | The null table. Like 'Nothing'.
167nothingTable :: Table DB a => MaybeTable a
183nothingTable :: Table DB a => MaybeTable a
168nothingTable = MaybeTable null undefined
184nothingTable = MaybeTable null undefined
187+-- | Lift any table into 'MaybeTable'. Like 'Just'. Note you can also use
171justTable :: a -> MaybeTable a
189justTable :: a -> MaybeTable a
172justTable = MaybeTable (nullify (litExpr IsJust))
190justTable = MaybeTable (nullify (litExpr IsJust))
193+-- | Project a single expression out of a 'MaybeTable'. You can think of this
194+-- operator like the '$' operator, but it also has the ability to return
197+-- >>> select c $ fmap (fst $?) (optional (values [lit (True, False)]))
175($?) :: forall a b db. (DBType db, Nullabilizes db b)
199($?) :: forall a b db. (DBType db, Nullabilizes db b)
176 => (a -> Expr b) -> MaybeTable a -> Expr (Maybe db)
200 => (a -> Expr b) -> MaybeTable a -> Expr (Maybe db)
177f $? ma@(MaybeTable _ a) = case nullabilization @b of
201f $? ma@(MaybeTable _ a) = case nullabilization @b of