[cwn] Attn: Development Editor, Latest OCaml Weekly News

Alan Schmitt alan.schmitt at polytechnique.org
Tue Jun 28 00:37:16 PDT 2022


Hello

Here is the latest OCaml Weekly News, for the week of June 21 to 28,
2022.

The mailing list mode of discuss.ocaml.org seems to have been down for a
few days, so I had to manually scrape the messages. My apologies if I
missed any.

Table of Contents
─────────────────

An amusing use of first-class modules: reading from plaintext and compressed files
Lwt.5.6.0 (and other Lwt packages)
Old CWN


An amusing use of first-class modules: reading from plaintext and compressed files
══════════════════════════════════════════════════════════════════════════════════

  Archive:
  <https://discuss.ocaml.org/t/an-amusing-use-of-first-class-modules-reading-from-plaintext-and-compressed-files/10073>


Chet_Murthy explained
─────────────────────

  I was recently trying to write a thing in Rust, and having problems,
  so I wrote the same thing in OCaml, just to make sure that it was
  doable. I thought I’d post about it, b/c maybe it’s an example of what
  we’ll find more tractable, once we have modular implicits.

  The problem: I have both compressed and plaintext files, and I want to
  run a function over the uncompressed contents. I’d like a combinator
  that I can apply to the filename and the function, that will do the
  work of opening the file, calling the function, closing the file, etc.

  This isn’t so hard.

  1. define a type of READER (and two instances for plaintext and
     gzipped). This is the equivalent of Rust’s “io::BufRead”.

     ┌────
     │ module type READER =
     │   sig
     │     type in_channel
     │     val open_in : string -> in_channel
     │     val input_char : in_channel -> char
     │     val close_in : in_channel -> unit
     │   end
     │ let stdreader = (module Stdlib : READER) ;;
     │ let gzreader = (module Gzip : READER) ;;
     └────

  2. then define a type of “in channel user” (“ICUSER”) and the generic
     version of it

     ┌────
     │ module type ICUSER = sig
     │   type in_channel
     │   val use_ic : in_channel -> unit
     │ end
     │ module type GENERIC_ICUSER = functor (R : READER) -> (ICUSER with type in_channel = R.in_channel)
     └────

  3. then define our function that takes a generic in_channel, and uses
     it – “Cat”

     ┌────
     │ module Cat(R : READER) : ICUSER with type in_channel = R.in_channel = struct
     │   type in_channel = R.in_channel
     │   let use_ic ic =
     │   let rec rerec () =
     │     match R.input_char ic with
     │       c -> print_char c ; rerec ()
     │     | exception End_of_file -> ()
     │   in rerec ()
     │ end
     └────

  4. And then write our “with_input_file” function, that takes a
     filename, the function from #3, and applies it to either a normal
     in_channel, or one produced from a gzip-reader.

     ┌────
     │ let with_input_file fname (module R : GENERIC_ICUSER) =
     │   let (module M : READER) =
     │     if Fpath.(fname |> v |> has_ext "gz") then
     │       gzreader
     │     else stdreader in
     │   let open M in
     │   let ic = M.open_in fname in
     │   let module C = R(M) in
     │   try let rv = C.use_ic ic in close_in ic ; rv
     │   with e -> close_in ic ; raise e
     └────

  And now we can use it:

  ┌────
  │ with_input_file "/etc/passwd" (module Cat) ;;
  │ with_input_file "foo.gz" (module Cat) ;;
  └────

  Easy-peasy. I don’t remember enough about the modular implicits
  proposal to remember if this can be cast in the supported language
  there, so I suppose I should get some version of that code (or the
  newer versions from others) up-and-running, and see if this can be
  made to work.


hyphenrf asked and Chet_Murthy replied
──────────────────────────────────────

        can’t we get rid of the `GENERIC_ICUSER' requirement and
        just ask for functions that take a packed module of type
        `READER'

        by that I mean the signature of `with_input_file' becomes
        `string -> ((module READER) -> 'a) -> 'a'

  It’s a good question, and as a newbie user of first-class modules, I
  don’t know the typing rules well enough to answer. But I did try:

  ┌────
  │ let with_input_file' fname f =
  │   let (module M : READER) =
  │     if Fpath.(fname |> v |> has_ext "gz") then
  │       gzreader
  │     else stdreader in
  │   let open M in
  │   let ic = M.open_in fname in
  │   f (module M : READER) ic
  └────

  and got

  ┌────
  │ File "ioabs.ml", line 96, characters 24-26:
  │ 96 |   f (module M : READER) ic
  │ 			     ^^
  │ Error: This expression has type M.in_channel
  │        but an expression was expected of type 'a
  │        The type constructor M.in_channel would escape its scope
  └────

  ETA: I remember in the modular implicits paper, that there was a lot
  of wrappering code in structs (that didn’t start off in structs). I
  wonder if that’s evidence that you really do have to “push up” code to
  the module level in order to make it work.


octachron then said
───────────────────

  You don’t need modular implicits to simplify your code. Your packed
  module type is equivalent to:

  ┌────
  │ type channel = { input_char: unit -> char; close_in: unit -> unit }
  │ type channel_generator = string ->  channel
  └────

  We could go fancy and manifest the type with an existential

  ┌────
  │ type 'a channel =
  │   { open_fn: string -> 'a; input_char: 'a -> char; close_in: 'a -> unit }
  │ type chan = Any: 'a channel -> chan
  └────

  but this has mainly the advantage to illustrate the fact that you are
  never using the non-existentially qualified `'a channel' which means
  that in the current version of your code, modular (explicits or)
  implicits is not a good fit: we are not selecting a module to provide
  functions for a type, we have an object (aka an existentially
  qualified record) with some hidden inner type that we never need to
  know.


c-cube later said
─────────────────

  I think it’s kind of counter-productive to want a `in_channel' type at
  all. This is what I’ve been doing, more and more:

  ┌────
  │ module type INPUT = sig
  │   val read_char : unit -> char
  │   val read : bytes -> int -> int -> int
  │   val close : unit -> unit
  │ end
  │ 
  │ type input = (module INPUT)
  │ 
  │ let open_file (filename:string) : input =
  │   let ic = open_in filename in
  │   (module struct
  │     let read_char() = input_char ic
  │     let read = input ic
  │     let close() = close_in ic
  │  end)
  │ 
  │ 
  │ let do_sth (module IN:INPUT) =
  │   IC.read_char ();
  │   IC.read …
  └────

  This behaves like classic objects in other languages and there’s no
  complicated typing going on (what with each implementation having its
  own channel type).


Lwt.5.6.0 (and other Lwt packages)
══════════════════════════════════

  Archive:
  <https://discuss.ocaml.org/t/ann-lwt-5-6-0-and-other-lwt-packages/10077>


raphael-proust announced
────────────────────────

  It is a real pleasure to announce the release of Lwt version 5.6.0 as
  well as Lwt-domain.0.2.0, Lwt-ppx.2.1.0 and Lwt-react.1.2.0. With this
  release Lwt is now compatible with OCaml version 5.

  <https://github.com/ocsigen/lwt/releases/tag/5.6.0>

  Thank you to the many contributors for the fixes, the improvements,
  and the OCaml5 compatibility! Check out the changelog for full details
  on each contribution.


Old CWN
═══════

  If you happen to miss a CWN, you can [send me a message] and I'll mail
  it to you, or go take a look at [the archive] or the [RSS feed of the
  archives].

  If you also wish to receive it every week by mail, you may subscribe
  [online].

  [Alan Schmitt]


[send me a message] <mailto:alan.schmitt at polytechnique.org>

[the archive] <https://alan.petitepomme.net/cwn/>

[RSS feed of the archives] <https://alan.petitepomme.net/cwn/cwn.rss>

[online] <http://lists.idyll.org/listinfo/caml-news-weekly/>

[Alan Schmitt] <https://alan.petitepomme.net/>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.idyll.org/pipermail/caml-news-weekly/attachments/20220628/eaa56383/attachment-0001.html>


More information about the caml-news-weekly mailing list