F# 风流莺函数

F# Suave warbler function

我已经开始学习 F# 和 Suave,并且正在阅读 F# Applied 一书。

我正在努力解决的一件事是 warbler 函数。我知道它与延迟执行有关,但我真的不明白为什么以及何时需要它。

显然我们也可以使用 request 函数作为 warbler 的替代方法。

任何人都可以提供更多关于使用这些功能的原因和时间的详细信息。

这三个函数是相关的,因为 requestcontextwarbler 的特殊版本。他们都做同样的事情 - 他们检查他们的论点(的某些方面)并返回一个函数以应用于该论点。

请记住,Suave WebPart 的基本“构建块”是一个函数 HttpContext -> Async<HttpContext option> 而不是某个具体对象。这实际上意味着这三个函数允许你检查这个 HttpContext 并基于它组成一个 WebPart 来使用。

warbler 的核心作用非常简单:

let warbler f a = f a a  
// ('t -> 't -> 'u) -> 't -> 'u

你给它一个函数 f 和参数 a。函数 f 查看 a 并返回一个新函数 't -> 'u 然后应用于 a.

关于 warbler 的事情是它是完全通用的 - 只要类型对齐,您可以在任何使用 contextrequest 的地方使用它,但它不我对 Suave 感兴趣的域一无所知。

这就是为什么它有“说领域语言”的专门版本的原因:

let request apply (a : HttpContext) = apply a.request a
// (HttpRequest -> HttpContext -> 'a) -> HttpContext -> 'a 
let context apply (a : HttpContext) = apply a a
// (HttpContext -> HttpContext -> 'a) -> HttpContext -> 'a 

请注意,它们与 warbler 具有相同的“形状”- 唯一的区别是 HttpContext 类型是“硬编码”- 使用起来更方便。

另一个答案已经解释了 warbler 函数及其与 contextrequest 函数的关系。我想告诉你什么时候要用这些。

启动 Suave 服务器时,需要为其提供 WebParts 的请求处理管道 - 路由、HTTP 方法和响应生成函数。这意味着当您启动 Web 服务器时,提供给部分应用的 WebPart 函数的所有参数都已经被评估。

想象一个打印当前服务器时间的简约网络应用程序:

let app = GET >=> path "/" >=> OK (string DateTime.Now)

如果您使用此 app 管道启动网络服务器,您将始终看到创建 app 值时生成的相同时间戳,无论您何时发出网络请求检索它.

warbler 函数及其专用版本 contextrequest 不仅可以延迟执行,还可以让 Web 服务器在每次需要结果时调用提供的函数.

在示例场景中,此 app 将提供预期结果:

let app = GET >=> path "/" >=> warbler (fun ctx -> OK (string DateTime.Now))

@adzdavies 的评论显示了一种您不一定需要 warbler 的替代方法。在示例中,如果您使用匿名函数语法而不是部分应用 OK.

,您还可以推迟参数评估
let app = GET >=> path "/" >=> (fun ctx -> OK (string DateTime.Now) ctx)

我发现之前的解释让我感到困惑。这是我试图澄清的...

warbler 解决了优化的不纯急切求值函数式语言的问题,在这种语言中,部分应用的参数会提前求值并缓存。当这些应用的参数依赖于副作用并且希望每次调用都有新值时,这种缓存会出现问题。例如,以下对当前系统时间的 string 表示的查询将发生并缓存在 g: string -> string 的定义中。因此,对于 g:

的每个后续调用,它将 return 相同的值
let g = sprintf "%s %s" (string DateTime.Now)

g "a"  //"12/09/2020 18:33:32 a"
g "b"  //"12/09/2020 18:33:32 b"

但是,warbler 概念对于解决此重新评估问题是不必要的。只需将主题函数简单地包装在一个匿名函数中,然后每次都完全应用主题函数就足够了,如下所示:

let f = (fun x -> sprintf "%s %s" (string DateTime.Now) x)

f "c"    //"12/09/2020 18:53:32 c"
f "d"    //"12/09/2020 18:53:34 d"

warbler 正在做的是将上述匿名函数用作函数工厂,在调用时生成主题函数。然后用它的第二个参数调用该主题函数。 warbler 使用它的第二个参数来调用工厂函数是偶然的,但它确实存在误导点。可以想象,将参数传递给工厂可以让工厂配置主题函数函数或select替代类型兼容函数到return到warbler。尽管如此,这并不是 warbler 的目的。

let warbler f x = (f x) x

需要注意的是,要使重新计算生效,f 在调用点必须是匿名函数。因此,warbler 概念似乎不再有任何实用性,这个很酷的名称可能应该被弃用,并允许重新出现一些其他有用的概念。

顺便说一下,我遇到 warbler 是遇到 Giraffe