reason icon indicating copy to clipboard operation
reason copied to clipboard

ppx_export mark 2

Open jaredly opened this issue 8 years ago • 16 comments
trafficstars

taking over from https://github.com/facebook/reason/pull/1206

The purpose of this is to allow you to "export" values from your .re (or .ml) file directly, instead of going through a separate .mli file. I personally think this will be more usable, and in any case it will be more familiar to people coming from javascript.

We first implement it as a ppx, and then we can put some syntactic sugar (like an export keyword) in our reason parser to make it even more ergonomic.

work remaining

  • [x] basic things
  • [x] classes
  • [x] nested modules
  • [x] recursive modules
  • [x] good error reporting
  • [x] various cleanups
  • [x] export as & abstract type export

for example, you can do

let[@export] something: int = 5

let[@export] somefun (arg: int): string = "hello"

type[@export abstract] t = int
let[@export: t list] m = [2;3]

(* you can put an aribtrary signature in here too if you want more power*)
let[@export: val mm: int] mm = something

helpful documents for putting this together

  • https://github.com/ocaml/ocaml/blob/trunk/parsing/ast_helper.mli
  • https://github.com/ocaml/ocaml/blob/trunk/parsing/parsetree.mli
  • https://caml.inria.fr/pub/docs/manual-ocaml/extn.html#attr-payload

jaredly avatar May 10 '17 02:05 jaredly

One can export the whole type, or a more abstract version. Is this supported already? It's not crucial that this is supported in the first iteration, but to know that it's possible to extend it in future.

For example, here I'm just using modules directly for illustration, but imagine there are .re and .rei files, and a user of the .rei:

module M : { type t; let create : int => t; } = { type t = int; let create n => n };

then this shows that the type is hidden:

# M.create 34 == 34;
Error: This expression has type int but an expression was expected of type M.t 

cristianoc avatar May 10 '17 11:05 cristianoc

exporting a more abstracted version is not yet supported. I could imagine using an attribute w/ an "export type" to do that kind of thing, but yeah I think that should come as a separate diff. I'm thinking

module%export M = {
   [@export `abstract]
   type t = int;

  [@export () => t]
  let create () => 23
}

or something

jaredly avatar May 10 '17 14:05 jaredly

Specifying an export level sounds like a promising direction. In general, there can be many possible levels. Consider for example the concrete type type mylist = list int, then possible exports include

1. list int
2. list t
3. t
4. no export at all

cristianoc avatar May 10 '17 15:05 cristianoc

Would it be too hard to add "export as" in this diff? I'd be concerned that people are all too satisfied without the exporting as "abstract" feature because they are't used to having that capability, and then it never gets added - and then using the "exports" feature is inferior to using interface files. I think we should make it every bit just as good out of the gate. I'm finding that developers don't ask for abstract types, but they desperately need them.

jordwalke avatar May 10 '17 21:05 jordwalke

Good idea

jaredly avatar May 11 '17 20:05 jaredly

hmmm don't merge yet, I have a different idea

jaredly avatar May 12 '17 06:05 jaredly

Ok, "export as" working across the board! You can now do, for example

type[@export abstract] t = int
let[@export: t] myvar = 10

jaredly avatar May 13 '17 05:05 jaredly

Looks like some failures on 4.02.3 and 4.04. The 4.02.3 failures might be related to the ppx form we just discussed. Maybe you can use the equivalent syntactic form that would work on all versions. I'm not sure about the 4.04 failures though - looks more like slight differences in printing output.

jordwalke avatar May 15 '17 20:05 jordwalke

Also, @let-def can you take a look at the changes too?

jordwalke avatar May 15 '17 20:05 jordwalke

yeah, I'm not quite sure how I ought to fix the "ocaml 4.04 prints this a little differently"

jaredly avatar May 16 '17 18:05 jaredly

could you not just do export let ... that seems simpler, and would also be more familiar to newcommers.

cullophid avatar Jul 15 '17 13:07 cullophid

The proposal https://www.cl.cam.ac.uk/~jdy22/papers/extending-ocamls-open.pdf by @objmagic makes it possible to implement this feature with proper support from the compiler.

let-def avatar Sep 12 '17 08:09 let-def

In particular, it makes it possible to use the compiler infrastructure for finding names that are valid from the outside (e.g. assuming private type t = int -> int and exported let f : t = fun x -> x + 1, the compiler will rewrite the binding from val f : t to val f : int -> int)

let-def avatar Sep 12 '17 08:09 let-def

Thanks @let-def. The actual patch still needs some polish. And assuming if people are in favor of merging this feature into compiler, you probably will see it in >4.06 since I imagine the PR will go through a pretty long debate... ("looks ok to me" from Xavier, seems ok from Alain Frisch, neutral from Jacques and Leo)

That said, if people are in serious need of this ppx_export, continue working on this feature instead of waiting support from OCaml language is a better idea.

objmagic avatar Sep 13 '17 16:09 objmagic

open extension pull request was just opened at https://github.com/ocaml/ocaml/pull/1506

objmagic avatar Dec 02 '17 01:12 objmagic

That change ended up in 4.08, so as soon as the 4.08 PR gets merged this might be relevant again?

jaredly avatar Dec 05 '19 04:12 jaredly