Как я могу вызвать действие после printf OCaml с предоставленной пользователем строкой формата - PullRequest
3 голосов
/ 11 июля 2011

У меня есть простая функция, которая печатает строку и завершает работу:

let fatal s = 
  print_string "Log: ";
  print_endline s;
  exit 1

Я могу использовать printf, чтобы сделать нечто подобное без exit 1:

let log fmt = 
  printf ("Log: " ^^ fmt)

Эта функция журналапринимает строку формата и возвращает функцию, которая принимает параметры, необходимые для этой строки формата, и выводит «Log:» впереди.(Конечно, мой префикс не так прост для моего реального приложения.)

Взять эти два и объединить их нелегко.Первая попытка:

let fatalf fmt =
   Printf.printf ("Log: " ^^ fmt) 
   ???
   exit 1

Проблема в том, что мне нужно вернуть результат моего выражения printf, чтобы к нему можно было применить оставшиеся аргументы.После того, как я вернул это значение, у меня больше нет управления потоком для запуска exit.

Форматер printf %t выглядит полезным, так как он принимает функцию и запускает ее:

printf ("Log: " ^^ fmt ^^ "%!%t") ... (fun _ -> exit 1)

Это, похоже, не работает, так как %t должен быть последним, поэтому он запускается после записи сообщения журнала, но это означает, что функция выхода должна быть после заданных пользователем параметров, и, поскольку нет никакого способачтобы знать, сколько будет промежуточных параметров, нельзя создать замыкание, которое полностью применяет printf при наличии промежуточных аргументов.

Я помню, что есть некоторая поддержка именованных аргументов printf, но этотянуло так как глючило.Есть ли способ подражать этому или добиться желаемого поведения «выход после произвольного printf»?

Ответы [ 2 ]

8 голосов
/ 11 июля 2011

Вы ищете Printf.kprintf:

let fatalf fmt =
  Printf.kprintf (fun str ->
    Printf.eprintf "Fatal error: %s !\n%!" str;
    exit 1) fmt

kprintf принимает продолжение с типом string -> 'a и применяет его к результату sprintf с предоставленным форматом. Результатом продолжения является результат всего вызова, как и ожидалось.

1 голос
/ 11 июля 2011

printf - сложная часть OCaml, потому что она поддерживается магией компилятора. Если источник содержит строку constant и его типовая среда требует формат printf, компилятор волшебным образом преобразует строковую константу в формат. Это довольно гладко, но как только вы выходите за рамки простейшего использования, вам иногда нужно сделать магию самостоятельно. В частности, обратите внимание, что это должна быть строка константа , чтобы компилятор мог применять строгую типизацию.

Ваш второй пример не показывает параметр s, но я полагаю, вы просто его не указали. Если вы просто хотите напечатать один параметр, используя формат, который также передается в качестве параметра, вы можете сделать что-то вроде этого:

let fatalfs f s =
    printf ("Log:" ^^ f) s;
    exit 1

Оператор ^^ объединяет два формата в один формат.

Вот сеанс с этой функцией:

$ rlwrap ocaml312
    Objective Caml version 3.12.0

# let fatalfs f s = Printf.printf ("Log: " ^^ f) s; exit 1;;
val fatalfs :
  ('a -> 'b, out_channel, unit, unit, unit, unit) format6 -> 'a -> 'c = <fun>
# fatalfs "Here is the value: [%s]\n" "value";;
Log: Here is the value: [value]
$ echo $?
1
$

Обратите внимание, что fatalfs на самом деле полиморфен в типе параметра s. Это работает, пока тип строки формата и второй параметр совпадают. Это довольно впечатляюще.

...