Closed kylegoetz closed 3 years ago
Hey @kylegoetz,
It's certainly possible to build these kind of things in Kotlin. I've build IndexedStateT
a couple years ago, but the Kotlin Compiler could at that time not handle the complexity of the generics 😅 It however still lives in a branch on the repo here.
We're however removing Monad
and the kinded typeclasses from Arrow.
I'm however wondering if you could define those things as follows:
fun StatusOpen.setStatus(statusValue: Number): HeadersOpen
and fun HeadersOpen.setHeader(name: String, value: String): HeadersOpen
and fun HeadersOpen.closeHeaders(): BodyOpen
. Which would reuslt in following DSL.
setStatus(200)
.setHeaders("Accept-Charset", "utf-8")
.setHeaders("Cache-Control", "no-cache")
.closeHeaders()
...
Since you can easily mix in suspend
functionality here, you can automatically mix it with IO
based code.
Wrapping it inside either { }
would also allow you to use !
inside inline fun
lambdas in this DSL.
So I think in this way such libraries can be build without having to rely on Monad
hierarchies.
Lately I've been doing some work in TypeScript, and there is a FP library for writing web servers called
hyper-ts
that uses an indexed monad to ensure a route handler follows a prescribed state progression: write a response status, then write or skip writing headers, then write the body and send.It is a compile-time error to write a body and then write headers, or write headers/body before writing status, or to skip writing status.
Some utility functions (like
json
) will write an 'application/json' header, then mark headers as done being written, then write a stringified response.I added my own indexes to let you define a controller as requiring authentication and authorization and require these steps to be performed before writing status.
Basically an indexed monad is defined as
Monad InitialState NewState value
(with the state being the "index" in "indexed monad").You can define a function (pseudocode) like
setStatus: (statusValue:number) => Monad StatusOpen HeadersOpen void
andsetHeader: (name:string,value:string) => Monad unknown HeadersOpen void => Monad HeadersOpen HeadersOpen void
andcloseHeaders: () => Monad unknown HeadersOpen void => Monad HeadersOpen BodyOpen void
and so forth. You compose these and will not be able to usesetHeader
aftercloseHeaders
because setHeader expects the initial index to beHeadersOpen
but closeHeaders sets the index to BodyOpen.I'm being long-winded. Here is the inspiration for this post. I'm not saying add this Express middleware stuff. Just the actual IMonad type to enable this kind of thing would be cool to see.