on 04fdf27 help sign in
rel8/src/Rel8/Schema/Generic.hs
193 lines 5.9 kB history raw
1{-# language AllowAmbiguousTypes #-}
2{-# language DataKinds #-}
3{-# language DisambiguateRecordFields #-}
4{-# language DefaultSignatures #-}
5{-# language FlexibleContexts #-}
6{-# language FlexibleInstances #-}
7{-# language GADTs #-}
8{-# language MultiParamTypeClasses #-}
9{-# language QuantifiedConstraints #-}
10{-# language ScopedTypeVariables #-}
11{-# language StandaloneKindSignatures #-}
12{-# language TypeApplications #-}
13{-# language TypeFamilies #-}
14{-# language TypeOperators #-}
15{-# language UndecidableInstances #-}
17{-# options_ghc -fno-warn-orphans #-}
19module Rel8.Schema.Generic
20 ( Rel8able
21 )
22where
24-- base
25import Data.Kind ( Constraint, Type )
26import GHC.Generics
27 ( Generic, Rep, from, to
28 , (:*:)( (:*:) ), K1( K1 ), M1( M1 )
29 , D, S
30 , Meta( MetaSel )
31 )
32import qualified GHC.Generics as G ( C )
33import GHC.TypeLits ( KnownSymbol )
34import Prelude
35import Unsafe.Coerce ( unsafeCoerce )
37-- rel8
38import Rel8.Schema.Context ( Col )
39import Rel8.Schema.Context.Label ( Labelable, labeler, unlabeler )
40import Rel8.Schema.Field ( Reify, Reifiable, hreify, hunreify )
41import Rel8.Schema.HTable ( HTable )
42import Rel8.Schema.HTable.Label ( HLabel, hlabel, hunlabel )
43import Rel8.Schema.HTable.Pair ( HPair(..) )
44import qualified Rel8.Schema.Kind as K
45import Rel8.Schema.Name ( Name )
46import Rel8.Table
47 ( Table, Columns, Context, fromColumns, toColumns
48 )
51instance
52 ( Rel8able t
53 , Labelable context
54 , Reifiable context
55 ) => Table context (t context)
56 where
57 type Columns (t context) = GRep t
58 type Context (t context) = context
60 fromColumns = unreify . gfromColumns . hreify
61 toColumns = hunreify . gtoColumns . reify
64-- | This type class allows you to define custom 'Table's using higher-kinded
65-- data types. Higher-kinded data types are data types of the pattern:
66--
67-- @
68-- data MyType f =
69-- MyType { field1 :: Column f T1 OR HK1 f
70-- , field2 :: Column f T2 OR HK2 f
71-- , ...
72-- , fieldN :: Column f Tn OR HKn f
73-- }
74-- @
75--
76-- where @Tn@ is any Haskell type, and @HKn@ is any higher-kinded type.
77--
78-- That is, higher-kinded data are records where all fields in the record are
79-- all either of the type @Column f T@ (for any @T@), or are themselves
80-- higher-kinded data:
81--
82-- [Nested]
83--
84-- @
85-- data Nested f =
86-- Nested { nested1 :: MyType f
87-- , nested2 :: MyType f
88-- }
89-- @
90--
91-- The @Rel8able@ type class is used to give us a special mapping operation
92-- that lets us change the type parameter @f@.
93--
94-- [Supplying @Rel8able@ instances]
95--
96-- This type class should be derived generically for all table types in your
97-- project. To do this, enable the @DeriveAnyType@ and @DeriveGeneric@ language
98-- extensions:
99--
100-- @
101-- \{\-\# LANGUAGE DeriveAnyClass, DeriveGeneric #-\}
102--
103-- data MyType f = MyType { fieldA :: Column f T }
104-- deriving ( GHC.Generics.Generic, Rel8able )
105-- @
106type Rel8able :: K.Table -> Constraint
107class HTable (GRep t) => Rel8able t where
108 gfromColumns :: (Labelable context, Reifiable context)
109 => GRep t (Col (Reify context)) -> t (Reify context)
111 gtoColumns :: (Labelable context, Reifiable context)
112 => t (Reify context) -> GRep t (Col (Reify context))
114 default gfromColumns :: forall context.
115 ( Generic (t (Reify context))
116 , GColumns (Rep (t (Reify context))) ~ GRep t
117 , GRel8able context (Rep (t (Reify context)))
118 ) => GRep t (Col (Reify context)) -> t (Reify context)
119 gfromColumns = to . fromGColumns @_ @(Rep (t (Reify context)))
121 default gtoColumns :: forall context.
122 ( Generic (t (Reify context))
123 , GColumns (Rep (t (Reify context))) ~ GRep t
124 , GRel8able context (Rep (t (Reify context)))
125 ) => t (Reify context) -> GRep t (Col (Reify context))
126 gtoColumns = toGColumns @_ @(Rep (t (Reify context))) . from
129type GRep :: K.Table -> K.HTable
130type GRep t = GColumns (Rep (t (Reify Name)))
133type GColumns :: (Type -> Type) -> K.HTable
134type family GColumns rep where
135 GColumns (M1 D _ rep) = GColumns rep
136 GColumns (M1 G.C _ rep) = GColumns rep
137 GColumns (rep1 :*: rep2) = HPair (GColumns rep1) (GColumns rep2)
138 GColumns (M1 S ('MetaSel ('Just label) _ _ _) (K1 _ a)) =
139 HLabel label (Columns a)
142type GRel8able :: K.Context -> (Type -> Type) -> Constraint
143class GRel8able context rep where
144 fromGColumns :: GColumns rep (Col (Reify context)) -> rep x
145 toGColumns :: rep x -> GColumns rep (Col (Reify context))
148instance GRel8able context rep => GRel8able context (M1 D c rep) where
149 fromGColumns = M1 . fromGColumns @context @rep
150 toGColumns (M1 a) = toGColumns @context @rep a
153instance GRel8able context rep => GRel8able context (M1 G.C c rep) where
154 fromGColumns = M1 . fromGColumns @context @rep
155 toGColumns (M1 a) = toGColumns @context @rep a
158instance (GRel8able context rep1, GRel8able context rep2) =>
159 GRel8able context (rep1 :*: rep2)
160 where
161 fromGColumns (HPair a b) =
162 fromGColumns @context @rep1 a :*: fromGColumns @context @rep2 b
163 toGColumns (a :*: b) =
164 HPair (toGColumns @context @rep1 a) (toGColumns @context @rep2 b)
167instance
168 ( Table (Reify context) a
169 , Labelable context
170 , KnownSymbol label
171 , GColumns (M1 S meta k1) ~ HLabel label (Columns a)
172 , meta ~ 'MetaSel ('Just label) _su _ss _ds
173 , k1 ~ K1 i a
174 ) => GRel8able context (M1 S meta k1)
175 where
176 fromGColumns = M1 . K1 . fromColumns . hunlabel unlabeler
177 toGColumns (M1 (K1 a)) = hlabel labeler (toColumns a)
180reify ::
181 (-- Rel8able t
182 --, forall necessity a. Coercible (Field context necessity a) (AField context necessity a) => Coercible (t context) (t (Reify context))
183 )
184 => t context -> t (Reify context)
185reify = unsafeCoerce
188unreify ::
189 (-- Rel8able t
190 --, forall necessity a. Coercible (AField context necessity a) (Field context necessity a) => Coercible (t (Reify context)) (t context)
191 )
192 => t (Reify context) -> t context
193unreify = unsafeCoerce