I have two GADTs I am using to model a SQL EDSL. To keep the client facing api clean and simple I want to use OverloadedStrings
to cast string literals to a Column Selection
.
Therefore you can simply type
select ["a", "b"] $ from tbl
instead of
select [Column "a", Column "b"] $ from tbl
The problem is that select allows for both Column Selection
s and Reduction
s
to allow for queries that perform aggregations.
mean :: Column Selection -> Column Reduction
select :: [Column a] -> Query b -> Query Selection
select [mean "a"] $ from tbl
and thus the strings are ambiguous in this context of [Column a]
.
But select [mean "a"] $ from tbl
is valid since mean
provides the necessary context to infer that the string literal is a Column Selection.
Can anyone recommend a way out of this mess?
My current code below (irrelevant instances omitted)
{-# LANGUAGE
GADTs
, RankNTypes
, DataKinds
, TypeFamilies
, FlexibleContexts
, FlexibleInstances
, OverloadedStrings #-}
data Sz = Selection | Reduction deriving Show
data Schema = Schema{name :: Maybe String, spec :: [Column Selection]}
type family ColOp (a :: Sz) (b :: Sz) where
ColOp Selection Selection = Selection
ColOp Selection Reduction = Selection
ColOp Reduction Selection = Selection
ColOp Reduction Reduction = Reduction
data Column (a :: Sz) where
Column :: String -> Column Selection
Assign :: String -> Column a -> Column a
FKey :: String -> Schema -> Column Selection
BinExpr :: BinOp -> Column a -> Column b -> Column (ColOp a b)
LogExpr :: LogOp -> Column a -> Column b -> Column Selection
AggExpr :: AggOp -> Column Selection -> Column Reduction
instance IsString (Column Selection) where
fromString s = Column s
data Query (a :: Sz) where
Table :: Schema -> Query Selection
Select :: [Column a] -> Query b -> Query Selection
Update :: [Column a] -> Query b -> Query Selection
Where :: [Column Selection] -> Query Selection -> Query Selection
Group :: [Column Selection] -> Query Selection -> Query Reduction
I would also like to make the following signature fail for Select
/Update
:
[Column Selection] -> Query Reduction -> Query Selection
But that's a whole other can of worms...
The compiler is correct to give you an ambiguous type error for Select ["a"]
- the IsString (Column Selection)
instance can be chosen only if a priori the argument to Column
is known to be Selection
. This is exactly the intended behaviour.
What you want is the following:
instance (x ~ Selection) => IsString (Column x) where
fromString = Column
This will allow the compiler to infer that "x" :: Column _
must actually be "x" :: Column Selection
, as opposed to requiring it.
Select [mean "a"]
is a completely different situation - since mean :: Column Selection -> Column Reduction
, the compiler knows, before instance selection happens, that "a" :: Column Selection
, because the type of mean
forces this to be the case.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With