Почему я должен сообщать F#, использую ли я int или float в этой строке?
let total counts = Array.sum counts
к let total (counts:int[]) = Array.sum counts
?
Исходя из sml, я нахожу вывод типа F# слишком ограничительным.
P.S. Я мало что знаю о ландшафте функциональных языков, но было бы интересно, если бы кто-нибудь просветил меня, какие F-языки используются в дикой природе.
Согласно сообщению об ошибке, я понимаю, что проблема в том, что вы должны ограничить его типом, поддерживающим оператор «+».
Я предполагаю, что это делают и float, и int, но не тип по умолчанию «obj», который вы получаете, если не указываете тип массива.
В F# компиляция является однопроходной и (в основном) строго прямой. Я считаю, что это сделано для улучшения времени компиляции, но я не уверен. В результате в выражении Array.sum counts
функция sum
не знает, какой тип counts
, если вы не добавите аннотацию типа.
Вы можете написать вычислительно эквивалентное выражение counts |> Array.sum
и, поскольку counts
появляется раньше в файле, sum
может правильно разрешить тип. Это одна из причин, по которой оператор трубопровода|>
так часто встречается в коде F#.
(Ключевые слова rec
и and
позволяют компилятору ссылаться на вещи вперед в исходном файле, чтобы разрешить взаимно рекурсивные функции и типы, а member
в определениях классов взаимно рекурсивны по умолчанию, но это исключение.)
Не прямой ответ, но у вас есть возможность создать встроенную функцию, которая будет более общей:
let inline total counts = Array.sum counts
Тип this автоматически выводится с необходимыми статически разрешенными параметрами типа:
> val inline total:
counts: ^a[] -> ^a
when ^a: (static member (+) : ^a * ^a -> ^a) and
^a: (static member get_Zero: -> ^a)
Использование:
total [|1; 2; 3|] // 6
total [|1.; 2.; 3.|] // 6.0
Привет, спасибо, это гораздо менее ограничительно. После просмотра документов кажется, что встроенные файлы следует использовать с осторожностью. «Встроенные функции — это функции, интегрированные непосредственно в вызывающий код». Это может привести к «утечке» кода между модулями без повторного использования скомпилированного кода библиотеки. Является ли хорошей практикой использование встроенных функций только внутри?
@Alex_ inline
в F# означает не то, что означает в большинстве других контекстов. Это неправильное название, правда. Каков источник вашей цитаты?
@Alex_ Определенно этим можно злоупотреблять и сделать код чрезмерно сложным, а компиляцию очень медленной. Но для некоторых очень общих библиотечных функций это может быть полезно. По этой причине несколько функций основной библиотеки F# помечены как встроенные.
Документы @phoog MSDN. docs.microsoft.com/en-us/dotnet/fsharp/language-reference/…
Также см. мой дополнительный вопрос. stackoverflow.com/questions/71642273/…
@Alex_ спасибо. Я подозреваю, что это определение немного вводит в заблуждение в контексте F#, несмотря на его появление в документации по F#. Встраивание традиционно означает буквально интеграцию кода функции в вызывающую функцию, и я вполне уверен, что компилятор F# этого не делает: в F# функция inline
может быть сколь угодно длинной, а ключевое слово имеет отношение к программисту из-за вывода типа, а не спектакль. Попробуйте декомпилировать программу F#, в которой используются встроенные функции, и вы увидите, что это обычные вызовы IL.
@phoog Я считаю, что ключевое слово inline
действительно может встроить функцию, как вы описываете, но только при определенных условиях (мне неизвестных). Это определенно может повлиять на производительность. Например. прямой оператор |>
— это просто встроенная функция.
@phoog Это можно увидеть здесь. Функция total
не вызывается при компиляции/декомпиляции в C#. Даже Array.sum
не вызывается, потому что он также встроен.
Но вопрос о том, встроена ли функция в традиционном смысле, зависит от JIT-компилятора. Оператор прямого канала, определяемый как inline
, аналогично для вывода типа; в библиотеках F# .NET нет функции переадресации конвейера (так же, как нет функции (+)
— это что-то вроде фикции, поддерживаемой компилятором F#). Компилятор F# мая встраивает функцию, помеченную inline
, как в вашем примере, но в некоторых случаях это делает не можешь (особенно let rec inline ...
). Если вы скопируете реализацию Array.sum
в свой собственный модуль и будете использовать ее, встраивает ли это компилятор?
Кроме того, компилятор F# сильно изменился с тех пор, как я в последний раз подробно исследовал его. Я подозреваю, что тот, на кого я смотрел много лет назад, вероятно, просто позвонил бы Array.sum
, но я не мог поклясться в этом. Конечно, если функция inline
состоит из 100 строк, ее не следует встраивать, особенно если она вызывается более одного раза. Интересно, есть ли способ предотвратить явное встраивание функций inline
; то есть ограничить его действие выводом типа. Это было бы полезно для предотвращения утечки, о которой вы беспокоитесь. (Это исправленная версия предыдущего комментария.)
Одним из преимуществ одностороннего вывода является то, что пользователю становится намного проще понять, почему выводится тип, и исправить возникающие ошибки.