ocaml любые типы соответствия - PullRequest
3 голосов
/ 05 декабря 2011

Я пытаюсь написать функцию, которая может принимать функцию в качестве аргумента

let xxx ?(extractor = (fun a -> a)) yyy = ...

В результате получается тип:

val xxx: ?extractor:('a -> 'a) -> 'c -> ...

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

Я попытался изменить ее сигнатуру в mli как

val xxx: ?extractor:('a -> 'b) -> 'c -> ...

Но он не компилируется, говоря, что ('a ->' a) несовместимо с (a '->' b).Мне кажется странным, что ('a ->' a) не является подмножеством (a '->' b).Есть ли синтаксис, который я могу вставить в файл mli, чтобы сказать ('a -> *)?

Ответы [ 3 ]

3 голосов
/ 06 декабря 2011

Если сигнатура функции имеет переменные типа, такие как 'a, 'b и т. Д., Это НЕ означает, что функция может помещать туда любой тип, который она хочет при запуске. Напротив, это означает, что кто-то извне может сообщить функции, какой тип имеет каждая из этих переменных типа, и независимо от того, что он говорит, функция все равно сможет работать правильно. (Этот кто-то является средством проверки типов, которое устанавливает типы на основе контекста, в котором используется функция.)

Причина, по которой у вас нет смысла, в том, что, скажем, функция используется в контексте, который требует, чтобы 'a было int и 'b было string. Ваш аргумент по умолчанию fun a -> a не может удовлетворить требуемый тип int -> string. Все, что вы помещаете туда, должно работать для любой комбинации переменных типа, но, как уже упоминали другие, не имеет смысла иметь функцию, которая принимает любой тип и возвращает любой другой требуемый тип.

Я думаю, что вместо этого вам нужны две отдельные функции, одна с дополнительным аргументом, а другая без, при этом последняя вызывает первый, используя fun a -> a в качестве дополнительного аргумента. Обратите внимание, что функция без дополнительного аргумента будет иметь в своем типе на одну переменную меньше типа, поскольку больше не будет 'a и 'b отдельно.

3 голосов
/ 05 декабря 2011

В функциональном языке неопределенный (переменный) тип, который вы получаете от функции, в основном должен появляться где-то в типе, который вы передаете. В противном случае, откуда берется значение?Таким образом, нет (полезных) функций типа 'a -> 'b.

. Это может помочь подумать о типах, которые вы ожидаете передать своей функции, и о том, как функция экстрактора будет относиться к ним.Должно быть возможно иметь довольно прямое отношение.Борьба с системой типов обычно означает, что вы пытаетесь сделать что-то, что может неожиданно потерпеть неудачу, что и пытается предотвратить система типов.

Редактировать:

Возможно, я пытаюсь сказать, что 'a и 'b, типы ввода и вывода вашей функции экстрактора, не будут произвольными типами.Тип ввода 'a, вероятно, как-то связан с типом 'c.Аналогично, тип результата 'b, вероятно, будет связан с типом возврата функции xxx в целом.Без дополнительной информации об этих отношениях трудно вносить предложения.

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

В качестве простого примера, скажем, что 'a и 'c - это один и тот же тип, а 'b - это возвращаемый типфункция.Тогда ваша функция должна выглядеть примерно так:

let xxx ?extractor s =
    match extractor with
    | Some f -> f s
    | None -> s

Это поведение, которое вы хотели: функция извлечения по умолчанию - это функция идентификации.Но это также приводит к тому, что тип возвращаемого значения f совпадает с его типом ввода.Таким образом, тип xxx:

val xxx : ?extractor:('a -> 'a) -> 'a -> 'a

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

Может быть, будет работать две функции.Чтобы продолжить упрощенный пример, они будут выглядеть следующим образом:

# let xxx extractor s = extractor s;;
val xxx : ('a -> 'b) -> 'a -> 'b = <fun>
# let xxx_id s = xxx (fun x -> x) s;;
val xxx_id : 'a -> 'a = <fun>

Я думаю, что у них есть типы, которые вы хотите, и единственное неудобство в том, что у вас есть два разных имени.

2 голосов
/ 05 декабря 2011

Мое намерение состоит в том, чтобы экстрактор был функцией, которая извлекает информация из структуры, поэтому тип возвращаемого значения может быть любым, но Я хочу, чтобы по умолчанию была функция идентичности

O_PONIES? Возвращаемый тип идентифицирующей функции не может быть чем-либо , это точно что-то , переданное ему.

Мне кажется странным, что ('a ->' a) не является подмножеством (a '->' b).

Если вы хотите ограничить тип A типом B, то каждый экземпляр типа B должен быть экземпляром типа A, то есть тип B должен быть подмножеством (другими словами - более узким, чем) типа A (не наоборот). круглый). В вашем случае ('a ->' b) не является подмножеством ('a ->' a). Это как-то связано с LSP, но для этого достаточно интуиции.

...