hyper icon indicating copy to clipboard operation
hyper copied to clipboard

Define kind `ResponseState` and annoate `res` with kind `ResponseState -> Type`

Open JordanMartinez opened this issue 6 years ago • 0 comments

The res type in Conn is implicitly Type -> Type despite its declaration not showing that.

module Hyper.Conn where

-- | A `Conn` models the entirety of an HTTP connection, containing the fields
-- | `request`, `response`, and the extensibility point `components`.
type Conn req res components =
  { request :: req
  , response :: res -- (res :: Type -> Type)
  , components :: components
  }

This isn't immediately clear to the end-reader (unless one reads through the source code or potentially the docs). Moreover, the lack of using a kind here also allows one to use other non-ResponseState types here (e.g. res Int rather than res StatusLineOpen).

I propose we instead define a kind and more clearly indicate that. When I worked on implementing this, I found that the code is easier if the Conn type also has a type parameter for the responseState that it applies to res in its definition. In other words...

module Hyper.Conn where

-- | Defines the states of an HTTP request stream. It tracks whether or not
-- | some content has already been written to an HTTP request stream.
-- |
-- | Proper order of computations. Items marked with an asterisk indicate that
-- | transitioning back to the same state is valid:
-- | StatusLineOpen -> HeadersOpen* -> BodyOpen* -> ResponseEnded
foreign import kind ResponseState

-- | Type indicating that the status-line is ready to be
-- | sent.
foreign import data StatusLineOpen :: ResponseState

-- | Type indicating that headers are ready to be
-- | sent, i.e. the body streaming has not been started.
foreign import data HeadersOpen :: ResponseState

-- | Type indicating that headers have already been
-- | sent, and that the body is currently streaming.
foreign import data BodyOpen :: ResponseState

-- | Type indicating that headers have already been
-- | sent, and that the body stream, and thus the response,
-- | is finished.
foreign import data ResponseEnded :: ResponseState

-- | A `Conn` models the entirety of an HTTP connection, containing the fields
-- | `request`, `response`, and the extensibility point `components`.
type Conn request response components (responseState :: ResponseState) =
  { request :: request
  , response :: response responseState
  , components :: components
  }

-- now the `ResponseTransition` type alias is defined like so:
-- | A middleware transitioning from one `Response` state to another.
type ResponseStateTransition m (res :: ResponseState -> Type) (from :: ResponseState) (to :: ResponseState) =
  forall req comp.
  Middleware
  m
  (Conn req res comp from)
  (Conn req res comp to)
  Unit

JordanMartinez avatar Aug 06 '19 02:08 JordanMartinez