diff --git a/level02/src/FirstApp/Main.hs b/level02/src/FirstApp/Main.hs index 15be411..bab9c13 100644 --- a/level02/src/FirstApp/Main.hs +++ b/level02/src/FirstApp/Main.hs @@ -52,9 +52,10 @@ resp400 = error "resp400 not implemented" -- | --- These helpers will take the raw request information and turn it into --- one of our data types. This means we draw a line about where the unruly outside --- world must end, and where the well-typed world of our application begins. +-- These next few functions will take the raw request information and turn it +-- into one of our data types. This means we draw a line about where the unruly +-- outside world must end, and where the well-typed world of our application +-- begins. mkAddRequest :: Text -> LBS.ByteString @@ -62,9 +63,10 @@ mkAddRequest mkAddRequest = error "mkAddRequest not implemented" --- This has other benefits, we're able isolate our validation requirements into the --- smallest chunks we can manage. This allows for fantastic reuse and it also means --- that validation is not spread across the application. It is kept at the borders. +-- This has other benefits, we're able isolate our validation requirements into +-- the smallest chunks we can manage. This allows for fantastic reuse and it +-- also means that validation is not spread across the application. It is kept +-- at the borders. mkViewRequest :: Text -> Either Error RqType @@ -73,7 +75,7 @@ mkViewRequest = -- Even though it may seem too trivial or even pointless to write functions such -- as these it allows for much greater consistency across the application. --- + -- Some of these are straight forward data constructors, but by doing it this -- way we don't have any snowflakes littered about the code. It also enhances -- our ability to spot larger patterns in our application, which are @@ -94,24 +96,24 @@ mkErrorResponse mkErrorResponse = error "mkErrorResponse not implemented" -{-| -Lets use our RqTypes to write a function that will take the input from the -Wai library and turn it into something our application cares about. --} + +-- Lets use our ``RqType`` helpers to now write a function that will take the +-- input ``Request`` from the Wai library and turn it into something our +-- application cares about. mkRequest :: Request -> IO ( Either Error RqType ) mkRequest = error "mkRequest not implemented" --- Notice how we're only accepting our predefined request types that have the --- required information already validated and prepared for use in the handling --- of the request. +-- In this next function, notice how we're only accepting our predefined request +-- types that have the required information already validated and prepared for +-- use in the handling of the request. -- -- If we find that we need more information to handle a request, or we have a --- new type of request that we'd like to handle then we update the RqType --- structure and the compiler will let us know the affected portions of our --- application. +-- new type of request that we'd like to handle then we update the ``RqType`` +-- structure and the compiler will let us know which parts of our application +-- are affected. -- -- Reduction of concerns such that each section of the application only deals -- with a small piece is one of the benefits of developing in this way. @@ -121,8 +123,8 @@ handleRequest handleRequest _ = error "handleRequest not implemented" --- Reimplement our `app` function using the new functions and the RqTypes as a --- guide. +-- Reimplement this function using the new functions and the ``RqType`` +-- constructors as a guide. app :: Application app = diff --git a/level02/src/FirstApp/Types.hs b/level02/src/FirstApp/Types.hs index 5a8e28e..eb06031 100644 --- a/level02/src/FirstApp/Types.hs +++ b/level02/src/FirstApp/Types.hs @@ -35,35 +35,33 @@ data RqType = RqType -- it's useful to be able to be descriptive about what went wrong. -- Think about some of the basic things that can wrong with our Requests and --- building the RqTypes, and create some values to represent that. For now we --- don't need to worry about things like malformed requests or invalid headers --- etc. +-- constructing a ``RqType``, and create some values to represent that. For now +-- we don't need to worry about things like malformed requests or invalid +-- headers etc. data Error = Error --- Provide a type to list our response content types so we don't try to --- do the wrong thing with what we meant to be used as text or JSON etc. +-- Provide a type to list our response content types so we don't try to do the +-- wrong thing with what we meant to be used as text or JSON etc. data ContentType = ContentType --- The ContentType description for a header doesn't match our data definition --- so we write a little helper function to pattern match on our ContentType --- value and provide the correct header value. + renderContentType :: ContentType -> ByteString renderContentType = error "renderContentType not implemented" --- In Haskell the `newtype` comes with zero runtime cost. It is purely used for --- type-checking. So when you have a bare 'primitive' value, like an Int, String, or --- even [a], you can wrap it up in a `newtype` for clarity. +-- In Haskell the ``newtype`` is a wrapper of sorts that comes with zero runtime +-- cost. It is purely used for type-checking. So when you have a bare primitive +-- value, like an ``Int``, ``String``, or even ``[a]``, you can wrap it up in a +-- ``newtype`` to give it a descriptive name to be more precise in your types. --- The type system will check it for you, and the compiler will eliminate the cost. +-- The type system will check it for you, and the compiler will eliminate the +-- cost of the "wrapper". Also, having specialised constructor functions for the +-- newtypes allows you to set extra restrictions, such as minimum values. --- Having specialised constructor functions for the newtypes allows you to set --- extra restrictions for your newtype. - --- Write two `newtype` definitions for `Topic` and `CommentText` that wrap a --- `Text` value +-- We've constructed the ``newtype`` definitions for ``Topic`` and +-- ``CommentText`` below. -- Topic newtype Topic = Topic Text @@ -74,9 +72,10 @@ newtype CommentText = CommentText Text deriving Show -- | --- A benefit of `newtype` is that we can choose to *not* export the constructor --- and provide a function of our own. In our case, we're not interested in empty --- `Text` values so we will eliminate them and immediately report an error. +-- We can choose to *not* export the constructor for a data type and instead +-- provide a function of our own. In our case, we're not interested in empty +-- `Text` values so we will eliminate them with a special constructor and return +-- an error if an empty input is provided. mkTopic :: Text -> Either Error Topic diff --git a/level03/src/FirstApp/Conf.hs b/level03/src/FirstApp/Conf.hs index 33a847d..2349578 100644 --- a/level03/src/FirstApp/Conf.hs +++ b/level03/src/FirstApp/Conf.hs @@ -39,8 +39,8 @@ newtype HelloMsg = HelloMsg deriving (Eq, Show) -- The ``Conf`` type will need: --- - A customisable port number: `Port` --- - A changeable message for our users: `HelloMsg` +-- - A customisable port number: ``Port`` +-- - A changeable message for our users: ``HelloMsg`` data Conf = Conf -- Similar to when we were considering what might go wrong with the RqType, lets @@ -49,33 +49,34 @@ data ConfigError -- Our application will be able to load configuration from both a file and -- command line input. We want to be able to use the command line to temporarily --- override the configuration from our file. How to would you combine them? This --- question will help us find which abstraction is correct for our needs... +-- override the configuration from our file. How do we combine the different +-- inputs to enable this property? --- We want the CommandLine configuration to take precedence over the File +-- We want the command line configuration to take precedence over the File -- configuration, so if we think about combining each of our ``Conf`` records, -- we want to be able to write something like this: --- defaults <> file <> commandLine +-- ``defaults <> file <> commandLine`` -- We can use the ``Monoid`` typeclass to handle combining the ``Conf`` records --- together, and the Last newtype to wrap up our values to handle the desired --- precedence. The Last newtype is a wrapper for Maybe that when used with its --- ``Monoid`` instance will always preference the last Just value that it has: +-- together, and the ``Last`` type to wrap up our values to handle the desired +-- precedence. The ``Last`` type is a wrapper for Maybe that when used with its +-- ``Monoid`` instance will always preference the last ``Just`` value that it +-- has: -- Last (Just 3) <> Last (Just 1) = Last (Just 1) -- Last Nothing <> Last (Just 1) = Last (Just 1) -- Last (Just 1) <> Last Nothing = Last (Just 1) --- To make this easier, we'll make a new type `PartialConf` that will have our --- Last wrapped values. We can then define a ``Monoid`` instance for it and have --- our ``Conf`` be a known good configuration. +-- To make this easier, we'll make a new type ``PartialConf`` that will have our +-- ``Last`` wrapped values. We can then define a ``Monoid`` instance for it and +-- have our ``Conf`` be a known good configuration. data PartialConf --- We now define our ``Monoid`` instance for PartialConf. Allowing us to define --- our always empty configuration, which would always fail our requirements. --- More interestingly, we define our ``mappend`` function to lean on the --- ``Monoid`` instance for Last to always get the last value. +-- We now define our ``Monoid`` instance for ``PartialConf``. Allowing us to +-- define our always empty configuration, which would always fail our +-- requirements. More interestingly, we define our ``mappend`` function to lean +-- on the ``Monoid`` instance for Last to always get the last value. instance Monoid PartialConf where -- Set some sane defaults that we can always rely on @@ -127,8 +128,8 @@ fromJsonObjWithKey fromJsonObjWithKey = error "fromJsonObjWithKey not implemented" --- Construct the function that will take a FilePath, read it in and attempt to --- decode it as a valid JSON object, using the ``aeson`` package. Then pull +-- Construct the function that will take a ``FilePath``, read it in and attempt +-- to decode it as a valid JSON object, using the ``aeson`` package. Then pull -- specific keys off this object and construct our ``PartialConf``. Using the -- function we wrote above to assist in pulling items off the object. parseJSONConfigFile @@ -161,7 +162,7 @@ commandLineParser = in info (helper <*> partialConfParser) mods --- Combine the smaller parsers into our larger PartialConf type. +-- Combine the smaller parsers into our larger ``PartialConf`` type. partialConfParser :: Parser PartialConf partialConfParser = diff --git a/level03/src/FirstApp/Main.hs b/level03/src/FirstApp/Main.hs index 0fd5fad..f612c21 100644 --- a/level03/src/FirstApp/Main.hs +++ b/level03/src/FirstApp/Main.hs @@ -20,9 +20,11 @@ import FirstApp.Types runApp :: IO () runApp = do - -- Load up the configuration by providing a FilePath for the JSON config file. + -- Load up the configuration by providing a ``FilePath`` for the JSON config + -- file. cfgE <- error "configuration not implemented" - -- Loading the configuration can fail, so we have to take that into account now. + -- Loading the configuration can fail, so we have to take that into account + -- now. case cfgE of Left err -> undefined Right _cfg -> run undefined undefined @@ -72,8 +74,8 @@ app cfg rq cb = handleRErr = either Left ( handleRequest cfg ) --- Now we have some config, we can pull the helloMsg off it and use it in the --- response. +-- Now we have some config, we can pull the ``helloMsg`` off it and use it in +-- the response. handleRequest :: a -> RqType diff --git a/level03/src/FirstApp/Types.hs b/level03/src/FirstApp/Types.hs index cc44ba1..ef45f47 100644 --- a/level03/src/FirstApp/Types.hs +++ b/level03/src/FirstApp/Types.hs @@ -21,8 +21,6 @@ newtype Topic = Topic Text newtype CommentText = CommentText Text deriving Show --- Having specialised constructor functions for the newtypes allows you to set --- restrictions for your newtype. mkTopic :: Text -> Either Error Topic