Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/Rel8.hs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ module Rel8
, Expr
, Sql
, litExpr
, coerceExpr
, unsafeCastExpr
, unsafeCoerceExpr
, unsafeLiteral
Expand Down Expand Up @@ -417,7 +418,7 @@ import Rel8.Expr.Default
import Rel8.Expr.Eq
import Rel8.Expr.Function
import Rel8.Expr.Null
import Rel8.Expr.Opaleye (unsafeCastExpr, unsafeCoerceExpr, unsafeLiteral, unsafePrimExpr)
import Rel8.Expr.Opaleye (coerceExpr, unsafeCastExpr, unsafeCoerceExpr, unsafeLiteral, unsafePrimExpr)
import Rel8.Expr.Ord
import Rel8.Expr.Order
import Rel8.Expr.Serialize
Expand Down
19 changes: 19 additions & 0 deletions src/Rel8/Expr/Opaleye.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
module Rel8.Expr.Opaleye
( castExpr, unsafeCastExpr
, scastExpr, sunsafeCastExpr
, coerceExpr
, unsafeCoerceExpr
, unsafePrimExpr
, unsafeLiteral
Expand All @@ -19,6 +20,7 @@ where

-- base
import Prelude
import Data.Coerce ( Coercible )

-- opaleye
import qualified Opaleye.Internal.Column as Opaleye
Expand All @@ -39,6 +41,23 @@ castExpr :: Sql DBType a => Expr a -> Expr a
castExpr = scastExpr typeInformation


-- | Change the type of an 'Expr' without using @CAST()@, if the
-- Haskell type can be safely coerced.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if it follows that if the Haskell types can be safely coerced then the DB types can be safely coerced. Could you not have a newtype which uses a different DBType?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's sound only if you ever newtype-derive DBType instances. If that's true then it might not pull its weight: either it'll sound safer than it is, or we'll have to call it something like sometimesButNotAlwaysSafeCoerceExpr. Or use some constraint that indicates that it's specifically a newtype and not just Coercible. (e.g. the one in generic-data.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sometimesButNotAlwaysSafe is exactly what unsafe means though. unsafe just means that there is some pre-condition that we couldn't express in the types so you should be careful when using it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that's my point. It's slightly less unsafe but still unsafe, which makes me doubt if it's enough of an improvement over unsafeCoerceExpr to include. I might just close this PR.

--
-- This is useful for writing function that use @nextval()@ over @newtype@'d IDs:
--
-- @
-- newtype AuthorId = AuthorId Int64
-- deriving newtype (DBType, DBEq)
--
-- nextId :: Expr AuthorId
-- nextId = coerceExpr $ nextVal "authors_id_seq"
-- @
--
coerceExpr :: forall b a. Coercible a b => Expr a -> Expr b
coerceExpr = unsafeCoerceExpr


-- | Cast an expression to a different type. Corresponds to a @CAST()@ function
-- call.
unsafeCastExpr :: forall b a. Sql DBType b => Expr a -> Expr b
Expand Down