I found the prior explanation confusing (to me). This is my attempt at clarity...
warbler
resolves a problem with optimized impure eager evaluated functional languages in which partially applied arguments are evaluated early and cached. This caching presents a problem when those applied arguments are dependent on side effects and it becomes desirable to have fresh values with each invocation. For example, the following query for the string
representation of the current system's time will occur and be cached at the definition of g: string -> string
. As such, it will return the same value for each subsequent call to g
:
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"
However, the warbler
concept is unnecessary to resolve this reevaluation issue. It is enough to simply wrap the subject function inside an anonymous function that then fully applies the subject function each time, as follows:
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"
What warbler
is doing instead is using the above anonymous function as a function factory that produces the subject function when invoked. Then invoking that subject function with its second argument. It is incidental that warbler
uses its second argument to invoke the factory function but it does present a point of misdirection. Conceivably, passing the argument to the factory can allow the factory to configure the subject function function or select alternative type compatible functions to return to the warbler
. Still, that is not what the warbler
is intended for.
let warbler f x = (f x) x
It should be noted that for reevaluation to work, f
, must be an anonymous function at the point of call. Consequently, there seems to be no longer any utility for the warbler
concept and the cool name should probably be deprecated and allowed to resurface for some other useful concept.
Incidentally, my encounter with warbler
is with Giraffe
.