高级别问题:如何在SML中使用带有多个参数的functor
?
我查看了this,this,this和this (PDF)。所有这些内容在structure
或functor
定义语法方面似乎都存在冲突,除了一元functor
之外,它们都没有显示任何内容。
细节:我正在尝试在标准ML中编写Web服务器(您可以看到工作here),并决定将其分区为BUFFER
, PARSER
和TCPSERVER
块。BUFFER
和PARSER
块。 structure
和TCPSERVER
都是直截了当的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
谁能告诉我我做错了什么?或者甚至指出一个很好的文档?
答案 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则不支持。