Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Встроенная функция со статически разрешенными параметрами типа может требовать член с определенным именем и сигнатурой. F# 11.2 позволяет члену расширения в области удовлетворить это требование, даже если тип поступает из другой сборки.
Используйте эту возможность для разделения универсального алгоритма от адаптеров для определенных типов. Вам не нужно упаковывать эти типы или добавлять в них номинальный интерфейс.
Note
В пакете SDK .NET 11.0.100 включите<LangVersion>preview</LangVersion>, где компилятор разрешает расширения. См. параметры языка F# 11.2 для запланированной группы компонентов. Примеры пути и сопоставления используют библиотеку F# 11.0 и потребитель предварительной версии; см. статью "Компиляция библиотек и потребителей".
Адаптация свойства для общего алгоритма
Следующие пути кода приложения измеряет пути, представленные двумя существующими типами платформ. Оба пути имеют длину 8:
open System.Numerics
open Structural.Adapters
open Structural.Algorithms
let complexRoute =
[ Complex(0.0, 0.0); Complex(3.0, 4.0); Complex(6.0, 4.0) ]
let vectorRoute =
[ Vector2(0.0f, 0.0f); Vector2(3.0f, 4.0f); Vector2(6.0f, 4.0f) ]
printn (pathLength complexRoute) // 8
printn (pathLength vectorRoute) // 8
Источник библиотекиStructural.Adapters определяет и Structural.Algorithms. Адаптеры предоставляют одно и то же свойство координат для типов с различными собственными API:
type System.Numerics.Complex with
member point.Coordinates = point.Real, point.Imaginary
type System.Numerics.Vector2 with
member point.Coordinates = float point.X, float point.Y
Алгоритм запрашивает Coordinates свойство, возвращающее пару значений с плавающей запятой:
let inline coordinates (point: ^Point) : float * float =
(^Point : (member Coordinates: float * float) point)
let inline pathLength (points: ^Point list) =
points
|> List.pairwise
|> List.sumBy (fun (a, b) ->
let ax, ay = coordinates a
let bx, by = coordinates b
let dx, dy = bx - ax, by - ay
sqrt (dx * dx + dy * dy))
При каждом конкретном вызове компилятор выбирает Coordinates член, называемый следящий элемент ограничения. Встроенный алгоритм принимает встроенное или расширенное свойство; Пустые и одноточечные пути имеют нулевую длину.
Совместное использование операции между типами коллекций
Ограничения расширения также поддерживают статические элементы. Модуль библиотеки Structural.Adapters определяет адаптеры с помощью существующих List.map и Option.map функций:
type List<'T> with
static member Map(source: 'T list, mapping: 'T -> 'U) : 'U list =
List.map mapping source
type Option<'T> with
static member Map(source: 'T option, mapping: 'T -> 'U) : 'U option =
Option.map mapping source
В модуле библиотеки Structural.Algorithms запросите Map члена по полному типу входных данных:
let inline map (mapping: 'T -> 'U) (source: ^Input) : ^Output =
(^Input :
(static member Map: ^Input * ('T -> 'U) -> ^Output)
(source, mapping))
Затем приложение может предоставить общий доступ к преобразованию, изменить тип элемента и создать сопоставление по вложенной коллекции:
let inline cleanNames values =
map (fun (name: string) -> name.Trim().ToUpperInvariant()) values
let batch: string list = cleanNames [ " Ada "; " grace " ]
let optional: string option = cleanNames (Some " linus ")
let missing: string option = cleanNames (None: string option)
let lengths: int list = map String.length batch
let nested: int option list = map (map String.length) [ Some "abc"; None ]
printfn "%A; %A; %A" batch optional missing
printfn "%A; %A" lengths nested
// ["ADA"; "GRACE"]; Some "LINUS"; None
// [3; 5]; [Some 3; None]
Компилятор проверяет имена и типы элементов, а не интерфейсы или законы сопоставления. Это не более высокий полиморфизм: параметр не представляет конструктор контейнера, F<_>например.
Расширения операторов
Операторы могут использовать тот же механизм. Например, определите оператор сопоставления для списков и параметров:
type List<'T> with
static member (|>>) (source: 'T list, mapping: 'T -> 'U) =
List.map mapping source
type Option<'T> with
static member (|>>) (source: 'T option, mapping: 'T -> 'U) =
Option.map mapping source
let inline (|>>) (source: ^Input) (mapping: 'T -> 'U) : ^Output =
(^Input :
(static member (|>>): ^Input * ('T -> 'U) -> ^Output)
(source, mapping))
Потребитель открывает StructuralOperators и использует оператор:
open StructuralOperators
let incremented: int list = [1; 2; 3] |>> ((+) 10)
let length: int option = Some "hello" |>> String.length
// [11; 12; 13] and Some 5
Встроенные примитивные решения операторов сохраняют приоритет для соответствующих типов. Расширение не обеспечивает произвольное расширение с смешанным числом или не заменяет все встроенные операторы.
Режим F# 11.0 может принимать обычное объявление оператора расширения с FS1215, но не может использовать его в качестве решения SRTP. Проект оператора использует предварительную версию.
Операторы кортежа и контекст
Коллекция наблюдений и пара независимо накопленных результатов могут участвовать в одном и том же протоколе сочетания. Следующий потребитель определяет его универсальный алгоритм после открытия адаптеров библиотеки:
open Composition.Adapters
open Composition.Algorithms
let inline combineAll (batches: ^T list) : ^T =
List.reduce (++) batches
let observations = combineAll [["temperature"]; ["pressure"; "humidity"]]
let summary = combineAll [(2, ["temperature"]); (3, ["pressure"; "humidity"])]
printfn "%A" observations
printfn "%A" summary
// ["temperature"; "pressure"; "humidity"]
// (5, ["temperature"; "pressure"; "humidity"])
Эти расширения библиотеки дают оператору значение для целых чисел, списков и пар:
type System.Int32 with
static member (++) (left: int, right: int) = left + right
type List<'T> with
static member (++) (left: 'T list, right: 'T list) = left @ right
type ('T1 * 'T2) with
static member inline (++) ((a, b), (c, d)) = (a ++ c, b ++ d)
combineAll требуется список nonempty, так как он используется List.reduce. Операция сохраняет порядок списка; Компилятор не применяет ассоциативность или не предоставляет удостоверение для пустых входных данных.
Собственные заголовки type ('T1 * 'T2) with и type struct ('T1 * 'T2) with расширение ссылок и значений среды CLR. Оба являются функциями F# 11.2 и поддерживают два-семь элементов; Большие заголовки создают FS3915.
Кортежи также могут содержать контекст вместе со значением. Следующий оператор добавляет след при применении контекстной функции:
let priced =
(["catalogue price"], fun units -> decimal units * 12m)
<*> (["validated quantity"], 3)
printfn "%A" priced
// (["catalogue price"; "validated quantity"], 36M)
type ('Context * 'Value) with
static member inline (<*>) ((before, mapping), (after, value)) =
(before ++ after, mapping value)
type struct ('Context * 'Value) with
static member inline (<*>) (struct (before, mapping), struct (after, value)) =
struct (before ++ after, mapping value)
Вызовы точечного экземпляра кортежа и заголовки собственного типа функций, такие как type ('a -> 'b) with не поддерживаются.
Библиотека композиции и потребитель используют предварительную версию. Сохраняйте combineAll в потребителе: экспорт этого сокращения может производить FS0193 для кортежа ++. Примеры отдельных путей и сопоставлений экспортируют свои алгоритмы.
Возвратно-направленная конструкция
Для операции, которая вводит обычное значение в контейнер, тип вывода может определить, какой член должен создать его:
type List<'T> with
static member Return(value: 'T) : 'T list = [value]
type Option<'T> with
static member Return(value: 'T) : 'T option = Some value
let inline singleton (value: 'T) : ^Output =
(^Output : (static member Return: 'T -> ^Output) value)
let oneBatch: string list = singleton "ready"
let oneOptional: string option = singleton "ready"
// ["ready"] and Some "ready"
Примечания к результату потребителя выбирает расширение типа Return вывода. Входные данные не могут выбрать контейнер.
Каждый контейнер предоставляет свой собственный член. Для перегрузок только для возврата в одном типе используйте отдельный AllowOverloadOnReturnType атрибут.
Компиляция библиотек и потребителей
Параметры языка применяются для каждого проекта, а не транзитивно через ссылки. В следующей матрице используются параметры пакета SDK .NET 11.0.100 для функций F# 11.2:
| Реализация библиотеки | Использование потребителя | Требование к предварительной версии |
|---|---|---|
| Объявляет собственные заголовки расширения кортежа. | Разрешает операторы расширения. | Оба требуют предварительной версии; Новый синтаксис заголовка является предварительным просмотром. |
| Определяет обычные расширения и экспортирует неразрешенные ограничения встроенного члена. | Предоставляет конкретные типы, расширения которых решают это ограничение. | Потребитель нуждается в предварительной версии. Библиотека может использовать F# 11.0. |
| Решает ограничение расширения внутренне и предоставляет обычный или уже разрешенный встроенный API. | Вызывает этот API без дальнейшего разрешения расширения. | Библиотека нуждается в предварительной версии; потребитель может использовать F# 11.0. |
| Решает ограничения расширений и экспортирует ограничения, которые потребитель также разрешает с помощью расширений. | Разрешает оставшиеся ограничения. | Оба требуют предварительной версии. |
Библиотека путей и сопоставлений использует 11.0; ее потребитель использует preview. В каталоге потребителей запустите dotnet run вспомогательный пакет SDK F# 11.
Область расширения между сборками
Используйте общедоступные расширения и откройте модуль перед определением зависимых встроенных функций. Для импортированного неразрешенного ограничения также откройте адаптеры в потребителе; Объявления продюсера open не передаются. Для уже разрешенного API не требуется, чтобы потребитель повторно открыл свои адаптеры.
Вывод и подписи
Разрешение F# 11.2 откладывает некоторые варианты, которые делает режим F# 11.0 ранее. Рассмотрим встроенный вспомогательный элемент, определенный с приемником DateTime :
let inline shift (date: System.DateTime) offset = date + offset
В режиме F# 11.0 эта форма может вывести DateTime -> TimeSpan -> DateTime. С включенным разрешением расширения F# 11.2 он может сохранить ограничение для (DateTime or ^Offset) : (static member (+) : DateTime * ^Offset -> ^Result). Затем потребитель может выбрать общедоступное расширение в области для другого типа смещения.
Потребитель не может восстановить общее значение, потерянное в библиотеке F# 11.0. Проверьте экспортированные подписи; Производитель также может потребовать включения вывода F# 11.2.
Ограничения предварительной версии и устранение неполадок
- Для FS0001 или FS0193 проверьте параметры языка, общедоступную видимость и область адаптера.
- Если более поздний файл производителя разрешает пример
Complexпути, создание кода может завершиться ошибкой с помощью FS0971.Undefined value 'get_Coordinates'Имейте в виду это разрешение с его определениями или отдельным потребителем. - Конкурирующие адаптеры могут сделать выбор элементов неоднозначным.