Как мне объявить универсальный параметр в 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"]
Другие вопросы по тегам