You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
For some time I've been wanting to implement simple functions which take in values and return an HttpHandler, typically by making use of the built in functions that generate an HttpHandler, where the values passed in to the function would ultimately come from HttpContext. This ultimately requires implementing a new HttpHandler in order to get the HttpContext value to pass in to the function, then calling the resulting HttpHandler with the passed in HttpFunc and HttpContext values.
Some time ago I played around with the idea of reimplementing HttpHandler as a proper monad, but this made certain optimizations of the compose function become difficult or impossible to use properly. Recently, an attempt to use the razorHtmlView with a function designed to build a view model to pass in, I realized this could be satisfied by making use of an applicative making use of apply, and after some thought realized this resembles a specialization of the Reader applicative.
Based on this, I was able to come up with two versions of the HttpReader applicative, one which obtains simple values from HttpContext and passes them into the function directly, and another one which is aware of Task and Result<_,HttpHandler>.
The first one is very simple and I implemented it in 9 lines of code.
This allows an HttpHandler to be composed from HttpContext provided values fairly simply.
// Assuming:// val getService: HttpContext -> 'T// val getCookie: string -> HttpContext -> string// val getDatabaseRecord: DataContext -> DataRecord// val modelBuilder: DataRecord -> string -> ViewModel// val renderViewFromModel: string -> ViewModel -> HttpHandler// val renderViewFromArgs: string -> DataRecord -> string -> HttpHandler// using the builder patternletmyHandler= modelBuilder <!>(getService >> getDatabaseRecord)<*> getCookie "myCookie"=>>= renderViewFromModel "myView"// using the function patternletmyHandler= renderViewFromArgs "myView"<!>(getService >> getDatabaseRecord)<*> getCookie "myCookie"|> HttpReader.run
The second one allows the reader functions to return a task, which will be awaited in the final HttpHandler which will be built, as well as allowing the reader function to return a Result which can have an alternate HttpHandler be used in case of an error, short circuiting the rest. This is a bit more complicated, as it requires a DU in order to know which state the function building is in (in order to avoid allocating Task and Result objects if not necessary), as well as making member operators rather than function operators so that overloading can allow the different reader functions without having to create many different operators for essentially the same operation. Due to not being able to create a member operator which uses two functions as its operands, I have instead created a return prefix operator to convert the initial function into an HttpReader2 rather than using the map operator used by HttpReader. The use is mostly the same, except that the <!> is replaced with <*>, and a !> is added at the beginning. Also, the getDatabaseRecord and getCookie can return either a Result<'T,HttpHandler>, a Task<'T>, or a Task<Result<'T,HttpHandler>> and will still work as expected.
The code could be optimized a bit better, specifically when the function is a result type and the next reader is an async, by not awaiting the next value if the current result is an Error. Also, it could be written a bit more clear, as this prototype was mostly written just to make the types match properly and have proper execution. I'd like to request this to be added to the library if such functionality would be considered desirable. If necessary, I can also alter the code as necessary to be more consistent with the rest of the code base, as well as easier to read and understand.
The text was updated successfully, but these errors were encountered:
For some time I've been wanting to implement simple functions which take in values and return an
HttpHandler
, typically by making use of the built in functions that generate anHttpHandler
, where the values passed in to the function would ultimately come fromHttpContext
. This ultimately requires implementing a newHttpHandler
in order to get theHttpContext
value to pass in to the function, then calling the resultingHttpHandler
with the passed inHttpFunc
andHttpContext
values.Some time ago I played around with the idea of reimplementing
HttpHandler
as a proper monad, but this made certain optimizations of thecompose
function become difficult or impossible to use properly. Recently, an attempt to use therazorHtmlView
with a function designed to build a view model to pass in, I realized this could be satisfied by making use of an applicative making use ofapply
, and after some thought realized this resembles a specialization of theReader
applicative.Based on this, I was able to come up with two versions of the
HttpReader
applicative, one which obtains simple values fromHttpContext
and passes them into the function directly, and another one which is aware ofTask
andResult<_,HttpHandler>
.The first one is very simple and I implemented it in 9 lines of code.
This allows an
HttpHandler
to be composed fromHttpContext
provided values fairly simply.The second one allows the reader functions to return a task, which will be awaited in the final
HttpHandler
which will be built, as well as allowing the reader function to return aResult
which can have an alternateHttpHandler
be used in case of an error, short circuiting the rest. This is a bit more complicated, as it requires a DU in order to know which state the function building is in (in order to avoid allocatingTask
andResult
objects if not necessary), as well as making member operators rather than function operators so that overloading can allow the different reader functions without having to create many different operators for essentially the same operation. Due to not being able to create a member operator which uses two functions as its operands, I have instead created areturn
prefix operator to convert the initial function into anHttpReader2
rather than using themap
operator used byHttpReader
. The use is mostly the same, except that the<!>
is replaced with<*>
, and a!>
is added at the beginning. Also, thegetDatabaseRecord
andgetCookie
can return either aResult<'T,HttpHandler>
, aTask<'T>
, or aTask<Result<'T,HttpHandler>>
and will still work as expected.The code could be optimized a bit better, specifically when the function is a result type and the next reader is an async, by not awaiting the next value if the current result is an Error. Also, it could be written a bit more clear, as this prototype was mostly written just to make the types match properly and have proper execution. I'd like to request this to be added to the library if such functionality would be considered desirable. If necessary, I can also alter the code as necessary to be more consistent with the rest of the code base, as well as easier to read and understand.
The text was updated successfully, but these errors were encountered: