Previous week Up Next week

Hello

Here is the latest OCaml Weekly News, for the week of December 30, 2014 to January 06, 2015.

  1. Universe Library for OCaml
  2. Anecdote about OPAM pinning
  3. ocamldep & compilation units
  4. Problem with GADTs and escaping types
  5. Other OCaml News

Universe Library for OCaml

Archive: https://sympa.inria.fr/sympa/arc/caml-list/2015-01/msg00000.html

Kenichi Asai announced:
I am happy to announce that the Universe Library for OCaml is now
public at:

http://pllab.is.ocha.ac.jp/~asai/Universe/

It is ported from Racket universe teachpack and enables us to write
interactive games easily.  I used it in my class this fall, where
students enjoyed creating many fun communicating games.  Any comments,
bug reports, etc., are welcome.  Enjoy!
      

Anecdote about OPAM pinning

Archive: https://sympa.inria.fr/sympa/arc/caml-list/2015-01/msg00007.html

Lyn Headley told:
Y'all should know right away that I'm a newcomer to these
parts. I don't speak your fancy theorems and I ain't never proved
anything sound. Still, I reckon I got things to say, and I aim to have
some fun while I'm here. So strap yourselves in; I just had a great
little moment with ocaml and I'm going to hold forth on it, with
metaphors!

I've been building a tournament program, but that's not important. The
point is, I had just gotten my feet wet with everyone's favorite
packaging manager. Excuse me, I mean *platform* manager. But I had
built exactly *one* OPAM package in my life. And I'd been beavering
away on that project for months, and it was unfinished. So technically
I had built zero. But I decided to build another one anyway. Not in
a considered, deliberate way, mind you. In a seat-of-the-pants, New
Year's morning kind of way. It all just sort of happened, really.

I had been reading the js_of_ocaml documentation irregularly for
months, which is a bit like prospecting for gold. Only there's tons of
gold. But still, it takes work, and even ingenuity if I do say so
myself. So I found some shiny lumps about binding to the javascript
world, and I had been wondering what I might do for unit testing that
tournament program; So I guess I'd been kicking around thoughts about
binding to the jasmine javascript unit testing framework for
a while. Anyway, js_of_ocaml's runtime was starting to make a lot of
sense, and I had something I already wanted, nay "needed" to
build. The combination of a new tool and something to use it on is
like crack to me. So I really had no choice. I had to bind to jasmine.

(Jumping into the world of ocaml is like taking a course from
a brilliant, but somewhat aloof professor. You will experience
a regular series of epiphanies punctuated by moments of
frustration. But that's the yin and the yang of ocaml. It's kind of
ideal, actually. Indeed, you wouldn't *want* a completely smooth path,
because that would rob you of the satisfaction of having bested so
many private demons. And even though that professor sometimes seems
cold, you know that she's built your course with care, and with your
benefit at heart. But I digress.)

So I looked at the official jasmine tutorial and just started guessing
how I might bind to it. In an hour I had a minimally viable
binding. I could write multi-suite, multi-spec unit tests in ocaml for
the browser, and get a nice interface. So this was a lot of
fun. Incidentally, the API looks like this:

describe (string "a suite exercising jasmine itself")
(fun () ->
  it (string "runs basic boolean tests")
    (fun () ->
       (expect_bool _true)##toBe(_true));
  it (string "understands \"not\"")
    (fun () ->
       (expect_bool _false)##_not##toBe(_true)))


Having gotten this far, I wondered how to use this new binding from my
tournament program. An OPAM package? But it's so minimal that I'm the
only one who could find it useful! And I don't want to maintain it!
And what if people make fun of me? I wrestled with my soul, then threw
caution to the wind. I would build, and eventually publish, my second
OPAM package.

Elated by this reckless decision, I wasn't in the mood to go out and
read documentation (I admit I ran an opam pin --help). I wanted to
fire my gun. I wanted to test myself, and OPAM, to see what we were
made of. So I did.

Here's what happened [comments inline]:


------------- Begin Transcript ---------------

~/ocaml/ojasmine$ opam pin add ojasmine .

Package ojasmine does not exist, create as a NEW package ? [Y/n]

[I edited the opam file...]

ojasmine is now path-pinned to /home/laheadle/ocaml/ojasmine [NOTE]
You are pinning to /home/laheadle/ocaml/ojasmine as a raw path while
it seems to be a git repository.  [NOTE] Consider pinning with '-k
git' to have OPAM synchronise with your commits and ignore build
artefacts.

[ I did not know about this feature and found OPAM's hints helpful. ]

[ojasmine.~unknown] Synchronizing with /home/laheadle/ocaml/ojasmine

[WARNING] The opam file didn't pass validation:
  - Some fields are present but empty; remove or fill them
  - Missing field 'dev-repo'

Continue anyway ('no' will reedit) ? [Y/n] 

[ This is a good default; I will add the field later. ]

You can edit this file again with "opam pin edit ojasmine"

[ I did not know this. Again, helpful. ]

Save the new opam file back to "/home/laheadle/ocaml/ojasmine/opam" ? [Y/n] 

[ Yes. ]

ojasmine needs to be installed.
The following actions will be performed:
 - install   ojasmine.0.1*
=== 1 to install ===
Do you want to continue ? [Y/n] 

[ Sure. I thought I was just creating an opam file, but why not? Like
I said, I am feeling reckless. ]

=-=- Synchronizing package archives -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
[ojasmine.0.1] Synchronizing with /home/laheadle/ocaml/ojasmine

=-=- Installing packages =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Building ojasmine.0.1:
  make
  make install
[ERROR] The compilation of ojasmine.0.1 failed.
Removing ojasmine.0.1.
  make uninstall


#=== ERROR while installing ojasmine.0.1 ======================================#
# opam-version 1.2.0
# os           linux
# command      make install
# path         /home/laheadle/.opam/4.02.0/build/ojasmine.0.1
# compiler     4.02.0
# exit-code    2
# env-file     /home/laheadle/.opam/4.02.0/build/ojasmine.0.1/ojasmine-9015-6cc03f.env
# stdout-file  /home/laheadle/.opam/4.02.0/build/ojasmine.0.1/ojasmine-9015-6cc03f.out
# stderr-file  /home/laheadle/.opam/4.02.0/build/ojasmine.0.1/ojasmine-9015-6cc03f.err
### stdout ###
# ocamlbuild -cflag -annot -use-ocamlfind -pkgs js_of_ocaml.log,js_of_ocaml,js_of_ocaml.syntax -syntax camlp4o ojasmine.byte
# js_of_ocaml +weak.js ojasmine.byte
# ocamlbuild -cflag -annot -use-ocamlfind -pkgs js_of_ocaml.log,js_of_ocaml,js_of_ocaml.syntax -syntax camlp4o test_ojasmine.byte
# js_of_ocaml +weak.js test_ojasmine.byte
### stderr ###
# make: *** No rule to make target `install'.  Stop.

[NOTE] Pinning command successful, but your installed packages may be out of sync.

[Ok, that makes sense. I haven't even defined a make install target. ]

------------- End of Transcript ---------------

Even though the install failed, OPAM helpfully reminded me that the
pinning command worked. I found this reassuring.

Overall, this was a nice experience. I got to fire my gun and
everything went fine. OPAM anticipated my needs, led me through what
I needed to do, and gave me hints, all of which were very
helpful. OPAM's designers had thought carefully about what new
developer-users like me would be thinking.

Thanks OPAM!
      

ocamldep & compilation units

Archive: https://sympa.inria.fr/sympa/arc/caml-list/2015-01/msg00002.html

Remco Vermeulen asked:
I’m kind of stuck on the following situation.

foo.ml:
	...
 	module BAR = struct
 		…

		let x () = ...
	end
	...
	end

bar.ml:
       …
       let y () = …

baz.ml:
	open Foo
	...
	BAR.x ()
        …
	Bar.y ()

ocamldep -modules baz.ml, as used by Omake, returns baz.ml: BAR bar.
The manual of ocamldep states that the -modules option returns the
compilation units referenced in the source file.
But shouldn’t, in this situation, only Bar be a compilation unit?

In the current situation, Omake adds the compilation unit BAR to the
dependencies of baz.cm(o|x), meaning that it will search for the file
bAR.ml to satisfy the dependency.  This has the unfortunate effect of
using bar.ml on a case-insensitive filesystem (the default on OSX)
resulting in the error:
"Wrong file naming: bar.cmi contains the compiled interface for Bar when BAR was expected”

So my question is. is BAR in the above example correctly identified as
a compilation unit by ocamldep?
      
Gerd Stolpmann replied and Remco Vermeulen then said:
> The syntax doesn't allow an unambiguous identification, so ocamldep
> needs to take into account that BAR is a compilation unit. It doesn't
> follow "open" when doing this, and I guess this is the point that
> confuses you.

What confused me is that the documentation says ocamldep -modules
returns the module names of compilation units
referenced in the source file, and then includes local modules when
the “parent” module is implicitly qualified through open.
When the local module is referenced explicitly (i.e, fully qualified),
it is not included, which is inconsistent.

> The problem is that "ocamldep -modules" by definition can only analyze a
> single module. The output is imprecise, however, and possible
> inter-module effects are not taken into account (among other things). A
> precise output would list BAR with the exception that it might be
> shadowed by Foo.

Perhaps a note should be added to the documentation of ocamldep about this.

> But imagine now we had the information with this degree of detail. As
> omake wants to figure out the dependencies it would have to solve a
> puzzle. In your case it is easily to solve, but in practice there are
> often several "open" directives, and in this case you don't even know
> whether "open Foo" opens a compilation unit. I am not sure whether a
> well-performing algorithm even exists (did anybody tackle this
> problem?).
> 
> The workaround is to use naming schemes that allow you to clearly
> distinguish between local modules and compilation unit (e.g. all your
> local modules have 1-3 characters, and all compilation units have longer
> names).

This is not really an option, since this happens in an 3rd party library.
It seems that patching the omake ocaml scanner, to not rely on the -modules option,
seems to be the way to go as this is not trivial to handle in ocamldep.

Thanks for your clarification!
      
Gabriel Scherer then said:
ocamldep is approximative by design (providing a correct list of
dependencies requires interleaving type-checking and dependency
computations, a very different *compiler* design that would require an
important amount of work). The easiest way out in the current state of
things is to have an option in build systems to remove spurious
dependencies by hand (they're quite rare in practice). ocamlbuild for
example has a (non_dependency "baz" "BAR") that you would use in your
situation. If Omake lacks such a capability, I think the best step
forward would be to add it (instead of trying to come up with
different heuristics than "ocamldep -modules").
      
Xavier Leroy also replied to the original question:
> baz.ml:
> 	open Foo
> 	...
> 	BAR.x ()
>         …
> 	Bar.y ()
> 
> ocamldep -modules baz.ml, as used by Omake, returns baz.ml: BAR bar.

To add to the useful answers already given, another way to work around
this issue is to use explicit qualification for BAR:

 	open Foo
 	...
 	Foo.BAR.x ()
         …
 	Bar.y ()

That will prevent ocamldep from mistaking BAR for a compilation unit.

Gerd Stolpmann adds:

> But imagine now we had the information with this degree of detail. As
> omake wants to figure out the dependencies it would have to solve a
> puzzle. In your case it is easily to solve, but in practice there are
> often several "open" directives, and in this case you don't even know
> whether "open Foo" opens a compilation unit. I am not sure whether a
> well-performing algorithm even exists (did anybody tackle this
> problem?).

Matthias Blume showed a NP-completeness result for a closely-related problem:

Matthias Blume: Dependency analysis for Standard ML. ACM
Trans. Program. Lang. Syst. 21(4): 790-812 (1999)
      

Problem with GADTs and escaping types

Archive: https://sympa.inria.fr/sympa/arc/caml-list/2015-01/msg00008.html

Kaspar Rohrer asked:
I was playing around with GADTs over the last few days and encountered
the following problem (reduced to a minimal case here): 

    type t =
    | App : {k:'t . 'a->'t->'t; s:'a} -> t
    
    
    let munge : ('a->'t->'t as 'k) -> 'k = fun k -> fun s r -> k s r
    
    
    let transform = function
    | App {k;s} -> App {k=munge k;s}

Note that I am using inlined records, which are only available in the
unreleased 4.03 version. But I get the same error when using a
separate record in OCaml 4.01, i.e.:

    type t = App : 'a app_t -> t
    and 'a app_t = { k : 't. 'a->'t->'t; s:'a }

OCaml will choke on the transform function with the following error:

    
    Error: This field value has type a#17 -> 'b -> 'b which is less
    general than
    't. 'a -> 't -> 't

If I add type annotations like so:

    let transform_annotated = function
    | App {k;s} -> App {k=(munge k : 'a->'t->'t);s=(s:'a)}

OCaml will instead give me an error about escaping types:

    
    Error: This expression has type a#19 -> 'a -> 'a
    but an expression was expected of type a#19 -> 't -> 't
    The type constructor a#19 would escape its scope

I can work around this whole problem by inlining the munge function:

    let transform_inline = function
    | App {k;s} -> let k' s r = k s r in App {k=k';s}

This works but it is not ideal for my case, as it leads to somewhat
unwieldy match expressions.

Can somebody explain to me why the first case does not work?
Especially when considering that the types should be equal
(substituting the identity function for munge produces the the same
error).
      
Leo White replied:
>     type t =
>       | App : {k:'t . 'a->'t->'t; s:'a} -> t
>
>     let munge : ('a->'t->'t as 'k) -> 'k = fun k -> fun s r -> k s r
>
>     let transform = function
>       | App {k;s} -> App {k=munge k;s}
>
> OCaml will choke on the transform function with the following error:
>
>     Error: This field value has type a#17 -> 'b -> 'b which is less general than
>              't. 'a -> 't -> 't

This is an example of the value restriction. A function application
(like `munge k`) is not a syntactic value so it cannot be considered
polymorphic. Since `App` requires `k` to be polymorphic, you get an
error.

The simplest solution in your case is probably to make `munge` work
directly on the record type:

    let munge {k; s} = {k = (fun s r -> k s r); s}

You could also create another record type containing only `k`, and have
`munge` operate on that.

>
> If I add type annotations like so:
>
>     let transform_annotated = function
>       | App {k;s} -> App {k=(munge k : 'a->'t->'t);s=(s:'a)}
>
> OCaml will instead give me an error about escaping types:
>
>     Error: This expression has type a#19 -> 'a -> 'a
>            but an expression was expected of type a#19 -> 't -> 't
>            The type constructor a#19 would escape its scope

This is an example of the slightly surprising scoping of type
variables. It sometimes surprises people, but any type variable (e.g. 'a
in your above code), is given the same scope as the enclosing top-level
definition (e.g. `transform_annotated`). Since it would not be safe for
the existential type `'a` in the definition of `App` to escape outside
of its match case this results in a slightly surprising error.

Note that the annotation you have added does not actually do what you
want anyway. You are trying to enforce the result of `munge k` to have
the *polymorphic* type `'t. 'a -> 't -> 't`, but `'a -> 't -> 't` just
ensures that both 't types are equal, it does not ensure that they are
polymorphic. In fact, thanks to the top-level scope of type variables,
your annotation is having the opposite effect, forcing `'t` to be
considered monomorphic within the scope of the top-level definition.

Hope that clears things up for you. You've managed to hit two slightly
complicated parts of the type system in a single example.
      

Other OCaml News

From the ocamlcore planet blog:
Thanks to Alp Mestan, we now include in the OCaml Weekly News the links to the
recent posts from the ocamlcore planet blog at http://planet.ocaml.org/.

Senior Software Engineer at McGraw-Hill Education (Full-time):
  http://functionaljobs.com/jobs/8775-senior-software-engineer-at-mcgraw-hill-education

13 Virtues:
  https://blogs.janestreet.com/13-virtues/

Immutable:
  http://typeocaml.com/2015/01/02/immutable/

Mirage 2014 review: IPv6, TLS, Irmin, Jitsu and community growth:
  http://openmirage.org/blog/2014-in-review
      

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