eliom icon indicating copy to clipboard operation
eliom copied to clipboard

Discussion on Eliom6 naming

Open darioteixeira opened this issue 9 years ago • 18 comments

I've started playing a bit with the upcoming Eliom6. Since bikeshedding season is officially open, here are some initial thoughts on the API (more to come later):

  • The variants in Eliom_service.Ret.t are Ocaml | Non_ocaml | Unsafe. I think it's generally preferable to name a variant by what it is instead of that it's not. Doesn't Non_ocaml pertain to services which return non-executable data such as Html, raw text, etc? Wouldn't it be better then for the variants to be named Code | Data | Unsafe?
  • The constructors are named create, which goes against the community trend of calling them make. Even the Array module in the Stdlib has deprecated create in favour of make...
  • The new API depends heavily on constructor disambiguation if you want to avoid lots of parentheses and full qualifications. I understand there may be strong reasons to declare Id, Ret, and Meth as submodules, but without relying on disambiguation this approach leads to a very messy declaration of even a simple service:
let hello_service =
    Eliom_service.(create
        ~ret:Ret.Non_ocaml
        ~meth:Meth.(Get Eliom_parameter.unit)
        ~id:Id.(Path [])
        ())

darioteixeira avatar Apr 21 '16 10:04 darioteixeira

Code | Data | Unsafe?

I don't disagree, but that's wrong, all of them are data. Just (almost raw) OCaml objects or more standard objects. It's more a question of how things are serialized actually, either text or marshal, but I really don't want to put Marshall in the API, it's really an implementation detail.

against the community trend of calling them make.

There is no community trend, it's all over the place. To counter your argument, Hashtbl.create, Bigarray.X.create, String.create (which is not the same as String.make, etc.

The new API depends heavily on constructor disambiguation

Huum, that's a good point, maybe we could reexport them inside Eliom_service ?

Drup avatar Apr 21 '16 11:04 Drup

The submodules have very little code, and that code is only needed inside Eliom. They are only there to prettify the namespace, but apparently there are counter-arguments :). I would rather remove them than re-export them; I much prefer a single canonical name to refer to things.

vasilisp avatar Apr 21 '16 11:04 vasilisp

I would rather remove them than re-export them; I much prefer a single canonical name to refer to things.

I disagree a bit. type meth = Meth.t = ... is a common pattern.

Drup avatar Apr 21 '16 11:04 Drup

I don't disagree, but that's wrong, all of them are data. Just (almost raw) OCaml objects or more standard objects. It's more a question of how things are serialized actually, either text or marshal, but I really don't want to put Marshall in the API, it's really an implementation detail.

Okay, so if I understand correctly, the Ocaml variant is used for marshalled OCaml values, which may include -- but not necessarily -- executable code, right? Nevertheless, there must be some property that all Non_ocaml elements share besides not being marshalled OCaml. And since the most obvious common factor is that they all represent unmarshalled data, I think even a term like Raw or Crude might be preferable. Another possibility is to call them Safe, which contrasts nicely with Unsafe. Speaking of which, what exactly goes into Unsafe at the moment? Unchecked HtmlText, for example?

I think coming up with proper names might be easier if there was a full list somewhere with the type of content that goes into each of the three categories. Is there?

darioteixeira avatar Apr 21 '16 12:04 darioteixeira

which may include executable code, right?

No, you can't serialize functions. At least not here.

Drup avatar Apr 21 '16 12:04 Drup

Okay, so if I understand correctly, the Ocaml variant is used for marshalled OCaml values, which may include -- but not necessarily -- executable code, right?

It is basically for use by server functions called from the client, and it can handle everything that the Marshal module can handle. This doesn't include executable code at all :).

And since the most obvious common factor is that they all represent unmarshalled data, I think even a term like Raw or Crude might be preferable. Another possibility is to call them Safe, which contrasts nicely with Unsafe. Speaking of which, what exactly goes into Unsafe at the moment? Unchecked HtmlText, for example?

I think coming up with proper names might be easier if there was a full list somewhere with the type of content that goes into each of the three categories. Is there?

The closest we have to such a list is Eliom_registration. non_ocaml is used for disparate things like textual content, redirections, actions, etc. Really, everything but marshaled OCaml values :).

Unsafe is for internal use by Eliom_registration. It is used to implement some weird kinds of services, like Eliom_registration.Any.

vasilisp avatar Apr 21 '16 12:04 vasilisp

Now that I think of it, the only user-visible case in Eliom_service should be non_ocaml. So we can get rid of the Ret.t in Eliom_service.create (with an implicit non_ocaml type) and provide an Eliom_service.Unsafe for use by Eliom_registration.

vasilisp avatar Apr 21 '16 12:04 vasilisp

Hum, are there no use cases for writing ocaml services (outside of server functions) ?

Drup avatar Apr 21 '16 12:04 Drup

No, you can't serialize functions. At least not here.

Okay. I was under the mistaken impression that this mechanism was also used for executable code (the output of js_of_ocaml) to go through the wire.

darioteixeira avatar Apr 21 '16 12:04 darioteixeira

@Drup, you can use the Eliom_registration API, or the more liberal Unsafe submodule. Or we can even provide an Eliom_service.Ocaml, no big deal, it is one function. Which partially brings us back to the previous Eliom_service, with the difference being that the submodules for ret are only used for the complicated cases.

vasilisp avatar Apr 21 '16 12:04 vasilisp

In fact, no submodules are really needed, just a create_unsafe and a create_ocaml with different shadow parameters.

vasilisp avatar Apr 21 '16 12:04 vasilisp

I'm not sure how I feel about de-duplicating things again.

Drup avatar Apr 21 '16 12:04 Drup

It is just two functions now, not 2 * 12, and the duplicated ones will be aliasing create implementation-wise (I think), and under a (**/**) in the signature. So no big deal, I think.

vasilisp avatar Apr 21 '16 12:04 vasilisp

There is create_external also which would need to be duplicated (I suspect that remote OCaml services can be useful). So merge create_external into create and do the above? Not sure.

vasilisp avatar Apr 21 '16 13:04 vasilisp

I pushed a branch doing the above: https://github.com/ocsigen/eliom/compare/no-service-ret . It eliminates the need to explain Ret.t and the duplication is insignificant. I think I like the net effect.

vasilisp avatar Apr 21 '16 14:04 vasilisp

@vasilisp: Mind you, my biggest complaint about the API as it sits currently in master is not so much about being forced to always declare an explicit ret parameter, but more about that parameter taking the awkward name Non_ocaml. Moreover, since Non_ocaml covers pretty much the regular run-of-the-mill usage of the create function, why not simply call it Regular? (Perhaps I'm biased because most of my usage of Eliom is purely server-side...)

darioteixeira avatar Apr 21 '16 16:04 darioteixeira

On a related note, please avoid using abbreviations most of the time. Let's compare two equivalent functions from Eliom_service. Previously we had:

val void_coservice' : 
  (unit,
   unit,
   [< service_method > `Get ],
   [< attached > `Nonattached ],
   [< service_kind > `NonattachedCoservice ],
   [ `WithoutSuffix ], unit, unit,
   [< registrable > `Unregistrable ],
   [> non_ocaml_service ]
  ) service

This has been changed to:

val Eliom_service.reload_action :
  (unit,
   unit,
   Eliom_service.get,
   Eliom_service.non_att,
   Eliom_service.co,
   Eliom_service.non_ext,
   Eliom_service.non_reg,
   [ `WithoutSuffix ],unit, unit, Eliom_service.non_ocaml
  ) Eliom_service.t

So Nonattached became non_att, Coservice became co, Unregistrable became non_reg. Abbreviations make things harder to understand in an API that is already not easy to grasp.

If some term is going to be very widely used and the extra characters cause substantial formatting burden, then maybe an abbreviation is okay. In this case, the abbreviations should be clearly stated in the top of the module's documentation. A second okay case for abbreviations is if there is an established standard, like type t, which doesn't require any extra thinking because we all know what it means.

agarwal avatar Aug 26 '16 18:08 agarwal

On a related note, please avoid using abbreviations most of the time. Let's compare two equivalent functions from Eliom_service. Previously we had:

I'm with Ashish on this one. Abbreviations are write-friendly but very read-unfriendly, particularly when the API is so large...

darioteixeira avatar Aug 27 '16 15:08 darioteixeira