HORIZON HASKELLDocslts/ghc-10.0.x8c2f8e82026-10-19Search names, modules, packages, or :: a typeCtrl K

GHC 10.0.0.20260917 · lts/ghc-10.0.x · 8c2f8e8 · 2026-10-19

Moduleopaleye-0.10.8.0Haskell2010

Opaleye.Join

Left, right, and full outer joins.

  • 12 values
  • Packageopaleye-0.10.8.0
  • Exports12
  • LanguageHaskell2010
  • LicenceBSD-3-Clause
  • SourceJoin.hs

The recommended way of performing joins in Opaleye

2 declarations

Opaleye supports inner joins, left/right joins and full outer joins. Instead of using them directly we recommend the following, which provide APIs that are more familiar to a Haskell programmer and more composable:

  • Inner joins: use where_ directly, along with do notation (or use restrict directly, along with arrow notation)

  • Left/right joins: use optional

We suspect the following do not have real world use cases. If you have one then we'd love to hear about it. Please open a new issue on the Opaleye project and tell us about it.

valueoptional
  1. :: Default Unpackspec a a
  2. => SelectArr i a

    Input query

  3. -> SelectArr i (MaybeFields a)

    The rows of the input query wrapped in "Just", unless the input query has no rows in which case a single row of "Nothing"

#

Convenient access to lateral left/right join functionality. Performs a LATERAL LEFT JOIN under the hood and has behaviour equivalent to the following Haskell function:

optional :: [a] -> [Maybe a]
optional q = case q of
    [] -> [Nothing]
    xs -> map Just xs

That is, if q :: SelectArr i a returns no rows, optional q :: SelectArr i (MaybeFields a) returns exactly one "Nothing" row. Otherwise, optional q returns exactly the rows of q wrapped in "Just". For example,

> let l1 = ["one", "two", "three"] :: [Field SqlText]
> runSelectI conn (optional (values l1))
[Just "one", Just "two", Just "three"]

> let l2 = [] :: [Field SqlText]
> runSelectI conn (optional (values l2))
[Nothing]

optionalRestrict is a special case of optional and could be written in terms of optional as follows (except that optionalRestrict doesn't use LATERAL under the hood and optional does).

optionalRestrict q = optional $ proc cond -> do
  a <- q -< ()
  restrict -< cond a
  returnA -< a
valueoptionalRestrict
  1. :: Default Unpackspec a a
  2. => Select a

    Input query

  3. -> SelectArr (a -> Field SqlBool) (MaybeFields a)

    If any rows of the input query satisfy the condition then return them (wrapped in "Just"). If none of them satisfy the condition then return a single row of "Nothing"

#

Convenient access to left/right join functionality. Performs a LEFT JOIN under the hood and has behaviour equivalent to the following Haskell function:

optionalRestrict :: [a] -> (a -> Bool) -> [Maybe a]
optionalRestrict xs p =
   case filter p xs of []  -> [Nothing]
                       xs' -> map Just xs'

For example,

> let l = [1, 10, 100, 1000] :: [Field SqlInt4]
> runSelectI conn (proc () -> optionalRestrict (values l) -< (.> 100000))
[Nothing]

> runSelectI conn (proc () -> optionalRestrict (values l) -< (.> 15))
[Just 100,Just 1000]

See the documentation of leftJoin for how to use optionalRestrict to replace leftJoin (and by symmetry, rightJoin).

Direct access to joins (not recommended)

4 declarations

You probably want use the alternatives listed at the top of this module instead of these. The use of the Default NullMaker typeclass means that the compiler will have trouble inferring types. It is strongly recommended that you provide full type signatures when using the join functions. Example specialization:

leftJoin :: Select (Field a, Field b)
         -> Select (Field c, FieldNullable d)
         -> (((Field a, Field b), (Field c, FieldNullable d)) -> Field SqlBool)
         -> Select ((Field a, Field b), (FieldNullable c, FieldNullable d))
valueleftJoin
  1. :: (Default Unpackspec fieldsL fieldsL, Default Unpackspec fieldsR fieldsR, Default NullMaker fieldsR nullableFieldsR)
  2. => Select fieldsL

    Left query

  3. -> Select fieldsR

    Right query

  4. -> ((fieldsL, fieldsR) -> Field SqlBool)

    Condition on which to join

  5. -> Select (fieldsL, nullableFieldsR)

    Left join

#

We suggest you use optional instead. Instead of writing "leftJoin qL qR cond" you can write

do
  fieldsL <- qL
  maybeFieldsR <- optional $ do
    fieldsR <- qR
    cond (fieldsL, fieldsR)
    pure fieldsR
  pure (fieldsL, maybeFieldsR)

Typically everything except the optional block can be inlined in surrounding do notation. In such cases, readability and maintainability increase dramatically.

Alternatively, if you have a reason to avoid LATERAL joins you can use optionalRestrict and arrow notation.

proc () -> do
  fieldsL <- qL -< ()
  maybeFieldsR <- optionalRestrict qR -< curry cond fieldsL
  returnA -< (fieldsL, maybeFieldsR)

Explicit versions

6 declarations