Ввод в формате с принтерами с необязательными аргументами

Я столкнулся с интересным поведением типа OCaml. Кажется, что машинка не принимает принтер с необязательными аргументами.

Когда функция имеет необязательные аргументы, ее можно ввести как функцию без необязательных аргументов.

(** Simple example *)
let (f1 : ?arg : int -> unit -> int) =
  fun ?(arg = 3) () : int -> arg + 5

let f2  : ((unit -> int) -> int) =
  fun f -> f ()

let x : int = f2 f1
(* The type of f1 matches the signature of f2 :
   the optional argument is well discarded. *)

Здесь f1 имеет необязательный аргумент, но f2 f1 хорошо типизирован. Это потому, что (или, по крайней мере, я так понял) сигнатура аргумента f2 охватывает тип f1. Необязательный аргумент просто отбрасывается.

Однако такое поведение отвергается в принтерах, как показано в этом примере.

 (* Data structure *)
type 'a elt = {
  data : int;
  annot : 'a
}

(* Type of printer annotations *)
type 'annot printer = Format.formatter -> 'annot -> unit

(* Default printer prints nothing *)
let (default : 'a printer) = fun fmt _ -> Format.fprintf fmt ""

(* Generic printer for elts *)
let elt_printer
    ?(print_annot : 'a printer = default)
    (fmt : Format.formatter)
    (elt : 'a elt) =
  Format.fprintf fmt "%i(%a)"
    elt.data
    print_annot elt.annot

(* I don't care about printing the annotation *)
let f (elt : _ elt) =
  Format.printf
    "%a"
    elt_printer elt

Вот что возвращает компилятор при использовании elt_printer в вызове fprintf̀:

This expression has type
         ?print_annot:'a printer -> Format.formatter -> 'a elt -> unit
       but an expression was expected of type Format.formatter -> 'b -> unit

Я полагаю, что типографу удается вывести это 'b = 'a elt, но ему не удается отбросить необязательный аргумент.

У меня есть два вопроса относительно этого поведения:

  1. Это ожидаемое поведение машинки для второго примера?

  2. Если нет, существует ли стандартный синтаксис, запрещающий использование функций с необязательными аргументами? Например, есть ли способ запретить использование f1̀ в качестве аргумента f2 в первом примере?

Заранее спасибо.

Обновлено: Можно принудительно ввести elt_printer без необязательных аргументов, указав его тип.

let f (elt : _ elt) =
  Format.printf
    "%a"
    (elt_printer : _ printer) elt

Обновлено еще раз: Для помеченных аргументов средство ввода строго блокирует преобразования ввода, так как аргументы должны иметь определенное имя. Это не проблема, поднятая в моем вопросе.

Мой вопрос касается разницы между двумя разными поведениями для аналогичного использования функций высокого порядка с необязательными аргументами. Эта тема (хотя и интересная) не дает четкого ответа.

Stevendeo 17.04.2019 15:34
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
2
93
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий
  1. Да, это ожидаемо, функция ?foo -> bar -> baz не может быть преобразована в функцию bar -> baz. Его можно применять только без foo, что сильно отличается от неявного преобразования.

  2. Есть два решения

Во-первых, можно «не применять» метку: f ?print_annot:None сообщит типировщику, что параметр print_annot из f следует считать отсутствующим.

Во-вторых, и это метод, который я использовал для tyxml (посмотреть здесь), вы можете добавить единичный аргумент:

val pp : 
  ?encode:(string -> string) ->
  ?indent:bool ->
  ?advert:string ->
  unit -> 
  Format.formatter -> doc -> unit

Тогда у пользователей будет код, который выглядит так:

let s = Format.asprintf "%a" (Tyxml.Html.pp ()) my_html

Спасибо за ответ, но я не уверен, что понял вашу мысль. В моем первом примере есть неявное преобразование. Кроме того, я думаю, что вы отвечаете на неправильный вопрос во второй части. Мне не интересно, как определить принтер, чтобы он соответствовал правильной подписи. Мне интересно, как применить ограничение ввода, используемое в аргументах формата, в более простом примере (см. Первый).

Stevendeo 17.04.2019 15:30

Другие вопросы по теме