eliom
eliom copied to clipboard
Discussion on Eliom6 naming
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.tareOcaml | Non_ocaml | Unsafe. I think it's generally preferable to name a variant by what it is instead of that it's not. Doesn'tNon_ocamlpertain to services which return non-executable data such as Html, raw text, etc? Wouldn't it be better then for the variants to be namedCode | Data | Unsafe? - The constructors are named
create, which goes against the community trend of calling themmake. Even the Array module in the Stdlib has deprecatedcreatein favour ofmake... - 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, andMethas 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 [])
())
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 ?
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.
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.
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?
which may include executable code, right?
No, you can't serialize functions. At least not here.
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.
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.
Hum, are there no use cases for writing ocaml services (outside of server functions) ?
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.
@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.
In fact, no submodules are really needed, just a create_unsafe and a create_ocaml with different shadow parameters.
I'm not sure how I feel about de-duplicating things again.
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.
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.
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: 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...)
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.
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...