maybeMapper :: RelationalMap a b -> RelationalMap (Maybe a) (Maybe b)
maybeMapper
    -- rewrite the mapper to handle null fields, then tag
    -- it as having been done so we don't double-map it
    -- in a future `maybeMapper` call.
    --
 = RM_MaybeTag . go
  where
    go :: RelationalMap a b -> RelationalMap (Maybe a) (Maybe b)
    go (RM_Nest f rm) = RM_Nest (fmap f) (go rm)
    go (RM_Field f) =
      case checkNullability f of
        NotNullField notNullField ->
          RM_Field (nullableField notNullField)

        NullableField nullField ->
          -- When the underlying field is already nullable we need to make sure
          -- that 'NULL' is decoded to a 'Just'. Otherwise when the field is
          -- 'NULL' it causes the entire 'RelationalMap' to resolve to a
          -- 'Nothing' as if _all_ fields were 'NULL', even if they were not.
          RM_Field (asymmetricNullableField nullField)

    go (RM_Pure a) = RM_Pure (pure a)
    go (RM_Apply rmF rmA) = RM_Apply (fmap (<*>) $ go rmF) (go rmA)
    go (RM_Partial rm) = RM_Partial (flipError <$> go rm)
      where
        flipError :: Maybe (Either String a) -> Either String (Maybe a)
        flipError (Just (Right a)) = Right (Just a)
        flipError (Just (Left err)) = Left err
        flipError Nothing = Right Nothing
    go (RM_ReadOnly rm) = RM_ReadOnly (go rm)
    go rm@(RM_MaybeTag _) = fmap Just $ mapAttr join $ rm
maybeMapper :: RelationalMap a b -> RelationalMap (Maybe a) (Maybe b)
maybeMapper
    -- rewrite the mapper to handle null fields, then tag
    -- it as having been done so we don't double-map it
    -- in a future `maybeMapper` call.
    --
 = RM_MaybeTag . go
  where
    go :: RelationalMap a b -> RelationalMap (Maybe a) (Maybe b)
    go (RM_Nest f rm) = RM_Nest (fmap f) (go rm)
    go (RM_Field f) =
      case checkNullability f of
        NotNullField notNullField ->
          RM_Field (nullableField notNullField)

        NullableField nullField ->
          -- When the underlying field is already nullable we need to make sure
          -- that 'NULL' is decoded to a 'Just'. Otherwise when the field is
          -- 'NULL' it causes the entire 'RelationalMap' to resolve to a
          -- 'Nothing' as if _all_ fields were 'NULL', even if they were not.
          RM_Field (asymmetricNullableField nullField)

    go (RM_Pure a) = RM_Pure (pure a)
    go (RM_Apply rmF rmA) = RM_Apply (fmap (<*>) $ go rmF) (go rmA)
    go (RM_Partial rm) = RM_Partial (flipError <$> go rm)
      where
        flipError :: Maybe (Either String a) -> Either String (Maybe a)
        flipError (Just (Right a)) = Right (Just a)
        flipError (Just (Left err)) = Left err
        flipError Nothing = Right Nothing
    go (RM_ReadOnly rm) = RM_ReadOnly (go rm)
    go rm@(RM_MaybeTag _) = fmap Just $ mapAttr join $ rm
maybeMapper
    -- rewrite the mapper to handle null fields, then tag
    -- it as having been done so we don't double-map it
    -- in a future `maybeMapper` call.
    --
 = RM_MaybeTag . go
  where
    go :: RelationalMap a b -> RelationalMap (Maybe a) (Maybe b)
    go (RM_Nest f rm) = RM_Nest (fmap f) (go rm)
    go (RM_Field f) =
      case checkNullability f of
        NotNullField notNullField ->
          RM_Field (nullableField notNullField)

        NullableField nullField ->
          -- When the underlying field is already nullable we need to make sure
          -- that 'NULL' is decoded to a 'Just'. Otherwise when the field is
          -- 'NULL' it causes the entire 'RelationalMap' to resolve to a
          -- 'Nothing' as if _all_ fields were 'NULL', even if they were not.
          RM_Field (asymmetricNullableField nullField)

    go (RM_Pure a) = RM_Pure (pure a)
    go (RM_Apply rmF rmA) = RM_Apply (fmap (<*>) $ go rmF) (go rmA)
    go (RM_Partial rm) = RM_Partial (flipError <$> go rm)
      where
        flipError :: Maybe (Either String a) -> Either String (Maybe a)
        flipError (Just (Right a)) = Right (Just a)
        flipError (Just (Left err)) = Left err
        flipError Nothing = Right Nothing
    go (RM_ReadOnly rm) = RM_ReadOnly (go rm)
    go rm@(RM_MaybeTag _) = fmap Just $ mapAttr join $ rm