-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
TH for defining type synonyms #11
Comments
Here's a neat way of dealing with this issue with no TH. The idea is to write something like the following
|
Nice! You also need |
The TH would still be nice though, I may look into it. Thinking about something like: makeType [d|
data Person
{ id :: Int
, name :: Text
, createdTime :: UTCTime
}
]
makeAdaptorAndInstance "pPerson" ''PersonP
makeTable "people" ''PersonP => data PersonP a b = PersonP { id :: a, name :: b, createdTime :: c }
type PersonF f = PersonP (f Int) (f Text) (f UTCTime)
type Person = PersonF I
type PersonW = PersonF Wire
type PersonMW = PersonF MaybeWire
type PersonWM = PersonF WireMaybe
makeAdaptorAndInstance "pPerson" ''PersonP
table :: Table PersonW
table = Table "people" $ PersonP (Wire "id") (Wire "name") (Wire "created_time") It would make the assumption that it should uncamelcase field names (which we want), but that could be made configurable. We could also inline the type synonyms Liberal*. |
That sounds very cool. |
I quite like this idea. I wonder if it's worth introducing all of the |
While you can just use Just generating |
If we find an approach that works well then I'm happy to update the TH appropriately. It's hard to discuss these in the abstract so I encourage everyone to try different approaches to see what works well in practice. |
@tomjaguarpaw would you mind writing out the adaptor and instance would be for |
Hmm, well I'm not sure there is one really. The adaptor and instance only make sense when |
Here's what we've come up with so far, first we make a type family: type family To (a :: * -> *) (b :: *) :: *
type instance To x (a, b) = (x a, x b)
type instance To x (a, b, c) = (x a, x b, x c)
[...] Instead of making a bunch of type aliases you can now say And then some TH: makeTypes
[d| data User
= User {id :: Int, firstName :: String, lastName :: String}
deriving (Show)
|]
======>
data UserP a b c
= User {id :: a, firstName :: b, lastName :: c}
deriving (Show)
type User = UserP Int String String
type instance To x (UserP a b c) = UserP (x a) (x b) (x c) And to define a table (no TH for this yet): userTable :: Table (To Wire User)
userTable = Table "users"
User
{ id = Wire "id"
, firstName = Wire "first_name"
, lastName = Wire "last_name"
} I also aliased insert
:: ( Default TableMaybeWrapper wires maybewires
, Default (PPOfContravariant Assocer) maybewires maybewires
, maybewires ~ To Maybe wires
)
=> Connection -> Table wires -> Expr maybewires -> IO Int64
insert = runInsertConnDef which constrains the type a bit so you don't need a type signature for I also have a type family type family Ins a :: *
type instance Ins (User a b c) = UserP () b c
runInsertUser :: Ins User -> Connection -> IO ()
runInsertUser (User () b c) conn = insert conn userTable $ makeMaybeExpr (Nothing :: Maybe Int) (Just b) (Just c) |
Another issue that I'd love to have dealt with via TH or some convention would be to have normal, non-polymorphic versions of the records, with some easy conversion. Especially when starting out on projects, it's really nice to be able to take advantage of the TH derived instances (in libraries I regularly use: aeson, digestive-functors, heist), which doesn't work with the type aliases (at least I've confirmed aeson won't, and assume the rest won't). So it might be nice to have a record data User = User { id :: Int, name :: String } deriving (Show, Eq)
makeOpal ''User where data UserP a b = UserP { id :: a, name :: b }
type UserO = UserP Int String
...
instance OpalConvert UserO User where
toConcrete (UserP i n) = User i n
fromConcrete (User i n) = UserP i n Though perhaps people have better ideas how to accomplish this? |
What makes you say aeson won't work? Seems fine to me, using type User = UserP Int String String
instance ToJSON User |
Also note that in your example Too early for me to tell if a separate concrete type "inside" opaleye code is useful. We already have concrete types for everything that we would map this to so I think it wouldn't give us much. |
@bergmark Manually defining instances works fine. It's 'deriveJSON' and similar functions that don't work. |
|
Here's the code if anyone wants to play with it, first time writing TH for me so it's probably terrible! :) https://gist.github.com/bergmark/1fa4331e00f329be306c |
Here's another type family that seems useful. With the code above we can say things like type family MapTo (t :: * -> *) a :: *
type instance MapTo x (a,b) = (To x a, To x b)
allUsersTwice :: Query (MapTo Wire (User, User))
allUsersTwice = arr (id &&& id) . queryTable userTable |
This is interesting. What is |
See this comment |
So it applies |
Right, |
And you can also do |
Is it possible to do |
I'm not sure, maybe @hesselink knows? |
If you do a left join you end up with |
Nope, as far as I know you cannot partially apply a type family. The same goes for any type synonym, in fact. That's why we made |
This seems like it will be problematic then, as a different combinator will be required for every level of nesting. |
Well, the usual way around it is to have a data type corresponding to your type family, and an extra type family
This might be able to do what you want, though I'm not sure it will be pretty. |
I don't think it's problematic with |
The more I see |
I like |
I came up with a scheme based on https://github.com/karamaan/karamaan-opaleye/blob/dev/TODO.md |
If we have a
Person
table with columnsname :: string, height :: double, birthday :: date
, we would represent it with a type likeand give it synonyms like
If you want to insert a Person you have to come up with a value
myRow
of typeThis proliferation of types becomes tedious. Some generic way to make the types would be nice, TH or otherwise.
The text was updated successfully, but these errors were encountered: