1 {-# LANGUAGE AllowAmbiguousTypes #-}
2 {-# LANGUAGE DataKinds #-}
3 {-# LANGUAGE DefaultSignatures #-}
4 {-# LANGUAGE TypeFamilyDependencies #-}
5 {-# LANGUAGE UndecidableInstances #-} -- for type instance defaults
6 module Symantic.CLI.API where
8 import Control.Applicative (Applicative(..), Alternative(..))
9 import Control.Monad (Monad(..))
11 import Data.Char (Char)
13 import Data.Function (($), (.), id)
15 import Data.Kind (Constraint)
16 import Data.Maybe (Maybe(..), maybe)
17 import Data.Monoid (Monoid)
18 import Data.String (String)
19 import Data.Text (Text)
20 import Text.Show (Show)
21 import Data.Functor (Functor(..), (<$>))
25 (<.>) :: repr a b -> repr b c -> repr a c
30 repr a b -> repr b c -> repr a c
31 x <.> y = noTrans (unTrans x <.> unTrans y)
36 (<!>) :: repr a k -> repr b k -> repr (a:!:b) k
37 opt :: repr (a->k) k -> repr (Maybe a->k) k
42 repr a k -> repr b k -> repr (a:!:b) k
43 x <!> y = noTrans (unTrans x <!> unTrans y)
47 repr (a->k) k -> repr (Maybe a->k) k
48 opt = noTrans . opt . unTrans
52 -- | Like @(,)@ but @infixr@.
53 data (:!:) a b = a:!:b
58 dimap :: (a->b) -> (b->a) -> repr (a->k) k -> repr (b->k) k
63 (a->b) -> (b->a) -> repr (a->k) k -> repr (b->k) k
64 dimap a2b b2a = noTrans . dimap a2b b2a . unTrans
67 class AltApp repr where
68 many0 :: repr (a->k) k -> repr ([a]->k) k
69 many1 :: repr (a->k) k -> repr ([a]->k) k
72 AltApp (UnTrans repr) =>
73 repr (a->k) k -> repr ([a]->k) k
76 AltApp (UnTrans repr) =>
77 repr (a->k) k -> repr ([a]->k) k
78 many0 = noTrans . many0 . unTrans
79 many1 = noTrans . many1 . unTrans
81 -- * Class 'Permutable'
82 class Permutable repr where
83 -- Use @TypeFamilyDependencies@ to help type-inference infer @(repr)@.
84 type Permutation (repr:: * -> * -> *) = (r :: * -> * -> *) | r -> repr
85 type Permutation repr = Permutation (UnTrans repr)
86 runPermutation :: Permutation repr k a -> repr (a->k) k
87 toPermutation :: repr (a->k) k -> Permutation repr k a
88 toPermDefault :: a -> repr (a->k) k -> Permutation repr k a
90 -- | Convenient wrapper to omit a 'runPermutation'.
93 -- opts '<?>' next = 'runPermutation' opts '<.>' next
96 App repr => Permutable repr =>
97 Permutation repr b a -> repr b c -> repr (a->b) c
98 opts <?> next = runPermutation opts <.> next
105 type Segment = String
107 -- * Class 'CLI_Command'
108 class CLI_Command repr where
109 command :: Name -> repr a k -> repr a k
112 class CLI_Var repr where
113 type VarConstraint repr a :: Constraint
114 var' :: VarConstraint repr a => Name -> repr (a->k) k
115 just :: a -> repr (a->k) k
118 type VarConstraint repr a = VarConstraint (UnTrans repr) a
121 CLI_Var (UnTrans repr) =>
122 VarConstraint (UnTrans repr) a =>
123 Name -> repr (a->k) k
126 CLI_Var (UnTrans repr) =>
130 CLI_Var (UnTrans repr) =>
132 var' = noTrans . var'
133 just = noTrans . just
134 nothing = noTrans nothing
136 -- | Like 'var'' but with the type variable @(a)@ first instead or @(repr)@
137 -- so it can be passed using 'TypeApplications' without adding a |\@_| for @(repr)@.
141 VarConstraint repr a =>
142 Name -> repr (a->k) k
147 class CLI_Env repr where
148 type EnvConstraint repr a :: Constraint
149 env' :: EnvConstraint repr a => Name -> repr (a->k) k
151 type EnvConstraint repr a = EnvConstraint (UnTrans repr) a
154 CLI_Env (UnTrans repr) =>
155 EnvConstraint (UnTrans repr) a =>
156 Name -> repr (a->k) k
157 env' = noTrans . env'
159 -- | Like 'env'' but with the type enviable @(a)@ first instead or @(repr)@
160 -- so it can be passed using 'TypeApplications' without adding a |\@_| for @(repr)@.
164 EnvConstraint repr a =>
165 Name -> repr (a->k) k
177 class (App repr, Permutable repr, CLI_Var repr) => CLI_Tag repr where
178 type TagConstraint repr a :: Constraint
179 tagged :: Tag -> repr f k -> repr f k
182 -- tagged n = (tag n <.>)
183 short :: TagConstraint repr a => Char -> repr (a->k) k -> Permutation repr k a
184 short n = toPermutation . tagged (TagShort n)
185 long :: TagConstraint repr a => Name -> repr (a->k) k -> Permutation repr k a
186 long n = toPermutation . tagged (TagLong n)
188 option :: TagConstraint repr a => a -> repr (a->k) k -> Permutation repr k a
189 option = toPermDefault
190 flag :: TagConstraint repr Bool => Tag -> Permutation repr k Bool
191 flag n = toPermDefault False $ tagged n $ just True
192 shortOpt :: TagConstraint repr a => Char -> a -> repr (a->k) k -> Permutation repr k a
193 shortOpt n a = toPermDefault a . tagged (TagShort n)
194 longOpt :: TagConstraint repr a => Name -> a -> repr (a->k) k -> Permutation repr k a
195 longOpt n a = toPermDefault a . tagged (TagLong n)
198 type TagConstraint repr a = TagConstraint (UnTrans repr) a
201 CLI_Tag (UnTrans repr) =>
202 Tag -> repr f k -> repr f k
205 CLI_Tag (UnTrans repr) =>
207 tagged n = noTrans . tagged n . unTrans
208 endOpts = noTrans endOpts
210 -- * Class 'CLI_Response'
211 class CLI_Response repr where
212 type ResponseConstraint repr a :: Constraint
213 type ResponseArgs repr a :: * -- = (r:: *) | r -> a
214 type Response repr :: *
216 ResponseConstraint repr a =>
217 repr (ResponseArgs repr a)
220 type ResponseConstraint repr a = ResponseConstraint (UnTrans repr) a
221 type ResponseArgs repr a = ResponseArgs (UnTrans repr) a
222 type Response repr = Response (UnTrans repr)
226 CLI_Response (UnTrans repr) =>
227 ResponseConstraint (UnTrans repr) a =>
228 ResponseArgs repr a ~ ResponseArgs (UnTrans repr) a =>
229 Response repr ~ Response (UnTrans repr) =>
230 repr (ResponseArgs repr a)
232 response' = noTrans (response' @_ @a)
237 ResponseConstraint repr a =>
238 repr (ResponseArgs repr a)
240 response = response' @repr @a
241 {-# INLINE response #-}
243 -- * Class 'CLI_Help'
244 class CLI_Help repr where
245 type HelpConstraint repr d :: Constraint
246 help :: HelpConstraint repr d => d -> repr f k -> repr f k
248 program :: Name -> repr f k -> repr f k
249 rule :: Name -> repr f k -> repr f k
251 type HelpConstraint repr d = HelpConstraint (UnTrans repr) d
254 CLI_Help (UnTrans repr) =>
255 Name -> repr f k -> repr f k
258 CLI_Help (UnTrans repr) =>
259 Name -> repr f k -> repr f k
260 program n = noTrans . program n . unTrans
261 rule n = noTrans . rule n . unTrans
265 class Trans repr where
266 -- | The @(repr)@esentation that @(repr)@ 'Trans'forms.
267 type UnTrans repr :: * -> * -> *
268 -- | Lift the underlying @(repr)@esentation to @(repr)@.
269 -- Useful to define a combinator that does nothing in a 'Trans'formation.
270 noTrans :: UnTrans repr a b -> repr a b
271 -- | Unlift a @(repr)@esentation. Useful when a 'Trans'formation
272 -- combinator needs to access the 'UnTrans'formed @(repr)@esentation,
273 -- or at the end to get the underlying 'UnTrans'formed @(repr)@esentation
274 -- from the inferred @(repr)@ value (eg. in 'server').
275 unTrans :: repr a b -> UnTrans repr a b