[MLton-user] Sockets+Monads in SML?

Vesa Karvonen vesa.a.j.k at gmail.com
Thu Feb 14 16:33:52 PST 2008


On Thu, Feb 14, 2008 at 9:14 PM, Wesley W. Terpstra <wesley at terpstra.ca> wrote:
[...]
>  Haskell has the concept of Monads, which comes quite close to what I
>  need:
[...]
>  The method 'read' would in reality return from the method, but install
>  the 'ret' function to be called once the data was ready.  The problem
>  here is that 'fn' can't be used without ()s.  Is there any way to
>  introduce variables without excessive bracketing?

Note that you only need a fn -expression when you want to bind a
value.  In particular, using monads, you can define the usual
sequencing operator

fun aM >> bM = aM >>= const bM

and your example would become (with all the required parentheses and
the last redundant bind removed):

  fun f x =
      myprint "hello\n" >>
      read >>= (fn x =>
      read >>= (fn y =>
      myprint (x ^ y ^ "\n")))

>  It would also be very helpful if pattern matching were available, eg:
>
>  read (socket, Time.fromSeconds 10) >>=
>  fn TIMEOUT => ...
>   | DATA x => ...

As you know, you only need to add the parentheses to make the above
pattern valid SML.

>  The problem I'm running up against is that only 'let' clauses and
>  function declarations can introduce variable scope.  AFAICT, the let
>  clause prevents me from popping out of the method after installing a
>  signal handler.

Do you have a specific reason to avoid threads?  With threads you
could avoid using monads and just write the program in direct style.
Your example would become:

  fun f x s = let
     val () = print "hello\n"
     val x = read s
     val y = read s
  in
     print (x ^ y ^ "\n")
  end

>  The function approach suffers due to the syntax of SML.

Yes, I agree that the required parenthesis are a bit of a nuisance.  I
would personally consider changing the syntax of SML to allow fn
-expressions without parentheses in these contexts like in Haskell.
The monad based example would become:

  fun f x =
      myprint "hello\n" >>
      read >>= fn x =>
      read >>= fn y =>
      myprint (x ^ y ^ "\n")

But, if you put this side-by-side with the version with parentheses,
the different isn't really that large.  If you avoid long functions,
which is a good idea anyway, the extra parentheses aren't much more
than a bit of a nuisance, IMO.

I find the let -syntax fine for functional programming, but somewhat
verbose for imperative programming (note the "val () =" in the earlier
example).  I would personally ditch the let -syntax and change the
syntax of SML's sequence expressions to allow bindings like at the top
level.  This would make imperative programming more convenient.  The
thread based version would become:

  fun f x s =
      (print "hello\n"
     ; val x = read s
     ; val y = read s
     ; print (x ^ y ^ "\n"))

(One of the semicolons above is redundant.)  In addition, it would
also make it much more convenient to use modules locally.  Consider

  (open DSEL ; ...)

vs

  let open DSEL in ... end

>  Does someone have a tidy way of writing code like this?

If you can't stand the parentheses, I would recommend considering threads.

With some monads, like parsers, something like the "pick" notation
described on the http://mlton.org/StaticSum page might feel more
comfortable.

>  Also, I am concerned by MLton's closure conversion.  With this style of
>  programming, there are a *lot* of bound variables which pile up towards
>  the end of the event chain.  As I understand it, the closure of MLton
>  is converted into a datatype which represents the function to call +
>  it's free variables.  The longer these chains of event handlers get,
>  the more free variables in the later functions.  My concern is that it
>  will cost me a lot to convert these datatypes.  I am under the
>  impression MLton will flatten them out into one record at each step
>  rather -> O(n^2) cost for n steps of function nesting (if nearly all n
>  variables are used in the last step, which is quite typical).  Should I
>  be worried?

I don't know enough about the specifics of MLton's closure conversion,
but I would personally be more worried if MLton wouldn't be doing
something like that to avoid space leaks.

-Vesa Karvonen



More information about the MLton-user mailing list