Универсальные функции для универсальных типов в F#

Я изучаю функциональное программирование на F# и хочу написать функцию для возведения числа в степень. Я написал это следующим образом:

let raiseToPower a power = 
    let folding x _ = x * a
    [1..power-1] |> List.fold folding a

let result = raiseToPower 2 5

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

let result = raiseToPower 2.3 5 // OK

// separate program let result = raiseToPower 2 5 // OK

// separate program let result1 = raiseToPower 2.3 5 // OK let result2 = raiseToPower 2 5 // fail

Первый вопрос: Есть ли способ сделать его общим?


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

Рассмотрим следующее:

type ComplexNumber = {re: double; im: double;}

let (@*) cn1 cn2 = 
    {re = cn1.re * cn2.re - cn1.im * cn2.im; im = cn1.re * cn2.im + cn1.im * cn2.re}

У меня есть новый тип ComplexNumber и новый оператор для него, который умножает два комплексных числа. Теперь я могу написать аналогичную функцию для возведения ComplexNumber в степень:

let raiseCnToPower a power = 
    let folding x _ = x @* a
    [1..power-1] |> List.fold folding a

Итак, второй вопрос: Есть ли способ заставить исходную функцию работать с пользовательскими типами и пользовательскими операторами?


Заключительный вопрос: Я хотел бы знать, что вы должны здесь делать с точки зрения функционального программирования. Считается ли это проблемой, или разные функции для разных типов, делающие почти одно и то же, в порядке? Есть ли способ улучшить решение с точки зрения функционального программирования и конкретно F#?


person Dmitry Volkov    schedule 29.01.2019    source источник
comment
Если вы их еще не видели, эти страницы документации по F#, а также ознакомьтесь с исходным кодом FSharpPlus.   -  person Jarak    schedule 30.01.2019


Ответы (1)


Самый простой способ — объявить его inline:

let inline raiseToPower a power = 
    let folding x _ = x * a
    [1..power] |> List.fold folding (a / a) // <- handles power = 0

let result1 = raiseToPower 2 5
let result2 = raiseToPower 2. 5
person AMieres    schedule 29.01.2019
comment
Почему это помогает? - person ChiefTwoPencils; 29.01.2019
comment
let inline raiseToPower a power = List.replicate power a |> List.fold ( *) LanguagePrimitives.GenericOne обрабатывает также power = 0, хотя и не ‹ 0. Сравните с pown. - person kaefer; 30.01.2019
comment
Эй, АМьер! Спасибо за ответ! У меня следующий вопрос: если inline работает в F# так же, как, например, в C++, а компилятор просто копирует функцию каждый раз, когда она используется, не делает ли это плохим решением? И что вообще функциональное программирование может сказать о inline вообще? - person Dmitry Volkov; 30.01.2019
comment
@DmitryVolkov inline расширяется в точке вызова, но он намного мощнее. Поступая таким образом, он распространяет ограничения членов любого используемого универсального арифметического оператора. Это также заразно. - person kaefer; 30.01.2019
comment
Насколько я знаю, ФП вообще равнодушен к использованию inline. Чрезмерное их использование может затруднить отладку. Для общих арифметических операций они являются простым и эффективным решением. Альтернативой является использование ограничений, но реализовать их гораздо сложнее. - person AMieres; 30.01.2019