标准ML中有多个输入的函数

时间:2015-06-13 06:07:56

标签: sml functor

高级别问题:如何在SML中使用带有多个参数的functor

我查看了thisthisthisthis (PDF)。所有这些内容在structurefunctor定义语法方面似乎都存在冲突,除了一元functor之外,它们都没有显示任何内容。

细节:我正在尝试在标准ML中编写Web服务器(您可以看到工作here),并决定将其分区为BUFFERPARSERTCPSERVER块。BUFFERPARSER块。 structureTCPSERVER都是直截了当的signature TCPSERVER = sig type SockAction type Request val serve : int -> (Request -> (INetSock.inet,Socket.active Socket.stream) Socket.sock -> SockAction) -> 'u end functor Server (Buf : BUFFER) (Par : PARSER) : TCPSERVER = struct type Request = Par.Request datatype SockAction = CLOSE | LEAVE_OPEN local ... [eliding more definitions, including calls to Par.* and Buf.* functions] ... fun serve port serverFn = let val s = INetSock.TCP.socket() in Socket.Ctl.setREUSEADDR (s, true); Socket.bind(s, INetSock.any port); Socket.listen(s, 5); print "Entering accept loop...\n"; acceptLoop s [] serverFn end end end 。使用smlnj的想法是它处理侦听/接受逻辑,但允许用户通过传递其他两个来指定适当的缓冲/解析策略。我得到的是类似

- use "server.sml" ;
[opening server.sml]
type Response =
  {body:string, headers:(string * string) list, httpVersion:string,
   responseType:string}
val fst = fn : 'a * 'b -> 'a
val snd = fn : 'a * 'b -> 'b
val a_ = fn : 'a * 'b * 'c -> 'a
val b_ = fn : 'a * 'b * 'c -> 'b
val c_ = fn : 'a * 'b * 'c -> 'c
val curry = fn : ('a * 'b -> 'c) -> 'a -> 'b -> 'c
signature TCPSERVER =
  sig
    type SockAction
    type Request
    val serve : int
                -> (Request
                    -> (INetSock.inet,Socket.active Socket.stream) Socket.sock
                       -> SockAction)
                   -> 'a
  end
functor HTTPServer(Buf: sig
                          type Buffer
                          val readInto : Buffer
                                         -> ('a,Socket.active Socket.stream) 
                                              Socket.sock
                                            -> BufferStatus
                          val new : int -> Buffer
                          val toSlice : Buffer -> Word8ArraySlice.slice
                          val printBuffer : Buffer -> unit
                        end) :
                  sig functor <functor> : <fctsig> end
val it = () : unit

以上似乎被mlton ...

接受
~/projects/serve-sml $ mlton server.mlb
Error: server.sml 23.1. # (line with "functor Server...")
  Syntax error: replacing  FUNCTOR with  FUN.
Error: server.sml 24.1.
  Syntax error: replacing  STRUCT with  ASTERISK.
Error: server.sml 87.1.
  Syntax error found at END.
Error: server.sml 88.0.
  Parse error.
...

...但被smlnj拒绝。

- HTTPServer(DefaultBuffer, DefaultParser) ;
stdIn:1.2-1.12 Error: unbound variable or constructor: HTTPServer
stdIn:2.7-3.1 Error: unbound variable or constructor: DefaultParser
stdIn:1.13-2.5 Error: unbound variable or constructor: DefaultBuffer
- 

此外,我不完全确定在评估后如何使用该定义。即使在.setHidden()中,显然也会失败:

Glance

谁能告诉我我做错了什么?或者甚至指出一个很好的文档?

3 个答案:

答案 0 :(得分:4)

您的Server仿函数通过 currying 执行多个参数。这在普通的SML中不起作用,因为它没有高阶函子(SML / NJ支持非标准扩展)。您需要使用未经处理的表单,通过引入辅助结构,就像您将使用核心语言中的元组或记录一样:

functor Server(X : sig structure Buffer : BUFFER; structure Parser : PARSER end) =
  ...X.Buffer...X.Parser...

structure MyServer =
  Server(struct structure Buffer = MyBuffer; structure Parser = MyParser end)

显然,这是非常笨拙和冗长的,所以至少SML有一些语法糖,允许你隐藏辅助结构:

functor Server(structure Buffer : BUFFER; structure Parser : PARSER) =
  ...Buffer...Parser...

structure MyServer =
  Server(structure Buffer = MyBuffer; structure Parser = MyParser)

但这与目前的SML一样短。

答案 1 :(得分:2)

理解标准ML由两种语言组成是有用的 - 值的核心语言(普通函数,数字,布尔值,它们的类型等)和模块的语言,包括签名,结构和仿函数。 / p>

Functors与核心函数类似,它们总是接受单个参数并返回模块级值。仿函数的参数类型由签名指定,而参数的实际值在“调用”仿函数时将是实现该签名的结构。仿函数返回一个结构,其类型再次由签名确定。这是基本的骨架:

signature ARG = sig end
signature RESULT = sig end
functor FUNCTOR(A : ARG) : RESULT

现在,正如安德烈亚斯·罗斯伯格(Andreas Rossberg)所提到的那样,该标准为表达仿函数的参数类型提供了一些语法糖。但是,当编译器需要多个结构作为输入时,我倾向于支持上述骨架:

signature SERVER_ARGS =
sig
  structure ARG_0 = sig end
  structure ARG_1 = sig end
  structure ARG_2 = sig end
  structure ARG_3 = sig end
end

signature SERVER = sig end

functor ServerFn(ARGS : SERVER_ARGS) : SERVER =
struct
end

现在,在调用仿函数时,语法有几种选择:

(* Using an anonymous structure *)
ServerFn(struct
  structure ARG_0 = struct end
  structure ARG_1 = struct end
  structure ARG_2 = struct end
  structure ARG_3 = struct end
end)

(* Using a named structure *)
structure ServerArgs =
struct
  structure ARG_0 = struct end
  structure ARG_1 = struct end
  structure ARG_2 = struct end
  structure ARG_3 = struct end
end

ServerFn(ServerArgs)

(* Using an anonynous structure, with syntactic sugar *)
ServerFn(
  structure ARG_0 = struct end
  structure ARG_1 = struct end
  structure ARG_2 = struct end
  structure ARG_3 = struct end
)

作为结构的仿函数结果可能只能在源代码中的结构位置找到,即,您使用structure关键字为其命名,或者将其作为参数传递给某些其他仿函数:

structure Server = ServerFn(ServerArgs)
structure Quux = OtherFunctor(ServerFn(ServerArgs))

structure关键字是核心语言中val关键字的模块级等价物。一种在模块级别绑定“变量”的方法。同样,signature关键字是核心语言中type关键字的模块级等效项 - 为sig ... end表示的匿名签名引入别名的有用方法。

这就是你的最后一个例子失败的原因,因为SML顶层尝试将HTTPServer(DefaultBuffer, DefaultParser);解释为核心级函数调用,而不是模块级函数/函数调用。

答案 2 :(得分:0)

我认为多参数仿函数的StandardML语法是:

signature PARSER = sig
  val parse : unit -> unit
end

signature BUFFER = sig
  val read : unit -> unit
end

functor Server (structure buffer : BUFFER
            structure parser : PARSER) = struct
end

我想问题是SML-NJ支持高阶函子,而MLton则不支持。