-
Notifications
You must be signed in to change notification settings - Fork 8
Description
Here's two basic libs that we could use as a starting point for a discussion:
https://github.com/luapower/dynarray
https://github.com/luapower/arrayview
arrayview is a struct with just a pointer & length that maps the idea of a finite array over a given buffer so that you can apply bound-checked access, copying, sorting, etc. over that buffer. dynarray is a malloc'ed arrayview, so basically an arrayview with additional methods and overloads for inserting and removing elements, etc. (and it actually uses arrayview instead of duplicating its code). The APIs are documented in the source code for now for many reasons (for one, this code was already been rewritten once and it will be further modified with increased usage).
Two points I want to make on these libs.
1. The necessity of a tier-0 lib
Even though they are the most basic libs you can imagine, they are necessarily not dependency-free, as they both depend on the low module (https://github.com/luapower/low) which is my tier-0 lib as discussed in #3. Hopefully it's now easier to see why that kind of lib is needed and what's in it. Here's an incomplete checklist:
addmethods(T, func)- pattern for declaring methods lazily for containers, see How to implement recursive types? terra#348addproperties(T)- addsT.propertieswhich can be quotes or macros so thatt.propredirects tot.properties.prop.after_getmethod(),before_entrymissing()etc. - allows a metamethod to be assigned multiple independent handlers -- this way you can have sayaddmethods()on a type which assigns__getmethodand still assign__getmethodafterwards for a different purpose.gettersandsetters()- callst:get_<name>()fort.<name>andt:set_<name>(val)fort.<name> = <val>iif(),min(),max(),assert()- ...copy(dst, src, len)- typedmemmoveequal(a, b)- typedmemcmpalloc()- typedreallochash(uint32|uint64, buf, len, hash)- default hash function- C headers are included with a wrapper around
includec()so that 1) the calls are memoized, 2) symbols are dumped into a single table which thelowmodule inherits so thatsetfenv(1, 'low')gives unprefixed access to functions likememsetetc.
Some of these are one-liners that can be copy-pasted to remove the dependency on the tier-0 lib, but some are not, so we need to talk about #3.
2. Number of users is the only reliable indicator of API quality
As I mentioned before, I think the best way (IMO the only way) to make a good lib is to first have an use case for it and have the lib emerge out of that. I strongly believe that an API is only as good as the number of calls to it that are made from a variety of contexts. I can't think of a stronger indicator of quality than that. An API that's not heavily used by at least 2-3 apps/libs is not ready for prime-time no matter how much design effort is put into it. My libs only have 1-2 users so far and already have undergone major refactorings and even rewrites. I'm not sure what the action point is here, rather than the advice to not waste time with "designing" APIs for imagined use cases: nobody's going to use them, and for good reason.