F#: Может ли кто-нибудь объяснить мою ошибку компилятора?

Кто-нибудь знает, в чем проблема с этим кодом?

let rec Foo(a,b) =
    match a () with
    | None -> Some(b)
    | Some(c) -> Some(Foo(c,b))

Вот ошибка компилятора:

«Несоответствие типов. Ожидается «а», но задан вариант «а». Результирующий тип будет бесконечным при объединении «а» и «опция а»


person rysama    schedule 29.12.2009    source источник
comment
Просто для забавы, что точно вы пытаетесь сделать? Может быть, кто-то может предложить лучший способ написать вашу функцию.   -  person Juliet    schedule 29.12.2009
comment
@ Джульетта - мне было неудобно искать лучший способ написать функцию, не зная, но, пока вы не упомянули об этом, я не думал о том факте, что остановился из-за непонимания.   -  person James Black    schedule 29.12.2009


Ответы (3)


Попробуем воспроизвести здесь, как компилятор пытается вывести типы.

let rec Foo(a,b) =
    match a () with
    | None -> Some(b)
    | Some(c) -> Some(Foo(c,b))

«Хорошо, я вижу, что a (). a должна быть функцией от unit до какого-то типа, я пока не знаю, какого именно. Я назову ее 'a».

a : unit -> 'a

«Результат a () сопоставляется с шаблонами None/Some. Таким образом, 'a должен быть 'b option, а c имеет тип 'b». (Опять же, 'b обозначает пока неизвестный тип).

a : unit -> 'b option
с : 'b

«Для b не вызываются никакие функции или методы (кроме Some, который не сужает тип, и Foo, тип которого нам пока неизвестен). Я буду обозначать его тип 'c».

a : unit -> 'b option
b : 'c
c : 'b

«Foo возвращает Some(b) в одной из ветвей, поэтому тип возвращаемого значения должен быть 'c option».

Foo : (unit -> 'b option) * 'c -> 'c option

«Я уже закончил? Нет, мне нужно проверить, что все типы в выражении имеют смысл. Посмотрим, в случае Some(c) возвращается Some(Foo(c,b)). Итак, Foo(c,b) : 'c. Поскольку Foo возвращает option, я знаю, что 'c должно быть 'd option для некоторых 'd и b : 'd. Подождите, у меня уже есть b : 'c, то есть b : 'd option. 'd и 'd option должны быть одного типа, но это невозможно! В определении должна быть ошибка. Мне нужно сообщить об этом." Так оно и есть.

person Alexey Romanov    schedule 29.12.2009
comment
Очень красивая разбивка. Другими словами, компилятор разрывается между 'a и 'a option option option option... option option etc. - person YotaXP; 30.12.2009

Это всегда помогает разбивать вещи шаг за шагом. Как написано, Foo имеет тип:

val Foo : (unit -> 'a option) * 'b -> 'b option

Каждое выражение в активном шаблоне должно иметь один и тот же тип. Первое совпадение с образцом в вашем выражении имеет тип:

'b option

Следовательно, другой шаблон также должен оцениваться как 'b option или 'a option. То, что у вас есть здесь, возвращает 'a option option.

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

let Foo2(a,b) =
    match a () with
    | None -> Some(b)
    | c    -> c

ХТН.

person Ray Vernagus    schedule 29.12.2009
comment
Как написано, Foo не имеет этого типа и фактически не имеет никакого другого типа; в этом суть ошибки! - person Alexey Romanov; 29.12.2009
comment
Я понимаю вашу точку зрения, но я скопировал эту подпись прямо из интерпретатора F# в Visual Studio. Да, код не скомпилируется и не запустится, как показано, но F# приложил немало усилий для этого. знак равно - person Ray Vernagus; 29.12.2009

Вы используете a() как параметр, который является первым параметром в Foo, но в последней строке c является типом, и тем не менее вы передаете его в рекурсивный вызов.

Именно это и приводит к ошибке.

Вы захотите, чтобы c был типом опции.

person James Black    schedule 29.12.2009