Last active
April 12, 2020 13:20
-
-
Save Gabriella439/563fa662f84e0a845c79775756cfce78 to your computer and use it in GitHub Desktop.
API design notes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
* Importance of category theory | |
* Answers the question: "What is a *timeless* API?" | |
* What does "timeless" mean? | |
* Likely to still be relevant years from now | |
* Likely to be low maintenance (since unlikely to change) | |
* Less likely to be subject to controversy or discussion ("obvious") | |
* Examples: | |
* Everything Haskell's typeclassopedia (except maybe `Foldable`) | |
* Categories / Monoids | |
* `(.)` / `id` | |
* `<=<` / `return` | |
* `&&` / `True` | |
* `||` / `False` | |
* `+` / `0` | |
* `*` / `1` | |
* Functors / Monoid Homomorphisms | |
* `fmap` (obviously) | |
* `length` | |
* `filter` | |
* `lift` | |
* I've actually done the experiment, authoring packages both based on | |
category and not based on category theory and difference in | |
maintainability is quite stark | |
* GIVE MODULES SHORT NAMES | |
* Please get rid of the `Data` / `Control` prefix | |
* A good rule of thumb is to name the default module to import after your | |
package (with no extra cruft) | |
* If you're going to claim valuable package namespace, then there's no | |
harm claiming valuable module namespace either | |
* This rule of thumb also greatly reduces the likelihood of module | |
naming conflicts between packages | |
* For example, the default import for the `lens` package should really be | |
just `Lens` (no `Control.` prefix) | |
* How deal with types with default values | |
* Just expose the record type. Stop obsessing over perfect backwards | |
compatibility | |
* At most, provide a `defaultFoo` value that users can use to minimize | |
migration work | |
* Haskell is a typed language. Your users will be easily able to update | |
their code if you change the shape. | |
* Think through what import lists will be like for your users | |
* "The idiomatic Haskell program is 6 language extensions, 20 imports, and | |
1 line of Perl" | |
* Re-export everything your users need in some module | |
* Professional users might not use it, but helps for beginners or | |
tutorial material | |
* Tell your users how to import your modules qualified | |
* Preferably by example (that you included in your haddocks) | |
* Even better if your module name is short enough that fully qualified is | |
fine (e.g. `Turtle`, `Pipes`) | |
* Users hate lots of type parameters | |
* Ask me how I know | |
* This seems to be type safety tradeoff that most users are not willing to | |
make | |
* Avoid type synonyms | |
* Prefer newtypes or no synonym at all | |
* Type synonyms don't reliably show up in inferred types or error messages | |
* Also introduces more indirection than most users are comfortable with | |
* Which string types to use? | |
* Prefer strict `Text` / `ByteString` over their lazy alternatives | |
* Avoid `String` | |
* Avoid typeclass dependency hell | |
* If you introduce a new typeclass, please do not introduce instances for | |
types other than types within the same package or ones in `base` | |
* Don't make the mistake the `semigroups` package made where it depended on | |
way too many packages just to provide instances. | |
* `deriving (Generic, Foo)` is pretty close to the best thing ever | |
* Please implement a way to derive your typeclass if you introduce one | |
* Think through what language extensions are necessary to use your package | |
* Provide named versions of operators | |
* Some users prefer to qualify essentially everything and they'd prefer | |
to qualify named things rather than operators | |
* Operators also require your users to learn fixity and precedence | |
* Better to leave things out if you're not sure | |
* It's way easier to add things later and an excellent opportunity for | |
new contributors to "cut their teeth" | |
* Minimize "unchecked" `IO` exceptions | |
* i.e. `throwIO` | |
* Really hard to refactor weakly-typed exception hierarchies later on | |
* Minimize use of `FlexibleContexts` / `FlexibleInstances` | |
* Refactoring your type classes to avoid these instances is worth it! | |
* Deciding on the right balance of type safety versus convenience | |
* In Haskell the typical level of safety to aim for is to make bugs hard to | |
introduce unintentionally, not impossible | |
* Take requests from the audience to review specific packages | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment