docs(adr): adr template, design principles adr #26

Merged
mvhutz merged 20 commits from docs/contract-v2 into main 2026-07-05 01:27:01 +00:00
Showing only changes of commit 3aa5be87f2 - Show all commits
+104 -3
View File
@@ -1,5 +1,14 @@
# Designing an Idiomatic API Interface # Designing an Idiomatic API Interface
- [Designing an Idiomatic API Interface](#designing-an-idiomatic-api-interface)
- [Current State](#current-state)
- [Interface of the Built-in Map](#interface-of-the-built-in-map)
- [Interface of `go-cuckoo`](#interface-of-go-cuckoo)
- [Determining Congruency](#determining-congruency)
- [Determining Familiarity](#determining-familiarity)
- [Target State](#target-state)
- [Solving Congruency](#solving-congruency)
We (the maintainers) built `go-cuckoo`'s API interface without design intent. We (the maintainers) built `go-cuckoo`'s API interface without design intent.
Up until now, we paid more attention implementing the underlying functionality of the cuckoo hashing. Up until now, we paid more attention implementing the underlying functionality of the cuckoo hashing.
@@ -15,7 +24,7 @@ It should align closer to the following principles:
## Current State ## Current State
### Interface of the built-in Map ### Interface of the Built-in Map
Listed below is every interface provided by Go to the built-in map object. Listed below is every interface provided by Go to the built-in map object.
Also included, are the functions from the package `maps` in the standard library. Also included, are the functions from the package `maps` in the standard library.
@@ -23,7 +32,7 @@ Also included, are the functions from the package `maps` in the standard library
<details> <details>
<summary>Interfaces</summary> <summary>Interfaces</summary>
| # | built-in Interface | Description | | # | Built-in Interface | Description |
| --- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | --- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1 | `m := make(map[K]V)` | Returns an empty map using the built-in `make()` function. | | 1 | `m := make(map[K]V)` | Returns an empty map using the built-in `make()` function. |
| 2 | `m := make(map[K]V, hint)` | Returns an empty map using `make()`, with a capacity 'hint'. This hint is how many items the map expects to hold, _not_ a measure of how large it is. | | 2 | `m := make(map[K]V, hint)` | Returns an empty map using `make()`, with a capacity 'hint'. This hint is how many items the map expects to hold, _not_ a measure of how large it is. |
@@ -56,7 +65,7 @@ On the other hand, here is the current contract for `go-cuckoo`.
<details> <details>
<summary>Interfaces</summary> <summary>Interfaces</summary>
| # | built-in Interface | Description | | # | `go-cuckoo` Interface | Description |
| --- | -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | | --- | -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| 1 | `m := New(opts...)` | Creates a table using the default hash and equal function. The options configure its behavior. Confined to comparable keys. | | 1 | `m := New(opts...)` | Creates a table using the default hash and equal function. The options configure its behavior. Confined to comparable keys. |
| 2 | `m := NewBy(keyFunc, opts...)` | Like #1, but allows any key type. A `keyFunc` is used to derive a comparable key. | | 2 | `m := NewBy(keyFunc, opts...)` | Like #1, but allows any key type. A `keyFunc` is used to derive a comparable key. |
@@ -307,6 +316,98 @@ There is no analog.
</details> </details>
### Determining Familiarity
We can categorize all existing table functionality by each interface's familiarity:
- ✅ Idiomatic: is clear, intuitive, and easily understood.
- ❌ Non-idiomatic: is misleading; addressed in [Target State](#solving-congruency).
<details>
<summary>✅ <code>m := New(opts...)</code></summary>
Criteria:
Noun/adjective form — Go constructors use NewX where X is a noun or adjective, not a verb or past participle (NewReaderSize, not NewSized)
Names what the user provides — the suffix should hint at the distinguishing parameter (NewBufferString tells you it takes a string)
Progression is readable — the three names together should imply simple → intermediate → advanced
Not misleadingly generic — NewWith or NewConfig could mean anything
</details>
<details>
<summary><code>m := NewBy(keyFunc, opts...)</code></summary>
- Use `NewKeyed()`.
</details>
<details>
<summary><code>m := NewCustom(hashA, hashB, equalFunc, opts...)</code></summary>
- Use `NewHashed()`.
</details>
<details>
<summary>❌ <code>seq := m.Entries()</code></summary>
- Call it `All()`.
</details>
<details>
<summary><code>v := m.Find(k)</code></summary>
- Call it `m.Lookup()`. The name `m.Find` implies a search algorithm.
</details>
<details>
<summary><code>v, ok := m.Get(k)</code></summary>
</details>
<details>
<summary><code>ok := m.Has(k)</code></summary>
</details>
<details>
<summary>❌ <code>err := m.Put(k, v)</code></summary>
- Call it `Set()`.
- No built-in library consensus, but 3rd party packages prefer `Set()`.
</details>
<details>
<summary>❌ <code>n := m.Size()</code></summary>
- Call it `Len()`.
- Size is a Java idiom.
</details>
<details>
<summary>✅ <code>str := m.String()</code></summary>
</details>
<details>
<summary>❌ <code>cap := m.TotalCapacity()</code></summary>
- Remove. This is an implementation detail.
</details>
<details>
<summary>❌ <code>ok := m.Drop(k)</code></summary>
- Call it `Delete()`.
</details>
## Target State ## Target State
### Solving Congruency ### Solving Congruency