F# 使用可区分联合的基本类型

Luk*_*keP 3 f# discriminated-union

我正在学习 F# 并努力尝试使用受歧视的联合。我有一个简单的案例,我试图Map.map在类型 Map 的简单可区分联合上使用,但它说存在类型不匹配。我基本上只是想使用类型价格作为地图

这是一个简化的示例:

type Prices = Prices of Map<string, int>

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

给我这个错误:

/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>'
Run Code Online (Sandbox Code Playgroud)

鉴于我在 map 函数中所做的一切都是返回值 * 2 我不明白为什么我会收到这个错误。

Fyo*_*kin 9

你不能“使用PricesMap”,因为Prices不是一个Map。您定义它的方式Prices是一种不同的类型,与 a 完全不同Map,但它在其中包含a 的实例Map

如果这确实是您的意思,那么为了得到Map一个Prices值,您需要对其进行模式匹配。像这样:

let GetSalePrice (Prices theMap) = theMap |> Map.map ...
Run Code Online (Sandbox Code Playgroud)

哇,这是怎么回事?如何Prices theMap不同prices: Prices?为什么我们将类型名称放在参数前面而不是通过冒号放在后面?这不是 F# 中类型的表示方式吗?

您可能会有些困惑,因为您Prices对类型及其构造函数使用了相同的名称。为了解决这个问题,让我像这样重新定义你的类型:

type PricesType = PricesCtor of Map<string, int>
Run Code Online (Sandbox Code Playgroud)

现在函数看起来像:

let GetSalePrice (PricesCtor theMap) = theMap |> Map.map ...
Run Code Online (Sandbox Code Playgroud)

所以你看,它不是我们放在参数前面的类型。它是构造函数。这个声明 - (PricesCtor theMap)- 告诉编译器我们期待一个类型的参数PricesType(因为它是PricesCtor属于哪里的),当我们得到这个参数时,它应该被解包,其中包含的映射应该被命名为theMap

这整个过程称为“模式匹配”。在这里,我们在构造函数上进行匹配PricesCtor


另一方面,您的原始函数仅指定参数的类型。使用我的新类型定义,我可能会像这样编写原始函数:

let GetSalePrice (prices: PricesType) = prices |> Map.map ...
Run Code Online (Sandbox Code Playgroud)

在这里,我们指定我们的参数应该有 type PricesType,但是我们试图将它用作 的参数Map.map,它需要一个 type 的参数Map<_,_>。难怪有类型不匹配!


模式匹配也不必出现在参数声明中。您可以在代码中的任何位置进行模式匹配。为此,请使用match关键字。这就是您的函数可以这样编写的方式:

let GetSalePrice prices =
    match prices with
    | PricesCtor theMap -> theMap |> Map.map ...
Run Code Online (Sandbox Code Playgroud)

match只要您的类型具有多个构造函数,关键字就变得很重要。例如:

type PricesType = PricesAsAMap of Map<string, int> | SinglePrice as int
Run Code Online (Sandbox Code Playgroud)

在这种情况下,如果您在参数声明中指定模式:

let GetSalePrice (PricesAsAMap theMap) = ...
Run Code Online (Sandbox Code Playgroud)

编译器会警告您模式匹配不完整。确实,您的函数知道在给定SinglePrice值时该做什么,但是当给定 a 时它应该做什么ConstantPrice?你还没有定义它,所以编译器会抱怨。

此设置是使用match关键字的场合:

let GetSalePrice prices = 
    match prices with
    | PricesAsAMap theMap -> theMap |> Map.map ...
    | SinglePrice p -> "single item", p
Run Code Online (Sandbox Code Playgroud)