Почему выбор типа F # настолько изменчив?

Компилятор F #, по-видимому, выполняет вывод типа в (справедливо) строгом порядке сверху вниз, слева направо. Это означает, что вы должны делать такие вещи, как поместить все определения перед их использованием, порядок компиляции файлов является значительным, и вам, как правило, требуется переупорядочить материал (через |> или что у вас есть), чтобы избежать явного аннотации типов.

Насколько сложно сделать это более гибким и запланировано для будущей версии F #? Очевидно, это можно сделать, поскольку у Haskell (например) нет таких ограничений с одинаково мощным умозаключением. Есть ли что-то по-разному в отношении дизайна или идеологии F #, вызывающих это?

Ответ 1

Что касается "Haskell столь же мощного вывода", я не думаю, что Haskell должен иметь дело с

  • Динамический подтипирование в стиле OO (классы типов могут выполнять некоторые подобные вещи, но классы типа легче набирать/выводить)
  • перегрузка метода (классы типов могут делать некоторые подобные вещи, но классы типа легче печатать/выводить)

То есть, я думаю, что F # должен иметь дело с некоторыми трудностями, которые Haskell этого не делает. (Почти наверняка, Haskell приходится иметь дело с некоторыми трудными вещами, которые F # не делает.)

Как уже упоминалось в других ответах, большинство основных языков .NET имеют инструментальную среду Visual Studio в качестве основного влияния дизайна языка (см., например, как LINQ имеет "из... select", а не SQL-y "... from", мотивированный получением intellisense из префикса программы). Intellisense, ошибки при чтении ошибок и понятность сообщений об ошибках - все это факторы инструментария, которые информируют о конструкции F #.

Возможно, удастся сделать лучше и сделать больше (не жертвуя другими событиями), но я не считаю это одним из наших высоких приоритетов для будущих версий языка. (Haskellers могут видеть, что вывод типа F # является несколько слабым, но они, вероятно, превосходят численностью С#, которые видят, что вывод типа F # очень силен.:))

Также может быть трудно расширить вывод типа в неразрывном режиме; вполне нормально менять незаконные программы на законные в будущей версии, но вы должны быть очень осторожны, чтобы гарантировать, что ранее законные программы не изменяют семантику в соответствии с новыми правилами вывода, а разрешение имен (ужасный кошмар на каждом языке), скорее всего, для взаимодействия с изменениями типа-типа - неожиданными способами.

Ответ 2

Есть ли что-то по-разному в дизайне или идеологии F #, вызывающих это?

Да. F # использует номинальную, а не структурную типизацию, потому что она проще и, следовательно, проще для простых смертных.

Рассмотрим этот пример F #:

let lengths (xss: _ [] []) = Array.map (fun xs -> xs.Length) xss

let lengths (xss: _ [] []) = xss |> Array.map (fun xs -> xs.Length)

Первый не компилируется, потому что тип xs внутри анонимной функции не может быть выведен, потому что F # не может выразить тип "некоторый класс с членом Length".

Напротив, OCaml может выразить прямой эквивалент:

let lengths xss = Array.map (fun xs -> xs#length) xss

потому что OCaml может выразить этот тип (он написан <length: 'a ..>). Обратите внимание, что для этого требуется более мощный вывод типа, чем у F # или Haskell в настоящее время, например. OCaml может вызывать типы сумм.

Однако эта функция, как известно, является проблемой удобства использования. Например, если вы испортили в другом месте кода, тогда компилятор еще не сделал вывод о том, что тип xs должен был быть массивом, поэтому любое сообщение об ошибке, которое оно может дать, может предоставлять только такую ​​информацию, как "некоторый тип с элементом длины" а не "массив". С немного более сложным кодом это быстро выходит из-под контроля, поскольку у вас есть массивные типы со многими структурно выведенными членами, которые не совсем унифицированы, что приводит к непонятным сообщениям (С++/STL-like).

Ответ 3

Я думаю, что алгоритм, используемый F #, имеет то преимущество, что легко (по крайней мере, грубо) объяснить, как он работает, поэтому, как только вы это поймете, у вас могут быть некоторые ожидания относительно результата.

Алгоритм всегда будет иметь некоторые ограничения. В настоящее время их легко понять. Для более сложных алгоритмов это может быть сложно. Например, я думаю, что вы можете столкнуться с ситуациями, когда вы думаете, что алгоритм должен что-то вывести, но если бы он был достаточно общим для покрытия дела, он был бы недискриминационным (например, можно было бы продолжать цикл навсегда).

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

Ответ 4

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

Недавно я попросил Дона Сима сделать несколько исходных проходов для улучшения типа. Его ответ был "Да, его можно сделать многопроходным тип вывода. Это также однопроходные вариации, которые генерируют конечный набор ограничений.

Однако эти подходы, как правило, дают плохие сообщения об ошибках и intellisense приводит к визуальному редактор".

http://www.markhneedham.com/blog/2009/05/02/f-stuff-i-get-confused-about/#comment-16153

Ответ 5

Короткий ответ заключается в том, что F # основан на традициях SML и OCaml, тогда как Haskell происходит из немного другого мира Миранды, Гофера и т.п. Различия в исторической традиции тонкие, но всепроникающие. Это различие параллельно с другими современными языками, такими как ML-подобный Coq, который имеет те же ограничения порядка по сравнению с Haskell-подобным Agda, который этого не делает.

Это различие связано с ленивой и строгой оценкой. Сторона Хаскелла вселенной верит в лень, и как только вы уже верите в лень, идея добавления лени к вещам вроде умозаключений типа - это не проблема. В то время как в ML-стороне Вселенной всякий раз, когда необходима лени или взаимная рекурсия, она должна быть явно отмечена использованием таких ключевых слов, как with, и, rec и т.д. Я предпочитаю подход Haskell, потому что это приводит к меньшему шаблону кода, но есть много людей, которые думают, что лучше сделать эти вещи явными.