Функции с общими типами параметров
Я пытаюсь выяснить, как определить функцию, которая работает с несколькими типами параметров (например, int и int64). Насколько я понимаю, перегрузка функций невозможна в F # (конечно, компилятор жалуется). Возьмем, например, следующую функцию.
let sqrt_int = function
| n:int -> int (sqrt (float n))
| n:int64 -> int64 (sqrt (float n))
Компилятор, конечно, жалуется, что синтаксис недействителен (ограничения типа в сопоставлении шаблонов не поддерживаются), хотя я думаю, что это иллюстрирует то, что я хотел бы достичь: функция, которая работает с несколькими типами параметров и возвращает значение соответствующего типа. У меня есть ощущение, что это возможно в F #, используя некоторую комбинацию типичных типов/типов inference/pattern matching, но синтаксис ускользнул от меня. Я также пробовал использовать:? оператор (тесты динамического типа) и предложения в блоке сопоставления шаблонов, но при этом все еще возникают всевозможные ошибки.
Поскольку я довольно новичок в языке, я вполне могу попытаться сделать что-то невозможное здесь, поэтому, пожалуйста, дайте мне знать, есть ли альтернативное решение.
Ответы
Ответ 1
Перегрузка, как правило, является bugaboo языков с типом вывода (по крайней мере, когда, например, F #, система типов недостаточно мощна, чтобы содержать классы типов). В F # есть ряд вариантов:
- Использовать перегрузку по методам (членам типа), и в этом случае перегрузка работает так же, как и на других языках .Net(вы можете использовать специальные элементы перегрузки, если вызовы могут различаться по числу/типу параметров)
- Используйте "встроенные", "^" и статические ограничения для ad-hoc для перегрузки по функциям (это то, что большинство различных математических операторов, которые должны работать на int/float/etc, синтаксис здесь странный, это мало используется помимо библиотеки F #)
- Имитировать классы типов, передавая дополнительный параметр словаря операций (это то, что INumeric делает в одной из библиотек F # PowerPack для обобщения различных математических алгоритмов для произвольных пользовательских типов)
- Вернитесь к динамической типизации (перейдите в параметр "obj", выполните тест динамического типа, выбросите исключение во время выполнения для плохого типа)
В вашем конкретном примере я, вероятно, просто воспользуюсь перегрузкой метода:
type MathOps =
static member sqrt_int(x:int) = x |> float |> sqrt |> int
static member sqrt_int(x:int64) = x |> float |> sqrt |> int64
let x = MathOps.sqrt_int 9
let y = MathOps.sqrt_int 100L
Ответ 2
Да, это можно сделать. Посмотрите этот поток hubFS.
В этом случае решением будет:
let inline retype (x:'a) : 'b = (# "" x : 'b #)
let inline sqrt_int (n:'a) = retype (sqrt (float n)) : 'a
Предостережение: проверка типа компиляции не производится. То есть sqrt_int "blabla"
компилируется отлично, но во время выполнения вы получите исключение FormatException.
Ответ 3
Это работает:
type T = T with
static member ($) (T, n:int ) = int (sqrt (float n))
static member ($) (T, n:int64) = int64 (sqrt (float n))
let inline sqrt_int (x:'t) :'t = T $ x
Он использует статические ограничения и перегрузку, что делает поиск во время компиляции по типу аргумента.
Статические ограничения автоматически генерируются в присутствии оператора (оператор $
в этом случае), но он всегда может быть записан вручную:
type T = T with
static member Sqr (T, n:int ) = int (sqrt (float n))
static member Sqr (T, n:int64) = int64 (sqrt (float n))
let inline sqrt_int (x:'N) :'N = ((^T or ^N) : (static member Sqr: ^T * ^N -> _) T, x)
Подробнее об этом здесь.
Ответ 4
Здесь другой способ проверки типов выполнения...
let sqrt_int<'a> (x:'a) : 'a = // '
match box x with
| :? int as i -> downcast (i |> float |> sqrt |> int |> box)
| :? int64 as i -> downcast (i |> float |> sqrt |> int64 |> box)
| _ -> failwith "boo"
let a = sqrt_int 9
let b = sqrt_int 100L
let c = sqrt_int "foo" // boom
Ответ 5
Не убирать правильные ответы, уже предоставленные, но на самом деле вы можете использовать ограничения типа при сопоставлении шаблонов. Синтаксис:
| :? type ->
Или, если вы хотите совместить проверку типов и литье:
| :? type as foo ->