Как мне объявить универсальный параметр в F#?
Учитывая следующий код:
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 ответа
Функции, которые передаются в качестве аргументов другим функциям (например, ваши 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# не может сделать вещи, которые не являются синтаксическими функциями, универсальными, но в вашем примере вы пишете синтаксические функции, так что здесь проблема не в этом.
Это работает также
let NotWorking (display:(obj -> unit) -> obj list -> unit) =
display (printfn "%O") [1;2;3]
display (printfn "%O") ["a";"b";"c"]