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
getServerTimeas a function, but that kind of doesn't matter, because that function is only called once, during construction of thewebApplicationvalue.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 -chooseand>=>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
WebParttype 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
getServerTimefunction, you created aWebPartthat always returns the same thing. It's kind of like this:Here,
gis a function (just like aWebPartis 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 ofgnow. The same way the current time is "baked in" in theWebPartthat you have created insidegetServerTime.If you want a different time to be returned every time, you need to recreate the
WebPartevery time. Construct a newWebParton 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
timeis silly: after all,let f x = g xcan always be replaced bylet f = g, right? Well, not always. Only as long asgis pure. But your webpart here is not: it depends on the current time.This way, every time the
timewebpart 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.