Search code examples
smlfunctor

Functors with multiple inputs in Standard ML


High level question: How do I use functors with multiple arguments in SML?

I've looked at this, this, this and this(PDF). All of them seem to conflict in terms of structure or functor definition syntax, and none of them show anything other than a unary functor.

Specifics: I'm trying to write a web server in Standard ML (you can see the effort here), and have decided to partition it into BUFFER, PARSER and TCPSERVER chunks. The BUFFER and PARSER are both just straightforward structures. The idea with the TCPSERVER is that it handles listening/accepting logic, but allows the user to specify an appropriate buffering/parsing strategy by passing the other two in. What I've got is something like

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

The above seems to be accepted by 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

... but rejected by 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.
...

Additionally, I'm not entirely sure how to use the definition once it's evaluated. Even in smlnj, the obvious fails:

- 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
- 

Can anyone tell me what I'm doing wrong? Or even point me to a good piece of documentation?


Solution

  • Your Server functor does multiple arguments via currying. That does not work in plain SML, because it does not have higher-order functors (which SML/NJ supports as a non-standard extension). You need to use uncurried form, by introducing an auxiliary structure, like you would use a tuple or record in the core language:

    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)
    

    Obviously, this is pretty clumsy and verbose, so at least SML has some syntactic sugar for the above, allowing you to keep the auxiliary structure implicit:

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

    But that is as short as it gets in current SML.