如何从标准 ML 中的仿函数参数重新导出数据类型

How to re-export datatype from functor argument in Standard ML

在标准 ML 中是否可以重新导出数据类型的构造函数,该数据类型是作为函子参数接收的结构的一部分。一些代码可能会使这更容易理解:

signature FLAG =
  sig
    type t
  end

signature MEMBER =
  sig
    structure Flag : FLAG
  end

functor Member(F : FLAG) : MEMBER =
  struct
    structure Flag = F
  end

structure M =
  Member(struct
    datatype t =
      FLAG_1
    | FLAG_2
  end)

val flag1 = M.Flag.FLAG_1;
(* Error: unbound variable or constructor: FLAG_1 in path M.Flag.FLAG_1 *)

上面的例子可能没有任何实际意义,但这只是我在我的一个项目中遇到的问题的简化版本。

如果我理解正确的话,FLAG 签名中未详细说明的类型规范意味着 t 是不透明的,因此实现 [=14= 的结构之外的任何东西都无法访问].

一般来说,在 SML 中,如果签名指定模块的接口,那么该模块中唯一可以从外部访问的部分是签名中明确描述的部分。正如您可能知道的那样,如果您为某个模块指定了一个接口,那么只有您在签名中明确声明的那些函数和值才会被提供给使用;所有被省略的都被密封在模块内。同样的原则在这里与 type t 无法解释的规范一起工作:由于签名没有说明这种类型是如何构成的,所以没有关于它的信息可用。

因此,您可以轻松地从作为参数给定的模块中将值构造函数重新导出到仿函数,前提是您已将这些构造函数包含在该模块接口的规范中。例如,

signature FLAG =
sig
    datatype t = FLAG_1 | FLAG_2
end

signature MEMBER =
sig
    structure Flag : FLAG
end

functor Member(F : FLAG) : MEMBER =
struct
    structure Flag = F
end

structure M =
Member(struct
        datatype t =
                 FLAG_1
               | FLAG_2
        end)

然后

- val a = M.Flag.FLAG_1;
val a = FLAG_1 : ?.t

这里需要注意的最重要的一点可能是:实现 FLAG 的模块中值构造函数的不可访问性与指定接口的方式有关,与事实上它在这里作为 functor Member 的参数出现。当您在以下程序中使用仿函数时,我们会得到与您观察到的相同的行为:

signature FLAG =
sig
    type t
end

structure F : FLAG =
struct
    datatype t =
             FLAG_1
           | FLAG_2
end

然后

[opening ~/Programming/sml/scratch/scratch.sml]
signature FLAG = sig type t end
structure F : FLAG
val it = () : unit
- F.FLAG_1;
stdIn:63.1-63.9 Error: unbound variable or constructor: FLAG_1 in path F.FLAG_1