Browse files

[doc] CONTRIBUTING: merged stdlib guidelines

  • Loading branch information...
Aqua-Ye committed Oct 3, 2012
1 parent fb77bd1 commit f5b2865705e5a4027b3fa0a52ded34a677bdb349
Showing with 243 additions and 234 deletions.
  1. +243 −0
  2. +0 −234 lib/stdlib/GUIDELINES
@@ -37,6 +37,249 @@ YEAR.MONTH.DAY, Version SEMVER (stability), GIT_HASH
* change 3
+# Stdlib
+## How to write an Opa module
+Read this before writing an Opa module.
+If you don't, chances are that the module will not be kept.
+The role of these guidelines is to make sure that:
+- we can find our way in the code easily
+- we produce documentation for end-users
+- your changes break neither the compiler nor the user's code.
+### I. All code must be documented.
+For examples of documented code, see the existing code.
+Try to stay as much as possible is the same spirit, and
+check that the generated html (opadoc) is well formed.
+Documenting takes only a few minutes.
+Understanding undocumented code takes hours or days.
+If you don't document your code, someone will hate you passionately.
+You have been warned.
+Important Note: Don't confuse "commenting" and "documenting".
+Comments are good but can't replace documentation.
+Again, see the examples.
+Documentation MUST indicate, among other things
+- what the module is for
+- who it is for ("@audience PUBLIC"/"@audience PRIVATE"/...)
+- possibly, how it is used, by who, in particular for modules of stdlib.core,
+because any change in this kind of modules can have an impact on the compiler.
+For an example of documentation, see DOCUMENTING, in this directory,
+or see all the existing documentation.
+### II. How should I call my module? Where should I put it?
+We have a notion of packages. The compiler is fully separated with respect to
+The main criterium for the separation of files is the packages dependencies.
+Package names can be named with a '.' (dot), but it is a fake hierarchy. In
+fact, a dot can be seen like an underscore, and packages are identifying by
+their names, so, if it is not needed, do not go too deep in the hierarchy.
+#### II.1) stdlib.core
+If your module is used by the compiler, it must go to : stdlib.core
+This package is needed for core Opa features, it contains mostly run time
+support, like comparison, serialization, servers codes.
+This package also defines its own interface for the compiler, i.e. the complete
+set of function the compiler can insert calls on. This interface is declared by declaration tagged by @opacapi directives.
+Theses declarations should only be toplevel aliases, on functions in stdlib.core,
+and the name of each alias must be the full path of the function with dots
+replaced by underscores.
+E.g. : Value_serialize = Value.serialize
+Be extremly aware of the fact that we are trying to reduce as much as possible
+the size of the minimal server. This has also an important impact on the debugging.
+If your module is not used by the compiler, then, it has really nothing to do in stdlib.core.
+If you add a lot of code in stdlib.core, someone will hate you passionately.
+You have been warned.
+##### II.2) stdlib.*
+These packages are additionnaly libs we want to be part of the standard library.
+It is still not really clear if a package should be in the stdlib, or not.
+Probably most of generic packages developped @mlstate will be in stdlib.*
+If your package is too specific, then it is probably better to make a separate package.
+some example:
+ facebook -> not is stdlib, TODO: specify the package organisation.
+ fgraph -> stdlib.fgraph
+If you want to add several modules related to the same thing, it would be preferable
+to create a new package. (cf e.g. fgraph)
+If your module is an extension of an existing package, simply add it in the corresponding package.
+### III. Code goes into modules.
+Remember, by default, in Opa, everything sits in the current package namespace.
+Every typename and every value name you create therefore pollutes the current package namespace,
+potentially breaking someone else's code.
+Consequently, all your values must be in modules, and/or restricting the interfaces
+with the scopes directives :
+Similarly, all your types should respect the namespace conventions.
+For the moment, we don't have a syntax to put types into record.
+Until then, if you wish to add a type "foo" to your module "Foo", call it "".
+The name of types should strictly follow the hierarchy of modules.
+If you wish to add a function or a type to the global namespace, ask the Opa team first.
+Be aware that big changes in the stdlib are dangerous because of the number of potential
+users which already use it.
+Be aware that if you add a function with a ugly name (or even worth, a function with a
+name incoherent with the implementation), someone could use it the next day,
+and if you want to change some names, and types after that, it will imply a big refactoring
+everywhere your functions are used.
+If you add function which does not follow naming conventions, or argument order conventions,
+or any other conventions written in this file, someone will hate you passionately.
+You have been warned.
+Remember that once a Opa distribution will be done, we will no more be able to change any interfaces !
+There is no hurry. Take your time. Review your code, ask advice for names of functions to other people,
+it is always good to have several point of vues.
+See also if a correspond lib does not already exists in an other language.
+For exemple, if there is a reference API in an other language, you can keep the same names
+(convention used e.g. in fgraph, the API is mostly preserved from OcamlGraph)
+Let's say we are playing in a contest of beauty of API.
+### IV. Naming conventions.
+Accessor functions:
+Conversion functions:
+### IV. Types are closed.
+When you define a new type or data structure, chances are that users don't need
+to know how it's implemented, so you should make it @abstract (if users should
+see the type but not its definition) or @private (if the type is purely
+internal). This will make error messages simpler for the user and it might make
+type-checking a tad faster.
+### V. Mutability.
+Mutability is bad. You have no idea how bad it is until you've attempted to
+execute mutable code in parallel. Consequently, mutable code should be
+avoided. Really.
+In the current versions of Opa, the unit of mutability is the session. If you
+need mutability, use a session. If you need a global state shared by the server,
+use [UserContext]. If you need something to be saved for future uses, use the
+database. Don't write volatile stuff into the database. No sessions, no session
+identifiers, etc.
+If you have a use case that fits none of these, and if you are really convinced
+that you need a reference, come and see David. He might scream at you, of
+### V. Interacting with the compiler.
+If you define a value which needs to be used by the compiler, there are a few
+steps you need to take in order to make sure that it won't be removed
+prematurely by a compiler optimization and/or by the persons maintaining the
+For this purpose, you should
+- mark your value as @opacapi
+- add it to opacapi/
+- in some cases, add it to the roots
+- document how and why it's used in the compiler
+### VI. Checking that it works.
+Compile with the normal procedure (make), and run tests.
+### VII. Bypasses.
+Make what you want with bypasses. Do not export private bypasses.
+### VIII. Fold, map, etc.
+The base loops upon each data structure are
+- fold, foldi
+- map, mapi
+- iter, iteri
+- filter, filteri
+- reduce, reducei
+- filter_map, filter_mapi
+Wherever possible, data structures should also support constructors
+- unfold, unfoldi
+- init
+- of_list
+And conversion to external data structures
+- to_list
+- to_map
+Unless your data structure doesn't support these constructions, they should all exist, exactly with this name.
+If your data structure supports several folds (or several maps, etc), one of them, the default one, must be
+called exactly "fold" (respectively "map"). Name the variants with a suffix, which makes the difference explicit
+(e.g. "fold" -> "fold_backwards", "iteri" -> "iteri_backwards", etc.)
+The order of arguments is the following, keep it consistent for all data structures:
+function ( iterator, structure, [more-arguments]) : 'result
+- exists: ( f:('a -> bool), structure: t('a) ): bool
+- filter: ( f:'a -> bool, structure: t('a) ): t('a)
+- filteri: ( f:int -> 'a -> bool, structure: t('a) ): t('a)
+- filter_map: ( f:'a -> option('b), structure: t('a) ): t('b)
+- filter_mapi: ( f:int -> 'a -> option('b), structure: t('a) ): t('b)
+- fold: ( f:('item, 'acc) -> 'acc, structure: t('item), init:'acc ): 'acc
+- foldi: ( f:(int, 'item, 'acc) -> 'acc, structure: t('item), init:'acc ): 'acc
+- init: ( f:int -> 'item, size: int): t('item)
+- iter: ( f:('a -> void), structure: t('a) ): void
+- iteri: ( f:(int, 'a) -> void, structure: t('a) ): void
+- map: ( f:'a -> 'b, structure: t('a) ): t('b)
+- mapi: ( f:(int,'a) -> 'b, structure: t('a) ): t('b)
+- mem: ( elt:'a, structure: t('a) ): bool
+- reduce: ( f:'a -> 'a, structure: t('a) ): option('a) //{none} if the structure was empty
+- reducei: ( f:int -> 'a -> 'a, structure: t('a) ): option('a) //{none} if the structure was empty
+- unfold: ( f:'counter -> option(('item, 'counter)), init: 'counter ): t('item)
+- unfoldi: ( f:(int, 'counter)-> option(('item, 'counter)), init: 'counter): t('item)
# Opa Documentation format :
Oops, something went wrong.

0 comments on commit f5b2865

Please sign in to comment.