Гибкие типы (F#)
Аннотация гибкого типа — это указание на то, что параметр, переменная или значение имеет тип, совместимый с указанным типом; совместимость определяется положением в объектно-ориентированной иерархии классов или интерфейсов.Гибкие типы особенно полезны в том случае, когда автоматического преобразования в типы, находящиеся выше в иерархии типов, не происходит, но тем не менее требуется, чтобы функциональность работала с любым типом в иерархии или любым типом, реализующим интерфейс.
#type
Заметки
В приведенной выше синтаксической конструкции type представляет собой базовый тип или интерфейс.
Гибкий тип эквивалентен универсальному типу с ограничением, сводящим разрешенные типы к типам, совместимым с базовым типом или типам интерфейса.Это значит, что следующие две строки кода эквивалентны.
#SomeType
'a when 'a :> SomeType
Существует несколько ситуаций, где полезны гибкие типы.Например, если имеется функция высшего порядка (функция, принимающая функцию в качестве аргумента), зачастую удобно, чтобы эта функция возвращала гибкий тип.В следующем примере использование гибкого типа с аргументом последовательности в iterate2 позволяет функции высшего порядка работать с функциями, формирующими последовательности, массивы, списки и любые другие перечислимые типы.
Рассмотрим следующие две функции, одна из которых возвращает последовательность, а вторая — гибкий тип.
let iterate1 (f : unit -> seq<int>) =
for e in f() do printfn "%d" e
let iterate2 (f : unit -> #seq<int>) =
for e in f() do printfn "%d" e
// Passing a function that takes a list requires a cast.
iterate1 (fun () -> [1] :> seq<int>)
// Passing a function that takes a list to the version that specifies a
// flexible type as the return value is OK as is.
iterate2 (fun () -> [1])
В качестве еще одного примера рассмотрим библиотечную функцию Seq.concat.
val concat: sequences:seq<#seq<'T>> -> seq<'T>
Этой функции можно передать любую из следующих перечислимых последовательностей:
список списков;
список массивов;
массив списков;
массив последовательностей;
любую другую комбинацию перечислимых последовательностей.
В следующем коде используется функция Seq.concat для демонстрации сценариев, которые можно поддерживать, используя гибкие типы.
let list1 = [1;2;3]
let list2 = [4;5;6]
let list3 = [7;8;9]
let concat1 = Seq.concat [ list1; list2; list3]
printfn "%A" concat1
let array1 = [|1;2;3|]
let array2 = [|4;5;6|]
let array3 = [|7;8;9|]
let concat2 = Seq.concat [ array1; array2; array3 ]
printfn "%A" concat2
let concat3 = Seq.concat [| list1; list2; list3 |]
printfn "%A" concat3
let concat4 = Seq.concat [| array1; array2; array3 |]
printfn "%A" concat4
let seq1 = { 1 .. 3 }
let seq2 = { 4 .. 6 }
let seq3 = { 7 .. 9 }
let concat5 = Seq.concat [| seq1; seq2; seq3 |]
printfn "%A" concat5
Выходные данные выглядят следующим образом.
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
В F#, как и в других объектно-ориентированных языках, существуют контексты, в которых производные типы или типы, реализующие интерфейсы, автоматически преобразуются в базовый тип или тип интерфейса.Эти автоматические преобразования происходят в непосредственных аргументах, но не когда тип находится в подчиненном положении, будучи частью более сложного типа, такого как возвращаемый тип или тип функции, или будучи аргументом типа.Таким образом, запись гибкого типа в первую очередь полезна тогда, когда она применяется к типу, являющемуся частью более сложного типа.