动机

对于我的一生,我无法弄清楚如何在其中使用高阶仿函数
SML/NJ适用于任何实际目的。

根据
SML/NJ docs on the implementation's special features
通过使用可以指定一个仿函数作为另一个仿函数的参数funsig关键字。因此,给定签名

signature SIG = sig ... end

我们应该能够指定一个仿函数,该仿函数将产生一个满足以下条件的模块SIG,当应用于满足某些签名SIG'的结构时。例如。,
funsig Fn (S:SIG') = SIG

以这种方式声明的Fn之后,我们应该(能够定义另一个
将此仿函数作为参数的仿函数。也就是说,我们可以定义一个模块
在另一个参数化模块上进行了参数化,并且大概使用了
后者在前者之内;因此:
functor Fn' (functor Fn:SIG) =
struct
...
structure S' = Fn (S:SIG')
...
end

从理论上讲,这一切看起来都不错,但是我不知道该如何真正利用
这种模式。

示例问题

这是我尝试使用此模式的两个实例,只是为了找到
这是行不通的:

第一次尝试

对于我的第一次尝试,只是在玩耍,我试图制造一个仿函数,
拿一个仿函数实现一个有序集合,并产生一个用于处理的模块
带整数集(不是很有用,但是它可以让您参数化集合
在不同的set实现上具有给定类型)。我可以定义
以下结构,它们将进行编译(使用新泽西州的标准ML
v110.7):
structure IntOrdKey : ORD_KEY
= struct
    type ord_key = int
    val compare = Int.compare
end

funsig SET_FN (KEY:ORD_KEY) = ORD_SET

functor IntSetFn (functor SetFn:SET_FN) =
struct
    structure Set = SetFn (IntOrdKey)
end

但是当我实际尝试将IntSetFn应用于应该满足以下条件的仿函数时SET_FN funsig,它只是不解析:
- structure IntSet = IntSetFn (functor ListSetFn);
= ;
= ;;
stdIn:18.1-24.2 Error: syntax error: deleting  RPAREN SEMICOLON SEMICOLON
- structure IntSet = IntSetFn (functor BinarySetFn) ;
= ;
= ;
stdIn:19.1-26.2 Error: syntax error: deleting  RPAREN SEMICOLON SEMICOLON

第二次尝试

我的第二次尝试以两种方式失败。

我定义了实现多态和
单态堆栈(the source file,出于好奇)。至
实现单态堆栈,您可以
- structure IntStack = Collect.Stack.Mono (type elem = int);
structure IntStack : MONO_STACK?
- IntStack.push(1, IntStack.empty);
val it = - : IntStack.t

等等。到目前为止,它似乎工作正常。现在,我想定义一个模块
参数化此仿函数。因此,我为Collect.Stack.Mono仿函数(可以在我的仓库中看到)。然后,按照
上面指出的模式,我试图定义以下测试模块:
(* load my little utility library *)
CM.autoload("../../../utils/sources.cm");

functor T (functor StackFn:MONO_STACK) =
struct
    structure S = StackFn (type elem = int)
    val x = S.push (1, S.empty)
end

但这不会编译!我收到类型错误:
Error: operator and operand don't agree [overload conflict]
  operator domain: S.elem * S.t
  operand:         [int ty] * S.t
  in expression:
    S.push (1,S.empty)

uncaught exception Error
  raised at: ../compiler/TopLevel/interact/evalloop.sml:66.19-66.27
             ../compiler/TopLevel/interact/evalloop.sml:44.55
             ../compiler/TopLevel/interact/evalloop.sml:292.17-292.20

但是,在functor T内部,我似乎正在使用完全相同的实例化
最适合在顶层使用的模式。我想念什么?

不幸的是,这还没有结束我的不幸。现在,我删除线
导致类型错误,离开,
functor T (functor StackFn:MONO_STACK) =
struct
    structure S = StackFn (type elem = int)
end

这样编译就可以了:
[scanning ../../../utils/sources.cm]
val it = true : bool
[autoloading]
[autoloading done]
functor T(<param>: sig functor StackFn : <fctsig> end) :
         sig
           structure S : <sig>
         end
val it = () : unit

但是我实际上不能实例化该模块!显然,路径访问语法
不支持高阶仿函数?
- structure Test = T (functor Collect.Stack.Mono);
stdIn:43.36-43.43 Error: syntax error: deleting  DOT ID DOT

我不知所措。

问题

我有三个相关的问题:
  • 我在SML/NJ中有高阶仿函数的基本原理吗
    缺失,还是仅仅是不完全,笨拙地实现了
    语?
  • 如果是后者,我在哪里可以寻求更优雅,更实用的更高要求
    仿函数? (希望是SML,但如有必要,我将介绍OCaml。)
  • 我是否应该采取其他方法来实现这些目标
    一起避免高阶仿函数的效应?

  • 在此先感谢您提供任何答案,提示或后续问题!

    最佳答案

    关于您的第一次尝试,应用IntSetFn仿函数的正确语法是:

    structure IntSet = IntSetFn (functor SetFn = ListSetFn)
    

    在第二次尝试中,您的Test仿函数的应用程序也是如此:
    structure Test = T (functor StackFn = Collect.Stack.Mono)
    

    那应该修复语法错误。

    尝试在functor S中使用堆栈结构T时遇到的类型错误与您定义MONO_STACK funsig的方式有关:
    funsig MONO_STACK (E:ELEM) = MONO_STACK
    

    这只是说它返回一个带有完全抽象的MONO_STACK类型的elem结构。它并不表示其elem类型将与E.elem相同。据此,我将能够传递像
    functor F (E : ELEM) = struct type elem = unit ... end
    

    到您的仿函数T。因此,在T内部,不允许类型系统假定S.elem = int类型,因此会出现类型错误。

    要解决此问题,您需要按以下方式优化MONO_STACK funsig:
    funsig MONO_STACK (E:ELEM) = MONO_STACK where type elem = E.elem
    

    那应该消除类型错误。

    [编辑]

    至于您的问题:
  • 在SML/NJ中,高阶仿函数在语法上有点尴尬,因为它试图与纯SML保持100%兼容,后者将仿函数的 namespace 与结构的 namespace 分开。如果不是这种情况,那么也就不需要funsigs作为单独的命名空间(以及其他语法巴洛克式),并且签名的语言可以简单地扩展为包括仿函数类型。
  • Moscow ML是另一种具有更高阶模块扩展的SML方言,可以更优雅地解决兼容性问题(并且更具表现力)。还有(现在几乎已经死了)ALice ML,这是另一种具有高阶仿函数的SML方言,其简单地删除了尴尬的 namespace 分隔。当然,OCaml首先没有这个约束,因此它的高阶模块在语法上也更加规则。
  • 该方法似乎很好。
  • 10-08 12:36