Функции с универсальными типами параметров - PullRequest
46 голосов
/ 01 февраля 2009

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

let sqrt_int = function
    | n:int   -> int (sqrt (float n))
    | n:int64 -> int64 (sqrt (float n))

Компилятор, конечно, жалуется, что синтаксис недопустим (ограничения типов в сопоставлении с образцом не поддерживаются, кажется), хотя я думаю, что это иллюстрирует то, чего я хотел бы достичь: функция, которая работает с несколькими типами параметров и возвращает значение соответствующего типа. У меня есть ощущение, что это возможно в F #, используя некоторую комбинацию общих типов / вывод типов / сопоставление с образцом, но синтаксис ускользнул от меня. Я также пытался использовать:? оператор (динамические тесты типов) и когда * в блоке сопоставления с образцом, но это все равно вызывает ошибки всех видов.

Поскольку я довольно плохо знаком с языком, я вполне могу попытаться сделать что-то невозможное здесь, поэтому, пожалуйста, дайте мне знать, если есть альтернативное решение.

Ответы [ 5 ]

61 голосов
/ 01 февраля 2009

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

  • Использовать перегрузку для методов (членов типа), в этом случае перегрузка работает так же, как и в других языках .Net (вы можете перегрузить специальные элементы, если вызовы можно различить по количеству / типу параметров)
  • Используйте «inline», «^» и статические ограничения членов для специальной перегрузки функций (это то, что большинство различных математических операторов, которые должны работать с int / float / и т.д .; синтаксис здесь странный это мало используется, кроме библиотеки 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
18 голосов
/ 29 сентября 2013

Это работает:

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)

Подробнее об этом здесь .

14 голосов
/ 01 февраля 2009

Да, это можно сделать. Взгляните на этот поток hubFS .

В этом случае решение будет:

let inline retype (x:'a) : 'b = (# "" x : 'b #)
let inline sqrt_int (n:'a) = retype (sqrt (float n)) : 'a

Предостережение : нет проверки типов во время компиляции. То есть sqrt_int "blabla" компилируется нормально, но вы получите FormatException во время выполнения.

9 голосов
/ 02 февраля 2009

Вот еще один способ использования проверок типов во время выполнения ...

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
2 голосов
/ 01 февраля 2009

Не отнимать от уже предоставленных правильных ответов, но вы можете фактически использовать ограничения типа в сопоставлении с образцом Синтаксис:

| :? type ->

Или, если вы хотите совместить проверку типов и приведение типов:

| :? type as foo ->
...