21 | %language ElabReflection
24 | %hide Data.List.Alternating.infixr.(<+)
25 | %hide Data.List.Alternating.infixl.(<+)
26 | %hide Data.String.Extra.infixr.(<+)
27 | %hide Data.String.Extra.infixl.(+>)
29 | -- A todo app is a very simple example of a real-world application with
30 | -- a tangible user experience and engineering challenges that are representative
31 | -- of the ones encountered in shipping software.
33 | ----------------------------------------------------------------------------------
34 | -- Datatypes --
35 | ----------------------------------------------------------------------------------
36 | -- First we define the type for our to-do items.
38 | -- Todo items will be identified with an ID
43 | -- And a status to indicate if the item is marked as done.
62 | -- The to-do item in itself is a record of an id, a content string and
63 | -- a status
80 | -- The todo app will display a list of items and provide 4 key functions:
81 | -- - create a new item
82 | -- - delete an existing item
83 | -- - mark an existing item as complete
84 | -- - Return the list of all items
85 | --
86 | -- A command is data sent from the client for the server to answer.
95 | -- CmdReturn is the type returned by each command
96 | -- All commands return Unit since they only perform a side-effect, except
97 | -- for Retrieve, it returns a list of todo items.
104 | ----------------------------------------------------------------------------------
105 | -- SQL --
106 | ----------------------------------------------------------------------------------
108 | -- A valid query is one with a type that has the appropriate instances
109 | -- for row conversions
117 | -- We can run any valid query and obtain the list of rows for that query
121 | -- The schema of our database. We onlye have
122 | -- a single table with all the todo items.
129 | ]
131 | -- We hard-code a SQL command to create our table. This is going to
132 | -- be called when we setup the table
140 | ]
142 | -- SQL command to add an item to the todo table
146 | -- SQL command to delete an item from the todo table
150 | -- SQL command to mark an item done
154 | -- SQL query to obtain all the todo items
158 | ----------------------------------------------------------------------------------
159 | -- Containers --
160 | ----------------------------------------------------------------------------------
162 | -- The errors that our program will deal with
166 | -- The monad in which we run our program as an endofunctor in Cont
170 | -- The CLI is represented as a container that expects strings as input
171 | -- and returns strings as output.
175 | -- AppOp is the abstract API of our app, we take in commands, and returns
176 | -- either unit, or tables from queries. Will run in FM.
180 | -- Interface for database commands. Commands only perform side-effects so the
181 | -- results are always Unit
185 | -- Interface of a database queries. Queries always return tables of a matching type
189 | ----------------------------------------------------------------------------------
190 | -- Containers Morphisms --
191 | ----------------------------------------------------------------------------------
193 | ---------------------------------
194 | -- Running database operations --
195 | ---------------------------------
197 | -- Running a command as a costate
201 | -- Running a query as a costate
205 | -- Running either a command or a query on a database as a costate
210 | ---------------------------------------
211 | -- Interacting with the command line --
212 | ---------------------------------------
214 | -- We need a parser to parse command line arguments and translate them into
215 | -- internal Todo commands.
226 | -- Convert a string-based api into the AppOp interface
227 | -- MaybeA is the error monad where all errors need to be handled
230 | where
242 | -- The parser lifted into effectful bidirectional programs
243 | -- Note that the parser itself does not concern itself with effects but
244 | -- we can lift it to make to compatible with effectful programs.
248 | ---------------------------------------
249 | -- Converting to database operations --
250 | ---------------------------------------
252 | -- Converting from internal app commands to database commands
253 | -- Again, notice that this is a pure interface, but the monad on containers
254 | -- will make this compatible with the rest of the infrastructure.
257 | where
271 | -- Commands translated to database operations lifted into the correct monads
272 | -- We lift our translatin to database operation through both FM and MaybeA
276 | -- The application itself as a Costate of FM CLI
277 | -- - First run the parser
278 | -- - Then convert commands into database operations
279 | -- - Then run the database
283 | ----------------------------------------------------------------------------------
284 | -- Running the app as a REPL --
285 | ----------------------------------------------------------------------------------
287 | partial
295 | -- Running the repl with our String -> String function extracted from the
296 | -- costate `Costate (FM CLI)`
297 | partial
301 | -- Running the database commands to setup an initial state
302 | partial
307 | -- Running the main app using RIO
308 | partial