1
1
mirror of https://github.com/qfpl/applied-fp-course.git synced 2024-11-23 11:54:36 +03:00

Restructured the Types.hs a bit to help with flow

This commit is contained in:
Sean Chalmers 2017-08-24 11:53:52 +10:00
parent c5264cef02
commit e491acb26e
2 changed files with 44 additions and 42 deletions

View File

@ -17,9 +17,9 @@ import Data.Text.Encoding (decodeUtf8)
import FirstApp.Types import FirstApp.Types
-- |------------------------------------------------------| -- ---------------------------------------------|
-- | Don't start here, go straight to FirstApp.Types! :) | -- Don't start here, go to FirstApp.Types! :) |
-- |------------------------------------------------------| -- ---------------------------------------------|
runApp :: IO () runApp :: IO ()
runApp = run 3000 app runApp = run 3000 app
@ -65,6 +65,7 @@ mkRequest rq =
-- These helpers will take the raw request information and turn it into -- 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 -- 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. -- world must end, and where the well-typed world of our application begins.
mkAddRequest mkAddRequest
:: Text :: Text
-> LBS.ByteString -> LBS.ByteString
@ -75,25 +76,27 @@ mkAddRequest ti c =
-- This has other benefits, we're able isolate our validation requirements into the -- 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 -- 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. -- that validation is not spread across the application. It is kept at the borders.
mkViewRequest mkViewRequest
:: Text :: Text
-> Either Error RqType -> Either Error RqType
mkViewRequest = mkViewRequest =
error "mkViewRequest not implemented" error "mkViewRequest not implemented"
-- Even thought it may seem trivial or even pointless to write functions such as these -- Even though it may seem too trivial or even pointless to write functions such
-- it allows for much greater consistency across the application. -- as these it allows for much greater consistency across the application.
--
-- These are straight forward data constructors, but by doing it this way we don't -- Some of these are straight forward data constructors, but by doing it this
-- have any snowflakes littered about the code. It also enhances our ability to -- way we don't have any snowflakes littered about the code. It also enhances
-- spot larger patterns in our application, which are opportunities for abstraction. -- our ability to spot larger patterns in our application, which are
-- opportunities for abstraction.
mkListRequest mkListRequest
:: Either Error RqType :: Either Error RqType
mkListRequest = mkListRequest =
error "mkListRequest not implemented" error "mkListRequest not implemented"
{-| {-|
HALP HALP - wording - is it useful?
Alternative type sig: Alternative type sig:
Either Error a Either Error a
@ -112,20 +115,17 @@ mkErrorResponse
mkErrorResponse _ = mkErrorResponse _ =
error "mkErrorResponse not implemented" error "mkErrorResponse not implemented"
{-| -- Notice how we're only accepting our predefined request types that have the
We'll stub these for now as the general structure and the process of reaching -- required information already validated and prepared for use in the handling
this stage is the more important lesson here. -- of the request.
--
Notice how we're only accepting our predefined request types that have the required -- If we find that we need more information to handle a request, or we have a
information already validated and prepared for use in the handling of the request. -- new type of request that we'd like to handle then we simply update the RqType
-- structure and the compiler will let us know the affected portions of our
If we find that we need more information to handle a request, or we have a new -- application.
type of request that we'd like to handle then we simply update the RqType structure --
and the compiler will let us know the affected portions of our application. -- 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.
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.
-}
handleRequest handleRequest
:: RqType :: RqType
-> Either Error Response -> Either Error Response

View File

@ -23,23 +23,33 @@ data RqType
-- types reflect when errors can be introduced into our program. Additionally -- types reflect when errors can be introduced into our program. Additionally
-- it's useful to be able to be descriptive about what went wrong. -- it's useful to be able to be descriptive about what went wrong.
-- So lets think about some of the basic things that can wrong with our -- Think about some of the basic things that can wrong with our Requests and
-- program and create some values to represent that. -- building the RqTypes, and create some values to represent that.
data Error data Error
-- For now we don't need to worry about things like malformed requests or
-- invalid headers etc.
-- Provide a type to list our response content types so we don't try to -- 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/JSON etc. -- do the wrong thing with what we meant to be used as text/JSON etc.
data ContentType data 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 -- In Haskell the `newtype` comes with zero runtime cost. It is purely used for
-- typechecking. So when you have a bare 'primitive' value, like an Int, String, or -- typechecking. 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. -- even [a], you can wrap it up in a `newtype` for clarity.
-- 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.
-- once it has passed.
-- Having specialised constructor functions for the newtypes allows you to set -- Having specialised constructor functions for the newtypes allows you to set
-- restrictions for your newtype. -- extra restrictions for your newtype.
-- Write two `newtype` definitions for `Topic` and `CommentText` that wrap a -- Write two `newtype` definitions for `Topic` and `CommentText` that wrap a
-- `Text` value -- `Text` value
@ -52,10 +62,9 @@ data ContentType
-- | -- |
-- An additional benefit of `newtype` is that we can choose to not export the -- A benefit of `newtype` is that we can choose to *not* export the constructor
-- constructor and provide a function of our own. In our case, we're not -- and provide a function of our own. In our case, we're not interested in empty
-- interested in empty `Text` values so we can eliminate them and immediately -- `Text` values so we can eliminate them and immediately report an error.
-- report an error.
mkTopic mkTopic
:: Text :: Text
-> Either Error Topic -> Either Error Topic
@ -69,13 +78,6 @@ mkCommentText =
error "mkCommentText not implemented" error "mkCommentText not implemented"
-- After you've implemented these functions, adjust the export list in the -- After you've implemented these functions, adjust the export list in the
-- module declaration so your constructor functions cannot be bypassed. -- module declaration so your constructor functions cannot be bypassed. Also
-- export the functions that let you access the inner value of the newtype.
-- 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"