Upgrade 0.5.1.0 → 0.9.1.3: Query (`Statement`) with generic parameter (formerly using `ToParam`)
Hi Eitan,
I’m currently doing a big migration from Squeal 0.5.1.0 → 0.9.1.3 and I love it overall, especially how EncodeParams + DecodeRows gets rid of so much boilerplate.
Problem
I was able to figure out 95% of the work, but I get stuck on turning this query that used to take an arbitrary (generic?) parameter (using ToParam), that can convert to a PostgreSQL type, to a Statement with EncodeParams + DecodeRow. I had to revert and hard-code it to PGtext + Text which broke some of my queries that need PGtimestampz + UTCTime:
Statement: selectContentBy
Usage: getBy
I tried the following without luck:
selectContentBy ::
(ToParam Schemas pgty hsty, SOP.Generic hsty) =>
( TableExpression 'Ungrouped '[] '[] Schemas '[pgty] from ->
TableExpression 'Ungrouped '[] '[] Schemas '[pgty] from
) ->
Statement Schemas hsty Content
selectContentBy clauses = Query encode decode sql
where
encode :: EncodeParams Schemas pgty hsty
encode = aParam
decode = decodeContentWithImage
sql = -- ...
as I get this error ❌:
zoomhub/src/ZoomHub/Storage/PostgreSQL/Internal.hs:180:33: error: [GHC-18872]
• Couldn't match kind ‘NullType’ with ‘*’
When matching types
pgty0 :: [*]
'[pgty] :: [NullType]
Expected: EncodeParams
'["public"
::: [ZoomHub.Storage.PostgreSQL.Schema.Schema0.ConfigTable0,
ZoomHub.Storage.PostgreSQL.Schema.Schema3.ContentTable3,
ZoomHub.Storage.PostgreSQL.Schema.Schema0.ImageTable0,
ZoomHub.Storage.PostgreSQL.Schema.Schema0.FlickrTable0]]
'[pgty]
hsty
Actual: EncodeParams Schemas pgty0 hsty
• In the first argument of ‘Query’, namely ‘encode’
In the expression: Query encode decode sql
In an equation for ‘selectContentBy’:
selectContentBy clauses
= Query encode decode sql
where
encode :: EncodeParams Schemas pgty hsty
encode = aParam
decode = decodeContentWithImage
sql = -- ...
I was thinking it might be something obvious you could spot right away. If not, I’m happy to go back and do the work to create a minimally reproducing example.
I appreciate your help 😄
Hmmm. Looks like GHC is inferring the kind of pgty0 too monomorphically, too soon. Can you try turning on PolyKinds? See if that helps. That's what the error poetry tells me:
• Couldn't match kind ‘NullType’ with ‘*’
When matching types
pgty0 :: [*]
'[pgty] :: [NullType]
Or perhaps there is a mistake in the type annotation of your encode term.
- encode :: EncodeParams Schemas pgty hsty
+ encode :: EncodeParams Schemas '[pgty] hsty
Turning on ScopedTypeVariables should let GHC identify this pgty with the one in the type annotation of selectContentBy.
It might typecheck if you just removed the type signature for encode entirely and let GHC infer it.
@echatav Thank you so much for taking the time to look into this. I tried your suggestions, but unfortunately, I wasn’t able to resolve the error.
To make it easier for you and potentially contribute to Squeal with an example of a parametrized query, I created this draft PR with an extension of the existing Example.hs with a parametrized query: https://github.com/morphismtech/squeal/pull/354/
Does this help you repro and potentially resolve the issue?
Thanks 🙇♂️
My dream is that one day a GitHub search for EncodeParams, DecodeRow, consRow, appendRows, etc. will show lots of examples of Squeal in action helping others enjoy this wonderful library. Hopefully figuring out the upgrade in https://github.com/zoomhub/zoomhub/pull/235 will be a first such example ✨
P.S. I let GPT-4 loose on this, but Squeal type errors are too powerful for our AI overlords 😜
I've kind of been stuck working on Squeal because every time I try I run into GHC/Postgres installation location problems.
squeal-postgresql > build (lib + exe)
squeal-postgresql > Preprocessing library for squeal-postgresql-0.9.1.3..
squeal-postgresql > Building library for squeal-postgresql-0.9.1.3..
squeal-postgresql > ld: warning: -single_module is obsolete
squeal-postgresql > ld: warning: search path '/opt/homebrew/opt/libpq/lib' not found
squeal-postgresql > ld: library 'pq' not found
squeal-postgresql > clang: error: linker command failed with exit code 1 (use -v to see invocation)
squeal-postgresql > `gcc' failed in phase `Linker'. (Exit code: 1)
Googling hasn't helped me. I hate computers. If you help me figure out how I can get GHC to find LibPQ then I can help with Squeal again.
@echatav Re: libpq. Ouch, I’m sorry! Are you on macOS / Apple Silicon? I had similar issues and it took me a while to resolve. In fact, that’s what that whole stack LTS upgrade is about (I got a new MacBook laptop last year.) If you want, I’d be happy to hop on a Zoom call with you and try and help out. Shoot me an email to [email protected] if you are interested 😄
Re: generic SELECT. I partially figured it out!
- First, it was through discovering
~for type equality in your docs (but I had to enableAllowAmbiguousTypes😢): https://github.com/morphismtech/squeal/pull/354/commits/3a1851b62fab908c5bc7d213de26cb3c556c1019 - Then I found out I can drop
pgtyand infer it fromhsty: https://github.com/morphismtech/squeal/pull/354/commits/168813cb68626f8c826e5f3e4abe6754b28cbdce - Bonus: Added encoding/decoding custom type to example (however, I haven’t looked into how to make it type-safe yet): https://github.com/morphismtech/squeal/pull/354/commits/21057aa06d19cbbc6b63be7b209918b3d0c25d17
I actually was able to get it working first in my own code (without even needing AllowAmbiguousTypes 🤷♂️):
https://github.com/zoomhub/zoomhub/pull/235/commits/c297109d4527704a0c6868f497f1e3add9026c93#diff-d0b2f0351a2e5459c9b6306ed066f9e76873900c6bfc62fbc24dc5c2cad2f036R219-R232
I’m generally pretty happy about it except for this long from type signature and the need for OidOfNull Schemas. I originally had from inferred using _ + PartialTypeSignatures, but that started causing problems. Suggestions on how to improve it are welcome: https://github.com/zoomhub/zoomhub/pull/235/commits/c297109d4527704a0c6868f497f1e3add9026c93#diff-d0b2f0351a2e5459c9b6306ed066f9e76873900c6bfc62fbc24dc5c2cad2f036R182-R217
@gasi Nice job! Yes, I'm on a macbook. I'll send you an email. I've been wanting to get back to Squeal a bit.
Re: type safety. I guess you mean for OrganizationType on the Haskell side having a corresponding Postgres type so the type column isn't stringly-typed. For that you can use Postgres enum types. Squeal even has special support for creating Postgres enums from a Haskell type like so:
>>> :{
data Schwarma = Beef | Lamb | Chicken
deriving stock GHC.Generic
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving (IsPG, FromPG) via Enumerated Schwarma
:}
>>> :{
let
createSchwarma :: Definition (Public '[]) '["public" ::: '["schwarma" ::: 'Typedef (PG Schwarma)]]
createSchwarma = createTypeEnumFrom @Schwarma #schwarma
in
printSQL createSchwarma
:}
CREATE TYPE "schwarma" AS ENUM ('Beef', 'Lamb', 'Chicken');