@@ -46,6 +46,10 @@ Eio replaces existing concurrency libraries such as Lwt
4646 * [ Async] ( #async )
4747 * [ Lwt] ( #lwt )
4848 * [ Unix and System Threads] ( #unix-and-system-threads )
49+ * [ Best Practices] ( #best-practices )
50+ * [ Switches] ( #switches-1 )
51+ * [ Casting] ( #casting )
52+ * [ Passing Stdenv.t] ( #passing-stdenvt )
4953* [ Further Reading] ( #further-reading )
5054
5155<!-- vim-markdown-toc -->
@@ -1320,6 +1324,117 @@ The [Eio_unix][] module provides features for using Eio with OCaml's Unix module
13201324In particular, ` Eio_unix.run_in_systhread ` can be used to run a blocking operation in a separate systhread,
13211325allowing it to be used within Eio without blocking the whole domain.
13221326
1327+ ## Best Practices
1328+
1329+ This section contains some recommendations for designing library APIs for use with Eio.
1330+
1331+ ### Switches
1332+
1333+ A function should not take a switch argument if it could create one internally instead.
1334+
1335+ Taking a switch indicates that a function creates resources that outlive the function call,
1336+ and users seeing a switch argument will naturally wonder what these resources may be
1337+ and what lifetime to give them, which is confusing if this is not needed.
1338+
1339+ Creating the switch inside your function ensures that all resources are released
1340+ promptly.
1341+
1342+ ``` ocaml
1343+ (* BAD - switch should be created internally instead *)
1344+ let load_config ~sw path =
1345+ parse_config (Eio.Path.open_in ~sw path)
1346+
1347+ (* GOOD - less confusing and closes file promptly *)
1348+ let load_config path =
1349+ Switch.run @@ fun sw ->
1350+ parse_config (Eio.Path.open_in ~sw path)
1351+ ```
1352+
1353+ Of course, you could use ` with_open_in ` in this case to simplify it further.
1354+
1355+ ### Casting
1356+
1357+ Unlike many languages, OCaml does not automatically cast objects (polymorphic records) to super-types as needed.
1358+ Remember to keep the type polymorphic in your interface so users don't need to do this manually.
1359+ This is similar to the case with polymorphic variants (where APIs should use ` [< ...] ` or ` [> ...] ` ).
1360+
1361+ For example, if you need an ` Eio.Flow.source ` then users should be able to use a ` Flow.two_way `
1362+ without having to cast it first:
1363+
1364+ <!-- $MDX skip -->
1365+ ``` ocaml
1366+ (* BAD - user must cast to use function: *)
1367+ module Message : sig
1368+ type t
1369+ val read : Eio.Flow.source -> t
1370+ end
1371+
1372+ (* GOOD - a Flow.two_way can be used without casting: *)
1373+ module Message : sig
1374+ type t
1375+ val read : #Eio.Flow.source -> t
1376+ end
1377+ ```
1378+
1379+ If you want to store the argument, this may require you to cast internally:
1380+
1381+ ``` ocaml
1382+ module Foo : sig
1383+ type t
1384+ val of_source : #Eio.Flow.source -> t
1385+ end = struct
1386+ type t = {
1387+ src : Eio.Flow.source;
1388+ }
1389+
1390+ let of_source x = {
1391+ src = (x :> Eio.Flow.source);
1392+ }
1393+ end
1394+ ```
1395+
1396+ Note: the ` #type ` syntax only works on types defined by classes, whereas the slightly more verbose ` <type; ..> ` works on all object types.
1397+
1398+ ### Passing Stdenv.t
1399+
1400+ The ` env ` value you get from ` Eio_main.run ` is a powerful capability,
1401+ and programs are easier to understand when it's not passed around too much.
1402+
1403+ In many cases, it's clearer (if a little more verbose) to take the resources you need as separate arguments, e.g.
1404+
1405+ <!-- $MDX skip -->
1406+ ``` ocaml
1407+ module Status : sig
1408+ val check :
1409+ clock:#Eio.Time.clock ->
1410+ net:#Eio.Net.t ->
1411+ bool
1412+ end
1413+ ```
1414+
1415+ You can also provide a convenience function that takes an ` env ` too.
1416+ Doing this is most appropriate if many resources are needed and
1417+ your library is likely to be initialised right at the start of the user's application.
1418+
1419+ In that case, be sure to request only the resources you need, rather than the full set.
1420+ This makes it clearer what you library does, makes it easier to test,
1421+ and allows it to be used on platforms without the full set of OS resources.
1422+ If you define the type explicitly, you can describe why you need each resource there:
1423+
1424+ <!-- $MDX skip -->
1425+ ``` ocaml
1426+ module Status : sig
1427+ type 'a env = <
1428+ net : #Eio.Net.t; (** To connect to the servers *)
1429+ clock : #Eio.Time.clock; (** Needed for timeouts *)
1430+ ..
1431+ > as 'a
1432+
1433+ val check : _ env -> bool
1434+ end
1435+ ```
1436+
1437+
13231438## Further Reading
13241439
13251440- [ lib_eio/eio.mli] ( lib_eio/eio.mli ) documents Eio's public API.
0 commit comments