Here is a test:
open System
open System.Threading
open Newtonsoft.Json
open Suave
open Suave.Logging
open Suave.Operators
open Suave.Filters
open Suave.Writers
let private configuration = {
defaultConfig with
bindings = [ HttpBinding.createSimple HTTP "0.0.0.0" 80 ]
}
let private getServerTime () : WebPart =
DateTime.UtcNow |> JsonConvert.SerializeObject |> Successful.OK >=> setMimeType "application/json"
let private webApplication =
choose
[
GET >=> choose
[
path "/servertime" >=> getServerTime ()
]
]
let start () =
let listening, server = startWebServerAsync configuration webApplication
server |> Async.Start
listening |> Async.RunSynchronously |> ignore
[<EntryPoint>]
let main _ =
start()
Thread.Sleep(Timeout.Infinite)
0
with this example, the getServerTime function is called once and this is it, every subsequent call to the endpoint will return the original result.
I don't understand why? When I use pathScan with parameters, then the function is called each time, as expected, but in this case, with a simple get, only the first call is done while this is defined as a function.
But I don't understand the doc at all either (from the flow of the doc, its contents and the overall document structure...), so the answer is probably simple :)
First of all, I would highly recommend that you study monadic composition. This is a necessary foundation for understanding these things. It will give you an idea of what
>=>
and>>=
are and how to deal with them.As for the problem at hand: yes, you defined
getServerTime
as a function, but that kind of doesn't matter, because that function is only called once, during construction of thewebApplication
value.The structure of the server is such that it's literally a function
HttpContext -> Async<HttpContext option>
. It gets a request context and returns a modified version of it. And all of those combinators -choose
and>=>
and so on - they work with such functions.The expression
path "/servertime"
is also such function. Literally. You can call it like this:Moreover, the expression
getServerTime()
is ALSO such function. So you can do:That's what the
WebPart
type is. It's an async function from context to new context.Now, what the
>=>
operator does is combine these functions. Makes them pipe the context from one webpart to the next. That's all.When you wrote your
getServerTime
function, you created aWebPart
that always returns the same thing. It's kind of like this:Here,
g
is a function (just like aWebPart
is a function), but whenever it's called, it will always return"x = 42"
. Why? Because I partially applied that parameter, it's sort of "baked in" in the definition ofg
now. The same way the current time is "baked in" in theWebPart
that you have created insidegetServerTime
.If you want a different time to be returned every time, you need to recreate the
WebPart
every time. Construct a newWebPart
on every call, one with that call's time baked in. The smallest change to do that is probably this:On the surface it may look like the definition of
time
is silly: after all,let f x = g x
can always be replaced bylet f = g
, right? Well, not always. Only as long asg
is pure. But your webpart here is not: it depends on the current time.This way, every time the
time
webpart is "run" (which means it gets a context as a parameter), it will runDateTime.UtcNow
, then pass it toSuccessful.OK
, and then pass the context to the resulting function.