Как мне объявить универсальный параметр в F #? - PullRequest
3 голосов
/ 08 декабря 2011

Учитывая следующий код:

let DisplayImpl logger data =
    data |> Seq.iter logger
    printfn ""

let Working =
    DisplayImpl (printfn "%O") [1;2;3]
    DisplayImpl (printfn "%O") ["a";"b";"c"]

let NotWorking display =
    display (printfn "%O") [1;2;3]
    display (printfn "%O") ["a";"b";"c"]
                            ~~~ ~~~ ~~~

В последней строке выдается ошибка: This expression was expected to have type int but here has type string

Я думал, что следующее может работать, но это не так:

let StillNotWorking (display: ('a -> unit) -> seq<'a> -> unit) =

Мой вопрос: как мне определить функцию NotWorking, чтобы параметр отображения оставался общим для функции?

Ответы [ 2 ]

6 голосов
/ 08 декабря 2011

Функции, которые передаются в качестве аргументов другим функциям (например, display), сами по себе не могут быть полиморфными в F #.Они могут использовать параметр общего типа ('a и т. Д.), Но фактический тип для этого параметра указывается при вызове основной функции (NotWorking в вашем случае).Это означает, что вы можете вызывать display только с одним действительным типом, используемым для переменной типа 'a в теле NotWorking.

. В качестве временного решения вы можете использовать интерфейс с универсальным методом:

type Displayer = 
  abstract Display : (obj -> unit) -> 'T list -> unit

let NotWorking (display:Displayer) = 
    display.Display (printfn "%O") [1;2;3] 
    display.Display (printfn "%O") ["a";"b";"c"] 

Метод Display интерфейса сам по себе является универсальным методом, поэтому вы можете вызывать этот метод несколько раз с аргументами разных типов (int в первом случае и string во втором).

Однако я не нашел это ограничение при написании нормального кода на F # очень часто, поэтому, возможно, есть более простое решение для вашей проблемы (возможно, использование неуниверсального IEnumerable или чего-то простогокак это - или obj list, как в ответе Джона).Если вы дадите более подробную информацию о вашем фактическом коде, это было бы полезно.

Некоторая справка , на тот случай, если вас интересуют теоретические детали, но на самом деле все это не то, что могло быиграть важную роль в повседневном программировании на F #.В любом случае -

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

Когда вы вызываете функцию, вам нужно указать, что такое 'a и что нельзя изменить (т. Е. Вы не можете использовать функцию 'a -> unit, которую вы получаете в качестве аргумента с двумя различными типами для 'a как только 'a исправлено).

Используя универсальные типы, вы можете написать forall самостоятельно, так что вы можете сказать, что тип:
(forall 'a . 'a -> unit) -> unit.Теперь универсальный параметр 'a связан только с функцией, которую вы получите в качестве аргумента.Тип функции, заданной в качестве аргумента, теперь сам по себе является универсальной функцией, и поэтому вы можете вызывать ее с разными типами, обозначающими 'a.

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

5 голосов
/ 08 декабря 2011

Это также работает

let NotWorking (display:(obj -> unit) -> obj list -> unit) =
    display (printfn "%O") [1;2;3]
    display (printfn "%O") ["a";"b";"c"]
...