标准 ML 中具有多个输入的函子
Functors with multiple inputs in Standard ML
高级问题:如何在 SML 中使用带有多个参数的 functor
?
我看过 this, this, this and this(PDF)。它们似乎都在 structure
或 functor
定义语法方面发生冲突,并且 none 显示除一元 functor
.
之外的任何内容
细节:我正在尝试用标准 ML 编写一个 Web 服务器(你可以看到努力 here),并决定将其划分为 BUFFER
、PARSER
和 TCPSERVER
块。 BUFFER
和 PARSER
都是直截了当的 structure
。 TCPSERVER
的想法是它处理 listening/accepting 逻辑,但允许用户通过传递其他两个来指定适当的 buffering/parsing 策略。我得到的是 [= =30=]
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
-
谁能告诉我我做错了什么?或者给我指点一份好的文档?
我认为多参数仿函数的 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 不支持。
您的 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 中已经很短了。
理解标准 ML 由两种语言组成很有用——值的核心语言(普通函数、数字、布尔值及其类型等)和模块语言,由签名、结构和仿函数组成.
仿函数类似于核心函数,它们总是接受一个参数和return一个模块级值。仿函数的参数类型由签名指定,而当 "calling" 仿函数时,参数的实际值将是实现该签名的结构。仿函数 return 是一个结构,其类型再次由签名确定。这是基本骨架:
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
关键字的模块级等价物。一种在模块级别绑定 "variables" 的方法。同样,signature
关键字是核心语言中 type
关键字的模块级等价物——一种为 sig ... end
表示的匿名签名引入别名的有用方法。
这就是您最后一个示例失败的原因,因为 SML 顶层试图将 HTTPServer(DefaultBuffer, DefaultParser);
解释为核心级函数调用,而不是模块级 function/functor 调用。
高级问题:如何在 SML 中使用带有多个参数的 functor
?
我看过 this, this, this and this(PDF)。它们似乎都在 structure
或 functor
定义语法方面发生冲突,并且 none 显示除一元 functor
.
细节:我正在尝试用标准 ML 编写一个 Web 服务器(你可以看到努力 here),并决定将其划分为 BUFFER
、PARSER
和 TCPSERVER
块。 BUFFER
和 PARSER
都是直截了当的 structure
。 TCPSERVER
的想法是它处理 listening/accepting 逻辑,但允许用户通过传递其他两个来指定适当的 buffering/parsing 策略。我得到的是 [= =30=]
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
-
谁能告诉我我做错了什么?或者给我指点一份好的文档?
我认为多参数仿函数的 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 不支持。
您的 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 中已经很短了。
理解标准 ML 由两种语言组成很有用——值的核心语言(普通函数、数字、布尔值及其类型等)和模块语言,由签名、结构和仿函数组成.
仿函数类似于核心函数,它们总是接受一个参数和return一个模块级值。仿函数的参数类型由签名指定,而当 "calling" 仿函数时,参数的实际值将是实现该签名的结构。仿函数 return 是一个结构,其类型再次由签名确定。这是基本骨架:
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
关键字的模块级等价物。一种在模块级别绑定 "variables" 的方法。同样,signature
关键字是核心语言中 type
关键字的模块级等价物——一种为 sig ... end
表示的匿名签名引入别名的有用方法。
这就是您最后一个示例失败的原因,因为 SML 顶层试图将 HTTPServer(DefaultBuffer, DefaultParser);
解释为核心级函数调用,而不是模块级 function/functor 调用。