{-# LANGUAGE BangPatterns, CPP, ConstraintKinds, DataKinds,
FlexibleContexts, FlexibleInstances, GADTs, InstanceSigs,
KindSignatures, LambdaCase, MultiParamTypeClasses,
OverloadedStrings, QuasiQuotes, RankNTypes,
ScopedTypeVariables, TemplateHaskell, TypeApplications,
TypeFamilies, TypeOperators, UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Frames.ColumnUniverse (
CoRec, Columns, ColumnUniverse, ColInfo,
CommonColumns, CommonColumnsCat, parsedTypeRep
) where
import Data.Maybe (fromMaybe)
#if __GLASGOW_HASKELL__ < 808
import Data.Semigroup (Semigroup((<>)))
#endif
import qualified Data.Text as T
import Data.Vinyl
import Data.Vinyl.CoRec
import Data.Vinyl.Functor
import Data.Vinyl.TypeLevel (RIndex, NatToInt)
import Frames.ColumnTypeable
import Frames.Categorical
import Language.Haskell.TH
inferParseable :: Parseable a => T.Text -> (Maybe :. Parsed) a
inferParseable = Compose . parse
inferParseable' :: Parseable a => (((->) T.Text) :. (Maybe :. Parsed)) a
inferParseable' = Compose inferParseable
tryParseAll :: forall ts. (RecApplicative ts, RPureConstrained Parseable ts)
=> T.Text -> Rec (Maybe :. Parsed) ts
tryParseAll = rtraverse getCompose funs
where funs :: Rec (((->) T.Text) :. (Maybe :. Parsed)) ts
funs = rpureConstrained @Parseable inferParseable'
newtype ColInfo a = ColInfo (Either (String -> Q [Dec]) Type, Parsed a)
instance Show a => Show (ColInfo a) where
show (ColInfo (t,p)) = "(ColInfo {"
++ either (const "cat") show t
++ ", "
++ show (discardConfidence p) ++"})"
parsedToColInfo :: Parseable a => Parsed a -> ColInfo a
parsedToColInfo x = case getConst rep of
Left dec -> ColInfo (Left dec, x)
Right ty ->
ColInfo (Right ty, x)
where rep = representableAsType x
parsedTypeRep :: ColInfo a -> Parsed Type
parsedTypeRep (ColInfo (t,p)) =
const (either (const (ConT (mkName "Categorical"))) id t) <$> p
orderParsePriorities :: Parsed (Maybe Type) -> Maybe Int
orderParsePriorities x =
case discardConfidence x of
Nothing -> Just 1
Just t
| t == tyText -> Just (0 + uncertainty)
| t == tyDbl -> Just (2 + uncertainty)
| t == tyInt -> Just (3 + uncertainty)
| t == tyBool -> Just (4 + uncertainty)
| otherwise -> Nothing
where tyText = ConT (mkName "Text")
tyDbl = ConT (mkName "Double")
tyInt = ConT (mkName "Int")
tyBool = ConT (mkName "Bool")
uncertainty = case x of Definitely _ -> 0; Possibly _ -> 5
lubTypes :: Parsed (Maybe Type) -> Parsed (Maybe Type) -> Maybe Ordering
lubTypes x y = compare <$> orderParsePriorities y <*> orderParsePriorities x
instance (T.Text ∈ ts, RPureConstrained Parseable ts) => Monoid (CoRec ColInfo ts) where
mempty = CoRec (ColInfo ( Right (ConT (mkName "Text")), Possibly T.empty))
mappend x y = x <> y
mergeEqTypeParses :: forall ts. (RPureConstrained Parseable ts, T.Text ∈ ts)
=> CoRec ColInfo ts -> CoRec ColInfo ts -> CoRec ColInfo ts
mergeEqTypeParses x@(CoRec _) y = fromMaybe definitelyText
$ coRecTraverse getCompose
(coRecMapC @Parseable aux x)
where definitelyText = CoRec (ColInfo (Right (ConT (mkName "Text")), Definitely T.empty))
aux :: forall a. (Parseable a, NatToInt (RIndex a ts))
=> ColInfo a -> (Maybe :. ColInfo) a
aux (ColInfo (_, pX)) =
case asA' @a y of
Nothing -> Compose Nothing
Just (ColInfo (_, pY)) ->
maybe (Compose Nothing)
(Compose . Just . parsedToColInfo)
(parseCombine pX pY)
instance (T.Text ∈ ts, RPureConstrained Parseable ts)
=> Semigroup (CoRec ColInfo ts) where
x@(CoRec (ColInfo (tyX, pX))) <> y@(CoRec (ColInfo (tyY, pY))) =
case lubTypes (const (either (const Nothing) Just tyX) <$> pX)
(const (either (const Nothing) Just tyY) <$> pY) of
Just GT -> x
Just LT -> y
Just EQ -> mergeEqTypeParses x y
Nothing -> mempty
bestRep :: forall ts.
(RPureConstrained Parseable ts,
FoldRec ts ts,
RecApplicative ts, T.Text ∈ ts)
=> T.Text -> CoRec ColInfo ts
bestRep t
| T.null t || t == "NA" = (CoRec (parsedToColInfo (Possibly T.empty)))
| otherwise = coRecMapC @Parseable parsedToColInfo
. fromMaybe (CoRec (Possibly T.empty :: Parsed T.Text))
. firstField
. (tryParseAll :: T.Text -> Rec (Maybe :. Parsed) ts)
$ t
{-# INLINABLE bestRep #-}
instance (RPureConstrained Parseable ts, FoldRec ts ts,
RecApplicative ts, T.Text ∈ ts) =>
ColumnTypeable (CoRec ColInfo ts) where
colType (CoRec (ColInfo (t, _))) = t
{-# INLINE colType #-}
inferType = bestRep
{-# INLINABLE inferType #-}
#if !MIN_VERSION_vinyl(0,11,0)
instance forall ts. (RPureConstrained Show ts, RecApplicative ts)
=> Show (CoRec ColInfo ts) where
show x = "(Col " ++ onCoRec @Show show x ++")"
#endif
type CommonColumns = [Bool, Int, Double, T.Text]
type CommonColumnsCat = [Bool, Int, Double, Categorical 8, T.Text]
type ColumnUniverse = CoRec ColInfo
type Columns = ColumnUniverse CommonColumns