Unify function and nullaryFunction#258
Conversation
There was a problem hiding this comment.
I think I'm overall on board with the idea (I was just in a bit of a foul mood yesterday 😅). That said, I think removing nullaryFunction is going a bit too far. If we have function and nullaryFunction we can get rid of the Arguments type class entirely, and things become even simpler IMO (fewer concepts to understand).
Alternatively, is it possible to just give () a Table instance? I haven't tried it, but looking at https://hackage.haskell.org/package/rel8-1.4.1.0/docs/Rel8.html#t:Table it feels like it might be possible? Columns is just a constant functor, but maybe the context stuff causes it to fall apart.
This also needs a changelog entry.
|
I knew you wouldn't like |
|
And yeah, it's not really possible to make |
My thinking was actually that
I just don't like type classes for this kind of overloading because it's tedious to interactively understand. If you're using holes, a
Thanks, of course I wasn't quite aware of that based on what was in this PR, but that does explode the API more (though it at least still has some symmetry). I'll present just one more alternative, which is again to ditch the type class, but to replace with a simple sum-type: function :: Sql DBType a => QualifiedName -> Arguments a -> Expr b
data Arguments a where
NoArguments :: Arguments ()
Arguments :: Table Expr a => a -> Arguments a(I wouldn't go further than this. E.g., I wouldn't go towards a fullblown HList or anything). It's a tiny bit more work to use: rem :: Sql DBIntegral a => Expr a -> Expr a -> Expr a
rem n d = function "rem" $ Argument (n, d)
now :: Expr UTCTime
now = function "now" NoArgumentsBut I think it's absolutely obvious what's going on. With this I think we've exhausted all options:
My vote is the GADT as everything becomes very self-contained, but I'll leave it with you to pick which you think is best as my +1 is fairly weak. I think any of 2/3/4 improve the current situation. |
70a1637 to
1684ce5
Compare
|
I prefer 2 to 3 or 4. In PostgreSQL, a function is just a function, whether it takes zero or more arguments. A As for the GADT approach, I understand your point about typed holes, but it feels pretty weak here. There's not much that GHC can tell you about what argument to supply to ghci> Rel8.function "foo" _
<interactive>:3:2: error:
• Overlapping instances for Arguments arguments0
arising from a use of ‘function’
Matching instance:
instance Table Expr a => Arguments a
-- Defined at src/Rel8/Expr/Function.hs:43:10
Potentially matching instance:
instance [overlap ok] Arguments ()
-- Defined at src/Rel8/Expr/Function.hs:47:27
(The choice depends on the instantiation of ‘arguments0’
To pick the first instance above, use IncoherentInstances
when compiling the other instance declarations)
• In the expression: function "foo" _
In an equation for ‘it’: it = function "foo" _Maybe it's a little bit confusing and indirect but GHC is basically saying "you can either put a Really though, I just have really hard time believing that even you would rather type |
|
Ah, I didn't remember that GHC told us about potential instances. That does weaken my argument about typed holes. |
c0e44bd to
e65d852
Compare
1684ce5 to
9962101
Compare
This does away with the weird variadic arguments thing we had going on with `function`. Functions with no arguments are now written as: ```haskell now :: Expr UTCTime now = function "now" () ``` Functions with multiple arguments are now written as: ```haskell quot :: Sql DBIntegral a => Expr a -> Expr a -> Expr a quot n d = function "div" (n, d) ``` Single-argument functions are written exactly as before.
9962101 to
fec9a3a
Compare
This does away with the weird variadic arguments thing we had going on with
function.Functions with no arguments are now written as:
Functions with multiple arguments are now written as:
Single-argument functions are written exactly as before.