Проблема здесь в том, что ваша функция log
возвращает функцию печати, заключенную в Simulation
, так что вам нужно использовать дополнительный let!
, чтобы развернуть его, прежде чем вы сможете его использовать.
Но ничто не мешает вам обернуть результат функции в Simulation
, а не саму функцию:
let log = Printf.ksprintf (fun str -> Simulation (fun simulation -> simulation.logger str))
Это позволит вашему коду скомпилировать:
do! Simulation.log "Value: %d" x
Потому что теперь выражение Simulation.log "Value: %d"
возвращает функцию int -> Simulation<unit>
, а не Simulation<int -> unit>
, как это было раньше.
Однако это также мономорфизирует функцию log
: компилятор увидит, что она использовалась с одним параметром int
и исправит его тип на принятие int
s, а затем, если вы попробуете что-то другое:
do! Simulation.log "Value: %s" "foo"
, он не будет компилироваться снова, жалуясь, что ожидал int
, но с учетом string
.
Чтобы решить эту новую проблему, вам придется немного помочь компилятору, предоставив явную аннотацию типа generi c и указав, как именно она должна быть переведено to ksprintf
:
let log (format: Printf.StringFormat<'T, _>) =
Printf.ksprintf (fun str -> Simulation (fun simulation -> simulation.logger str)) format
Здесь 'T
представляет строку параметров, например int ->
или string -> bool ->
или что-то еще, что указывает ваша строка формата.
С этим, любой формат будет компилироваться:
do! Simulation.log "Value: %d" x
do! Simulation.log "Value: %s" "foo"
do! Simulation.log "I have %d apples which are %s" 42 "rotten"