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:
parent
c5264cef02
commit
e491acb26e
@ -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
|
||||||
|
@ -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"
|
|
||||||
|
Loading…
Reference in New Issue
Block a user