Skip to content

Commit 0af5476

Browse files
committed
clarify importance of constructor over type in traits and docstrings
1 parent 54a5f9b commit 0af5476

File tree

6 files changed

+47
-12
lines changed

6 files changed

+47
-12
lines changed

docs/src/anatomy_of_an_implementation.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ mechanism for creating new versions of itself, with modified property (field) va
5151
this end, we implement `LearnAPI.constructor`, which must return a keyword constructor:
5252

5353
```@example anatomy
54+
"""
55+
Ridge(; lambda=0.1)
56+
57+
Instantiate a ridge regression algorithm, with regularization of `lambda`.
58+
"""
5459
Ridge(; lambda=0.1) = Ridge(lambda)
5560
LearnAPI.constructor(::Ridge) = Ridge
5661
nothing # hide
@@ -60,6 +65,8 @@ So, if `algorithm = Ridge(lambda=0.1)` then `LearnAPI.constructor(algorithm)(lam
6065
is another algorithm with the same properties, except that the value of `lambda` has been
6166
changed to `0.05`.
6267

68+
Note that we attach the docstring to the constructor, not the struct.
69+
6370

6471
## Implementing `fit`
6572

docs/src/reference.md

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ for such algorithms [`LearnAPI.is_composite`](@ref)`(algorithm)` must be `true`
9595
is `false`). Generally, the keyword constructor provided by [`LearnAPI.constructor`](@ref)
9696
must provide default values for all non-algorithm properties.
9797

98+
Any object `algorithm` for which [`LearnAPI.functions`](@ref)`(algorithm)` is non-empty is
99+
understood have a valid implementation of the LearnAPI.jl interface.
100+
98101
### Example
99102

100103
Any instance of `GradientRidgeRegressor` defined below is a valid algorithm.
@@ -110,8 +113,11 @@ GradientRidgeRegressor(; learning_rate=0.01, epochs=10, l2_regularization=0.01)
110113
LearnAPI.constructor(::GradientRidgeRegressor) = GradientRidgeRegressor
111114
```
112115

113-
Any object `algorithm` for which [`LearnAPI.functions`](@ref)`(algorithm)` is non-empty is
114-
understood have a valid implementation of the LearnAPI.jl interface.
116+
### Documentation
117+
118+
Attach public LearnAPI.jl-related documentation for an algorithm to it's *constructor*,
119+
rather than to the struct defining its type. In this way, an algorithm can implement
120+
non-LearnAPI interfaces (such as a native interface) with separate document strings.
115121

116122

117123
## Methods
@@ -125,8 +131,9 @@ Only these method names are exported by LearnAPI: `fit`, `transform`, `inverse_t
125131
### List of methods
126132

127133
- [`fit`](@ref fit): for training or updating algorithms that generalize to new data. Or,
128-
for non-generalizing ("static") algorithms, wrap `algorithm` in a mutable struct that
129-
can be mutated by `predict`/`transform` to record byproducts of those operations.
134+
for non-generalizing algorithms (see [Static Algorithms](@ref)), wrap `algorithm` in a
135+
mutable struct that can be mutated by `predict`/`transform` to record byproducts of
136+
those operations.
130137

131138
- [`predict`](@ref operations): for outputting [targets](@ref proxy) or [target
132139
proxies](@ref proxy) (such as probability density functions)

docs/src/traits.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,13 @@ Multiple traits can be declared like this:
9595
To ensure that trait metadata can be stored in an external algorithm registry, LearnAPI.jl
9696
requires:
9797

98-
1. *Finiteness:* The value of a trait is the same for all algorithms with same
99-
underlying `UnionAll` type. That is, even if the type parameters are different, the
100-
trait should be the same. There is an exception if `is_composite(algorithm) = true`.
98+
1. *Finiteness:* The value of a trait is the same for all `algorithm`s with same value of
99+
[`LearnAPI.constructor(algorithm)`](@ref). This typically means trait values do not
100+
depend on type parameters! There is an exception if `is_composite(algorithm) = true`.
101101

102-
2. *Serializability:* The value of any trait can be evaluated without installing any
103-
third party package; `using LearnAPI` should suffice.
102+
2. *Immediate serializability:* It should be possible to call a trait without first
103+
installing any third party package. Importing the package that defines the algorithm,
104+
together with `import LearnAPI` should suffice.
104105

105106
Because of 1, combining a lot of functionality into one algorithm (e.g. the algorithm can
106107
perform both classification or regression) can mean traits are necessarily less

src/traits.jl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,12 @@ julia> algorithm2.lambda
7676
7777
# New implementations
7878
79-
All new implementations must overload this trait. It must be possible to recover an
80-
algorithm from the constructor returned as follows:
79+
All new implementations must overload this trait.
80+
81+
Attach public LearnAPI.jl-related documentation for an algorithm to the constructor, not
82+
the algorithm struct.
83+
84+
It must be possible to recover an algorithm from the constructor returned as follows:
8185
8286
```julia
8387
properties = propertynames(algorithm)

src/types.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ end
8383

8484
const CONCRETE_TARGET_PROXY_TYPES_LIST = join(
8585
map(CONCRETE_TARGET_PROXY_TYPES_SYMBOLS) do s
86-
"`$s`"
86+
"`$s()`"
8787
end,
8888
", ",
8989
" and ",

test/integration/regression.jl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,17 @@ import DataFrames
1010
# We overload `obs` to expose internal representation of input data. See later for a
1111
# simpler variation using the `obs` fallback.
1212

13+
# no docstring here - that goes with the constructor
1314
struct Ridge
1415
lambda::Float64
1516
end
17+
18+
"""
19+
Ridge(; lambda=0.1)
20+
21+
Instantiate a ridge regression algorithm, with regularization of `lambda`.
22+
23+
"""
1624
Ridge(; lambda=0.1) = Ridge(lambda) # LearnAPI.constructor defined later
1725

1826
struct RidgeFitObs{T,M<:AbstractMatrix{T}}
@@ -176,9 +184,17 @@ end
176184

177185
# # VARIATION OF RIDGE REGRESSION THAT USES FALLBACK OF LearnAPI.obs
178186

187+
# no docstring here - that goes with the constructor
179188
struct BabyRidge
180189
lambda::Float64
181190
end
191+
192+
"""
193+
BabyRidge(; lambda=0.1)
194+
195+
Instantiate a ridge regression algorithm, with regularization of `lambda`.
196+
197+
"""
182198
BabyRidge(; lambda=0.1) = BabyRidge(lambda) # LearnAPI.constructor defined later
183199

184200
struct BabyRidgeFitted{T,F}

0 commit comments

Comments
 (0)