F # Использовать базовый тип размеченного объединения - PullRequest
3 голосов
/ 16 июня 2020

Я изучаю F # и изо всех сил пытаюсь использовать дискриминируемые объединения. У меня есть простой случай, когда я пытаюсь использовать Map.map в простом размеченном объединении типа Map, но в нем говорится о несоответствии типов. Я просто пытаюсь использовать тип «Цены» как карту

Вот упрощенный пример:

type Prices = Prices of Map<string, int>

let GetSalePrice (prices: Prices) = prices |> Map.map (fun k v -> (k, v * 2))

Выдает мне эту ошибку:

/Users/luke/code/chronos/Chronos.Mining/Chronos.Mining.Actors/Untitled-1(22,47): error FS0001: Type mismatch. Expecting a
    'Prices -> 'a'    
but given a
    'Map<'b,'c> -> Map<'b,'d>'    
The type 'Prices' does not match the type 'Map<'a,'b>'

Учитывая что все, что я делаю в функции карты, возвращает значение * 2 Я не понимаю, почему я получаю эту ошибку.

1 Ответ

9 голосов
/ 16 июня 2020

Вы не можете « использовать Prices как Map», потому что Prices это не a Map. Как вы его определили, Prices - это другой тип, совсем не то же самое, что Map, но содержит экземпляр Map внутри него.

Если это действительно то, что вы имели в виду, то, чтобы получить Map из значения Prices, вам необходимо сопоставить его с шаблоном. Как это:

let GetSalePrice (Prices theMap) = theMap |> Map.map ...

Эй, что здесь происходит? Чем Prices theMap отличается от prices: Prices? Почему мы помещаем имя типа перед параметром, а не после него через двоеточие? Разве не так обозначаются типы в F #?

Вы можете немного запутаться, потому что вы используете одно и то же имя Prices как для типа, так и для его конструктора. Чтобы прояснить это, позвольте мне переопределить ваш тип следующим образом:

type PricesType = PricesCtor of Map<string, int>

Теперь функция будет выглядеть так:

let GetSalePrice (PricesCtor theMap) = theMap |> Map.map ...

Итак, вы видите, это не тот тип, который мы поставив перед параметром. Это конструктор. И это объявление - (PricesCtor theMap) - сообщает компилятору, что мы ожидаем параметр типа PricesType (потому что ему принадлежит PricesCtor), и когда мы получим этот параметр, он должен быть развернут, а карта, содержащаяся внутри должен называться theMap.

Весь этот процесс называется «сопоставлением с образцом». Здесь мы сопоставляем конструктор PricesCtor.


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

let GetSalePrice (prices: PricesType) = prices |> Map.map ...

Здесь мы указываем, что наш параметр должен иметь тип PricesType, но затем мы пытаемся использовать его в качестве аргумента для Map.map, который ожидает параметр типа Map<_,_>. Неудивительно, что существует несоответствие типов!


Сопоставление с образцом также не обязательно должно быть в объявлении параметра. Вы можете сопоставить шаблон в любом месте кода. Для этого используйте ключевое слово match. Вот как ваша функция может быть записана таким образом:

let GetSalePrice prices =
    match prices with
    | PricesCtor theMap -> theMap |> Map.map ...

Ключевое слово match становится значимым, как только ваш тип имеет более одного конструктора. Например:

type PricesType = PricesAsAMap of Map<string, int> | SinglePrice as int

В этом случае, если вы укажете шаблон в объявлении параметра:

let GetSalePrice (PricesAsAMap theMap) = ...

, компилятор предупредит вас, что совпадение с образцом неполное . Действительно, ваша функция знает, что делать, когда задано значение SinglePrice, но что она должна делать, когда задано ConstantPrice? Вы не определили это, поэтому компилятор будет жаловаться.

Эта настройка - повод использовать ключевое слово match:

let GetSalePrice prices = 
    match prices with
    | PricesAsAMap theMap -> theMap |> Map.map ...
    | SinglePrice p -> "single item", p
...