【问题标题】:Ambiguity with pattern matching records模式匹配记录的歧义
【发布时间】:2017-07-04 11:54:32
【问题描述】:

我从this question 了解到可以对记录使用模式匹配。但是,我注意到我在尝试匹配不同类型的记录时遇到了问题。

我在这个例子中的目标是能够区分不同的记录。我收到了一条我不完全确定它是哪种类型的记录,我正在尝试使用模式匹配来找出它。

这是一个简化的例子:

module IceCream = struct
  type t = {
    temperature: float;
    toppings: string list;
  }
end

module Candy = struct
  type t = {
    flavour: string;
    colour: string;
    volume: int;
  }
end


(* Could be Candy or IceCream *)
let example =
  { Candy.
    flavour = "mint";
    colour = "green";
    volume = 10 }

let printFavoriteTreat treat = match treat with
  | { Candy.
      flavour = "mint";
      colour;
      volume } -> "It's Candy"
  | { IceCream.
      temperature;
      toppings } -> "It's IceCream"


let () = printFavoriteTreat example

当我尝试构建此文件时,我得到:

Error: The field IceCream.temperature belongs to the record type IceCream.t
       but a field was expected belonging to the record type Candy.t

这样的事情可能吗?

【问题讨论】:

  • 不可能对不同类型进行模式匹配,除非它们嵌入到 sum 类型(也称为变体类型、代数数据类型、可区分联合)中。

标签: ocaml


【解决方案1】:

我收到了一条我不完全确定它是哪种类型的记录,我正在尝试使用模式匹配来找出它。

这是不可能的。类型只存在于编译时,因此无法在运行时检查它是什么类型。

换句话说,在一个有效的程序中,您可以在每个表达式上放置一个类型注释(不过,在大多数情况下,您不必这样做,这要归功于类型推断)。如果您不能这样做,那么您应该以不同的方式设计您的程序,例如使用其他人建议的 sum 类型 - 在这种情况下,两个值将具有相同的类型(在编译时)但不同的构造函数(在运行时)。

【讨论】:

    【解决方案2】:

    皮埃尔提供的答案很好,但例子不多。 (我一直讨厌名为ab...的示例)

    因此,正如 Pierre 建议的那样,您可以像这样定义您的类型:

    type ice_cream = {
      temperature: float;
      toppings: string
    }
    
    type candy = {
      flavor: string;
      color: string;
      volume: int
    }
    

    然后,您可以将treat 定义为这两种类型的variant

    type treat =
    | Candy of candy
    | IceCream of ice_cream
    

    然后,使用模式匹配:

    let print_favorite_treat = function
    | Candy _ -> print_endline "You love candy!"
    | IceCream _ -> print_endline "You love ice cream!"
    

    【讨论】:

    • 函数被称为print_favorite_treat这一事实很好地暗示了需要某种treat类型。 :)
    • 啊,我希望我可以避免变体,但看起来您无法按照我尝试的方式匹配记录,谢谢您的回答:)
    • 您需要一个可以是要么 candy ice_cream 的类型。这是变体的典型用例。 :)
    【解决方案3】:

    您正在尝试在不使用变体类型的情况下匹配不同的类型。

    您使用的模块语法无济于事,因为模块只是构建代码。您可以定义以下类型:

    type a = {
         temperature: float;
         toppings: string list;
      }
    
    type b =  {
        flavour: string;
        colour: string;
        volume: int;
      }
    

    但结果是一样的。

    消除歧义的方法是使用变体类型(或下例中未描述的联合类型):

    let printFavoriteTreat treat = match treat with
      | `A{ Candy.
          flavour = "mint";
          colour;
          volume } -> "It's Candy"
      | `B { IceCream.
          temperature;
          toppings } -> "It's IceCream"
    ;;
    

    还有

    let () = printFavoriteTreat (`A example)
    

    【讨论】:

    • 您应该使用 `Candy 代替 `A 和 `IceCream 代替 `B。并解释为什么多态变体(+ 包括| _ -> "I don't know that treat")而不是正常的变体。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-10-29
    • 2011-10-29
    • 2013-10-19
    • 2013-06-25
    • 2018-01-08
    • 1970-01-01
    • 2016-04-03
    相关资源
    最近更新 更多