2016-03-02 27 views
9

Ho due GADT che sto usando per modellare una SQL EDSL. Per mantenere il client di fronte api pulito e semplice, desidero utilizzare OverloadedStrings per convertire valori letterali stringa su Column Selection.Come risolvere l'ambiguità nei miei GADT

Pertanto è sufficiente digitare

select ["a", "b"] $ from tbl 

invece di

select [Column "a", Column "b"] $ from tbl 

Il problema è che select consente sia Column Selection s e Reduction s per consentire per le query che eseguono aggregazioni.

mean :: Column Selection -> Column Reduction 

select :: [Column a] -> Query b -> Query Selection 
select [mean "a"] $ from tbl 

e quindi le corde sono ambigui in questo contesto di [Column a]. Ma select [mean "a"] $ from tbl è valido dal mean fornisce il contesto necessario per dedurre che la stringa letterale è una selezione di colonne.

Qualcuno può consigliare un modo per uscire da questo pasticcio?

Il mio codice corrente al di sotto (casi irrilevanti omessi)

{-# 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 

Vorrei anche fare la seguente firma falliscono per Select/Update:

[Column Selection] -> Query Reduction -> Query Selection 

ma questa è tutta un'altra lattina di worm ...

+1

'significa" a "' typechecks bene con questo codice, e il tipo dedotto del '" a "' è precisamente 'Colonna 'Selezione'. Quindi il tipo di "Seleziona [significa" a "]" è "Query b -> Query" Selezione ". Se qualcosa non funziona, includi con precisione quale espressione causa l'ambiguità e qual è l'errore effettivo. Non hai definito 'from' o' tbl' ovunque, quindi forse l'errore è legato a quelle funzioni. – user2407038

+0

right ma 'select ["a"] $ da tbl' fa * not * typecheck. –

+0

'from = Table' e' tbl = Schema {name = Just "tbl", spec = [Colonna "a", Colonna "b"]} ' –

risposta

6

Il compilatore è corretto per darti un errore di tipo ambiguo per Select ["a"] - l'istanza IsString (Column Selection) può essere scelta solo se a priori l'argomento a Column è noto per essere Selection. Questo è esattamente il comportamento previsto.

quello che vuoi è il seguente:

instance (x ~ Selection) => IsString (Column x) where 
    fromString = Column 

Questo permetterà al compilatore di dedurre che "x" :: Column _ deve effettivamente essere "x" :: Column Selection, al contrario di richiedono esso.

Select [mean "a"] è una situazione completamente diversa - dal mean :: Column Selection -> Column Reduction, il compilatore sa, prima della selezione esempio accade, che "a" :: Column Selection, perché il tipo di mean forze che questo sia il caso.