Эта функция:
let convert (v: float<_>) =
match v with
| :? float<m> -> v / 0.1<m>
| :? float<m/s> -> v / 0.2<m/s>
| _ -> failwith "unknown"
производит ошибку
Тип 'плавание <'u>' не имеет никаких надлежащих подтипов и не может использоваться в качестве источника проверки печатания или приведения во время выполнения.
Есть ли какой-либо путь, как скопировать единицы измерения соответствия?
Как подробно объясняет @kvb , проблема в том, что единицы измерения являются частью типа. Это означает, что тип float
отличается от типа float
(и, к сожалению, эта информация не сохраняется как часть значения во время выполнения).
Итак, вы фактически пытаетесь написать функцию, которая работала бы с двумя разными типами ввода.Чистое функциональное решение - объявить размеченное объединение, которое может содержать значения либо первого, либо второго типа:
type SomeValue =
| M of float<m>
| MPS of float<m/s>
Затем вы можете написать функцию, используя обычное сопоставление с образцом:
let convert v =
match v with
| M v -> v / 0.1<m>
| MPS v -> v / 0.2<m/s>
Вам нужно будет явно обернуть значения в размеченное значение объединения, но это, вероятно, единственный способ сделать это напрямую (без внесения каких-либо значительных изменений в структуру программы).
Для обычных типов, таких как int
и float
, вы также можете использовать перегруженные элементы (объявленные в некоторых типах F #), но это не работает для единиц измерения, поскольку подпись будет такой же после того, как компилятор F # удалит информацию о модуле.
У вашего подхода есть две проблемы. Прежде всего, когда вы используете подчеркивание в определении вашей функции, это то же самое, что и использование новой переменной типа, поэтому ваше определение эквивалентно следующему:
let convert (v: float<'u>) = //'
match v with
| :? float<m> -> v / 0.1<m>
| :? float<m/s> -> v / 0.2<m/s>
| _ -> failwith "unknown"
Сообщение об ошибке сообщает вам, что компилятор знает что v
имеет тип float <'u>
, а float <' u>
не имеет подходящих подтипов, поэтому нет смысла проводить проверку типа для определите, является ли это float
или любым другим типом.
Вы можете попытаться обойти это, сначала поместив v
в объект, а затем выполнив типовой тест.Это сработает, например, если у вас есть список <'a>
и вы хотите узнать, был ли это список
, потому что отслеживается полная информация о типах общих объектов во время выполнения, включая параметры универсального типа (в частности, это отличается от того, как работают некоторые другие среды выполнения, такие как Java). К сожалению, единицы измерения F # стираются во время выполнения, поэтому здесь это не сработает - система не может вывести правильный тип меры с учетом коробочного представления, поскольку во время выполнения значение представляет собой просто float.
- Система единиц измерения F # на самом деле очень похожа в этом отношении на то, как Java обрабатывает универсальные типы.
Кстати, то, что вы пытаетесь сделать, кажется довольно подозрительным - функции, которые являются общими в единицах измерения, не должны выполнять разные действия в зависимости от типа меры; они должны быть правильно параметрическими. Чего именно вы пытаетесь достичь? Это, конечно, не похоже на операцию, соответствующую физической реальности, которая является основой для типов мер F #.