Сопоставление с образцом единиц измерения в F#

Эта функция:

let convert (v: float<_>) =
  match v with
  | :? float<m> -> v / 0.1<m>
  | :? float<m/s> -> v / 0.2<m/s>
  | _ -> failwith "unknown"

производит ошибку

Тип 'плавание <'u>' не имеет никаких надлежащих подтипов и не может использоваться в качестве источника проверки печатания или приведения во время выполнения.

Есть ли какой-либо путь, как скопировать единицы измерения соответствия?

5
задан svick 9 November 2014 в 13:32
поделиться

2 ответа

Как подробно объясняет @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 # удалит информацию о модуле.

7
ответ дан 13 December 2019 в 22:04
поделиться

У вашего подхода есть две проблемы. Прежде всего, когда вы используете подчеркивание в определении вашей функции, это то же самое, что и использование новой переменной типа, поэтому ваше определение эквивалентно следующему:

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 #.

3
ответ дан 13 December 2019 в 22:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: