packages feed

alms-0.6.6: lib/libsocketcap3.alms

(*
    A typestate sockets library

    This is a bit more involved than the example in the paper,
    because we have error cases.  We deal with this by raising
    an exception which contains a witness that allows recovering
    the capability if presented with the corresponding socket.
*)

#load "libsocket"

module type SOCKET_CAP = sig
  (* The representation of a socket *)
  type 'a socket

  (* Socket capabilities and the socket states *)
  type 'a @ 'c : A
  type raw
  type named
  type listening
  type ready

  (* Socket operations *)
  val socket  : unit → ∃ 'a. 'a socket × 'a@raw
  val bind    : 'a socket → int → 'a@raw → 'a@named

  val connect : 'a socket → string → string →
                  'a@raw + 'a@named → 'a@ready
  val listen  : 'a socket → 'a@named → 'a@listening
  val accept  : 'a socket → 'a@listening →
                  (∃ 'b. 'b socket × 'b@ready) × 'a@listening
  val send    : 'a socket → string → 'a@ready → 'a@ready
  val recv    : 'a socket → int → 'a@ready → string × 'a@ready
  val close   :  'a socket → 'a@'c → unit

  val isSame  : 'a socket → 'b socket → ('a@'c → 'b@'c) option

  (* Operations for catching the error state associated with a given
     socket. *)
  val catchRaw       : (unit -A> `r) → 'a socket × ('a@raw -A> `r)       → `r
  val catchNamed     : (unit -A> `r) → 'a socket × ('a@named -A> `r)     → `r
  val catchListening : (unit -A> `r) → 'a socket × ('a@listening -A> `r) → `r
  val catchReady     : (unit -A> `r) → 'a socket × ('a@ready -A> `r)     → `r

  type 'a dynamicCap = Raw of 'a@raw
                     | Named of 'a@named
                     | Listening of 'a@listening
                     | Ready of 'a@ready

  exception Socket of (∃'a. 'a socket × 'a dynamicCap) option × string
end

module SocketCap : SOCKET_CAP = struct
  module S = Socket

  type 'a socket  = S.socket
  type 'a @ 'c = unit
  type raw
  type named
  type listening
  type ready

  type 'a dynamicCap = Raw of 'a@raw
                     | Named of 'a@named
                     | Listening of 'a@listening
                     | Ready of 'a@ready

  exception Socket of (∃'a. 'a socket × 'a dynamicCap) option × string

  let lift thunk sock mkcap =
    try thunk ()
    with IOError msg → raise (Socket (Some (sock, mkcap ()), msg))

  let socket _ : ∃ 'a. 'a socket × 'a@raw =
    try (S.socket (), ())
    with IOError msg → raise (Socket (None, msg))

  let bind sock port _ = lift (λ_ → S.bind sock port) sock Raw

  let connect sock host port cap =
    lift (λ_ → S.connect sock host port) sock
         (match cap with Left _ → Raw | Right _ → Named)

  let listen sock _ = lift (λ_ → S.listen sock) sock Named

  let accept sock _ =
    lift (λ_ → ((S.accept sock, ()) : ∃ 'a. 'a socket × 'a@ready, ()))
          sock Listening

  let send sock data _ = lift (λ_ → S.send sock data; ()) sock Ready

  let recv sock len _ = lift (λ_ → (S.recv sock len, ())) sock Ready

  let close sock _ =
    try S.close sock
    with IOError msg → raise (Socket (None, msg))

  let isSame sock sock' =
    if sock == sock'
      then Some (λ_ → ())
      else None

  let catchBy body state (sock', handler) =
    try body () with
    Socket ((Some (sock, dyncap), msg) as se) →
      if dyncap == state () && sock == sock'
        then handler ()
        else raise (Socket se)

  let catchRaw body       = catchBy body Raw
  let catchNamed body     = catchBy body Named
  let catchListening body = catchBy body Listening
  let catchReady body     = catchBy body Ready
end

module SocketCap2 : SOCKET_CAP = struct
  open SocketCap
  let catchBy body
              (prj : ∀'a. 'a dynamicCap →
                          'a dynamicCap + ('a@'c -A> 'a dynamicCap) × 'a@'c)
              (sock', handler) =
    try body () with
    | Socket (Some (sock, dyncap), msg) →
        match prj dyncap with
        | Left dyncap
          → raise (Socket (Some (sock, dyncap), msg))
        | Right (uncap, cap)
          → match isSame sock sock' with
            | None → raise (Socket (Some (sock, uncap cap), msg))
            | Some witness → handler (witness cap)

  let catchRaw body =
    catchBy body (function Raw cap → Right (Raw, cap)
                         | dyncap → Left dyncap)
  let catchNamed body =
    catchBy body (function Named cap → Right (Named, cap)
                         | dyncap → Left dyncap)
  let catchListening body =
    catchBy body (function Listening cap → Right (Listening, cap)
                         | dyncap → Left dyncap)
  let catchReady body =
    catchBy body (function Ready cap → Right (Ready, cap)
                         | dyncap → Left dyncap)
end