Сопоставление с образцом единиц измерения в F # - PullRequest
4 голосов
/ 19 апреля 2010

Эта функция:

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

выдает ошибку

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

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

Ответы [ 3 ]

7 голосов
/ 19 апреля 2010

Как подробно объясняет @ kvb , проблема в том, что единицы измерения являются частью типа. Это означает, что float<m> отличается от типа float<m/s> (и, к сожалению, эта информация не сохраняется как часть значения во время выполнения).

Итак, вы на самом деле пытаетесь написать функцию, которая работала бы с двумя различными типами ввода. Чистое функциональное решение состоит в том, чтобы объявить распознаваемое объединение, которое может содержать значения первого или второго типа:

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

3 голосов
/ 19 апреля 2010

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

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<m> или любой другой тип.

Вы можете попытаться обойти это, сначала поместив v в объект, а затем выполнив проверку типа. Это будет работать, например, если у вас есть list<'a> и вы хотите узнать, был ли он list<int>, потому что полная информация о типах универсальных объектов отслеживается во время выполнения, включая параметры универсального типа (в частности, это отличается от того, как некоторые другие среды выполнения, такие как работа Java). К сожалению, единицы измерения F # стираются во время выполнения, поэтому здесь это не сработает - система не может определить правильный тип меры при представлении в штучной упаковке, поскольку во время выполнения это значение просто float - Система F # для единиц измерения на самом деле очень похожа на то, как Java обрабатывает универсальные типы.

Кроме того, то, что вы пытаетесь сделать, кажется довольно подозрительным - функции, которые являются общими в единице измерения, не должны делать разные вещи в зависимости от типа меры; они должны быть правильно параметрическими. Что именно вы пытаетесь достичь? Это, конечно, не похоже на операцию, которая соответствует физической реальности, которая является основой для типов измерений F #.

0 голосов
/ 15 мая 2013

См. Единицы во время выполнения Раздел в http://msdn.microsoft.com/en-us/library/dd233243.aspx.

Я согласен с @kvb, я думаю, что лучший способ обойти это - передать объект.

Что бы я хотел сделать, используя вашу структуру кода:

let convert (v: float<_>) =
  match v with
  | :? float<m> -> v<m>
  | :? float<inches> -> v * 2.54 / 100.0<m>
...