不能泛化的变量

Mir*_*rza 0 ocaml

我正在尝试创建具有 Hashtable 值的 Hashtable,如下所示:

let map = ((Hashtbl.create 100) : ((int, (_,int) Hashtbl.t) Hashtbl.t ));;
Run Code Online (Sandbox Code Playgroud)

但不幸的是,转换类型对我没有帮助,编译器说:“Batteries.Hashtbl.t,包含无法泛化的类型变量”。我怎么解决这个问题?

Jef*_*eld 5

您显然希望包含的哈希表的键是任何类型的。但这是不可能的,OCaml 是一种强类型语言。

如果您事先知道类型,您可以创建一个变体类型,它将不同类型的标记值收集在一起并从中生成一种类型。

执行此操作的一种方法可能如下所示,其中每个内部哈希表都有一个一致的键类型。

type intstrHT = IntHT of (int, int) Hashtbl.t |
                StrHT of (string, int) Hashtbl.t

let map : (int, intstrHT) Hashtbl.t = Hashtbl.create 100
Run Code Online (Sandbox Code Playgroud)

另一种可能性是您希望在同一个哈希表中使用不同类型的键:

type intstr = Int of int | Str of string

let map : (int, (intstr, int) Hashtbl.t) Hashtbl.t = Hashtbl.create 100
Run Code Online (Sandbox Code Playgroud)

更新

您不能拥有具有未指定键类型的哈希表。请注意以下会话:

$ ocaml
        OCaml version 4.02.1

# let map : (_, int) Hashtbl.t = Hashtbl.create 100;;
val map : ('_a, int) Hashtbl.t = <abstr>
# Hashtbl.add map "yes" 15;;
- : unit = ()
# Hashtbl.add map 44 88;;
Error: This expression has type int but an expression was expected of type
         string
# map;;
- : (string, int) Hashtbl.t = <abstr>
Run Code Online (Sandbox Code Playgroud)

您可以将键的类型声明为“未指定”,但这只是意味着稍后会弄清楚。您仍然只能拥有一种类型的密钥。在上面,编译器计算出您的密钥类型是string.

如果编译器在模块结束时无法确定您的密钥类型,您将收到与您看到的相同的“无法概括”错误。

$ cat gk.ml
let map : (_, int) Hashtbl.t = Hashtbl.create 100
$ ocamlc -c gk.ml
File "gk.ml", line 1, characters 31-49:
Error: The type of this expression, ('_a, int) Hashtbl.t,
       contains type variables that cannot be generalized
Run Code Online (Sandbox Code Playgroud)

更新 2

您还可以声明一个接受不同类型值的函数。函数可以是多态的,但特定的容器不能。

# let f (x: (_, int) Hashtbl.t) y = Hashtbl.find x y;;
val f : ('a, int) Hashtbl.t -> 'a -> int = <fun>
Run Code Online (Sandbox Code Playgroud)

此函数可以接受具有不同键类型的哈希表。但是没有一个哈希表可以有多个键类型。