circuithub / rel8

Hey! Hey! Can u rel8?
https://rel8.readthedocs.io
Other
154 stars 39 forks source link

How to use nextval with a newtyped ID? #172

Closed sullyj3 closed 2 years ago

sullyj3 commented 2 years ago

I have the following table:

newtype UserId = UserId { toInt32 :: Int32 }
  deriving newtype (DBEq, DBType, Eq, Show)

deriving newtype instance DBType (PasswordHash a)

data User f = User
  { userId :: Column f UserId,
    userName :: Column f Text,
    userPassword :: Column f (PasswordHash Bcrypt)
  }
  deriving stock (Generic)
  deriving anyclass (Rel8able)

And I'm trying to figure out how to insert a user. I have this snippet in a Yesod Handler

    hashed <- liftIO <| hashPassword <| mkPassword password
    let user = User {
          userId = nextval "user_id_seq",
          userName = lit username,
          userPassword = lit hashed
        }
    Yesoder conn <- getYesod
    liftIO $ run (insertUser user) conn
    pure "registered"

But I'm getting

    • Couldn't match type ‘GHC.Int.Int64’ with ‘UserId’
      Expected: Column Expr UserId
        Actual: Expr GHC.Int.Int64
    • In the ‘userId’ field of a record
      In the expression:
        User
          {userId = nextval "user_id_seq", userName = lit username,
           userPassword = lit hashed}
      In an equation for ‘user’:
          user
            = User
                {userId = nextval "user_id_seq", userName = lit username,
                 userPassword = lit hashed}
   |
87 |             userId = nextval "user_id_seq",
   |                      ^^^^^^^^^^^^^^^^^^^^^

I thought I might be able to do something like UserId <$> nextval "user_id_seq" to obtain an Expr UserId, but Expr doesn't have a Functor instance. I only have the fuzziest understanding of how expr works, so I assume there's some good reason for this. On reflection this probably doesn't make sense.

How do I handle this correctly? It seems like a pretty foundational use case.

ocharles commented 2 years ago

I think you have a few options:

I'm not sure we have a great story here, but hopefully one of these options will work for you!

sullyj3 commented 2 years ago

I was about to resort to unsafeDefault, but I think unsafeCoerceExpr appeals the most. I'll go with that for now. Thanks!

sullyj3 commented 2 years ago

I'm assuming you meant unsafeCastExpr?

ocharles commented 2 years ago

Yes :smile: