Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to resolve ambiguity in my GADTs

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 Selections and Reductions 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...

like image 715
Brandon Ogle Avatar asked Mar 02 '16 20:03

Brandon Ogle


1 Answers

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.

like image 168
user2407038 Avatar answered Sep 19 '22 17:09

user2407038