Этот пример кода:
type recordA = { X: string; }
type recordB = { X: string; }
let modifyX newX record = { record with X = newX }
let modifiedRecordA = {recordA.X = "X"} |> modifyX "X2"
let modifiedRecordB = {recordB.X = "X"} |> modifyX "X2"
Результаты в:
let modifyX newX record = { record with X = newX }
--------------------------^^^^^^^^^^^^^^^^^^^^^^^^
stdin(4,27): warning FS0667: The field labels and expected type of this record expression or pattern do not uniquely determine a corresponding record type
let modifiedRecordA = {recordA.X = "X"} |> modifyX "X2"
-------------------------------------------^^^^^^^^^^^^
stdin(6,44): error FS0001: Type mismatch. Expecting a
recordA -> 'a
but given a
recordB -> recordB
The type 'recordA' does not match the type 'recordB'
Я ожидаю, что modifiedRecordA будет эквивалентно {recordA.X = "X2"}, а modifiedRecordB будет эквивалентно {recordB.X = "X2"}, но, похоже, это не работает.
- Почему это просто не выводит и не возвращает соответствующий тип записи на основе типа параметра?
- Что я могу сделать, чтобы это сработало?
3 ответа
Встроенная магия, необходимая для выполнения этой работы, основана на разрешении перегрузки в сочетании со статически разрешенными ограничениями членов. Перегрузки, определенные как операторы, позволяют избежать необходимости указывать явные ограничения.
type Foo = Foo with
static member ($) (Foo, record) = fun newX -> { record with recordA.X = newX }
static member ($) (Foo, record) = fun newX -> { record with recordB.X = newX }
let inline modifyX newX record = (Foo $ record) newX
let modifiedRecordA = {recordA.X = "X"} |> modifyX "X2"
let modifiedRecordB = {recordB.X = "X"} |> modifyX "X2"
Конструкции, передающие типы, для которых не существует перегрузки, не компилируются.
type recordC = { X: string; }
let modifiedRecordC = {recordC.X = "X"} |> modifyX "X2"
// Error No overloads match for method 'op_Dollar' ...
// Possible overload ... The type 'recordC' is not compatible with the type 'recordB'
// Possible overload ... The type 'recordC' is not compatible with the type 'recordA'
На самом деле это не предназначено для фактического использования. Прислушайтесь к советам и исследуйте, подходят ли другие подходы для вашей проблемы.
Проблема в том, что компилятор определяет тип modifyX
на основе использования. Насколько я понимаю, это происходит снизу вверх, поэтому предполагается, что тип будет val modifyX : newX:string -> record:recordB -> recordB
. Это, конечно, затем приводит к ошибке типа при попытке использовать это с записью типа recordA
. Предупреждение сообщает вам, что, хотя он выбирает тип, существует другой тип с такими же полями, поэтому все, что может сделать компилятор, - это наилучшим образом угадать, какой тип вы имели в виду. Возможно, удастся добиться того, что вы пытаетесь сделать, с помощью встроенных функций, но я не уверен, как это может работать навскидку.
Функция modifyX
неверна. Вы не можете использовать термин X
в определении и указывать X
на разные поля.
Раздел 6.3.6 спецификации F #
Каждая метка поля должна разрешаться в поле Fi в одном типе записи R, все поля которой доступны.
При передаче recordA
и recordB
в modifyX
X
не определяется однозначно как поле одного типа.
На самом деле вам, вероятно, нужен член полиморфного свойства, унаследованный через интерфейс, а не набор типов записей с общим именем члена.
Похожие вопросы
Новые вопросы
f#
F # - это сжатый, выразительный и эффективный функциональный и объектно-ориентированный язык для .NET, который помогает вам писать простой код для решения сложных задач.