1
+
Added:
(** Core measurement types for hito.
2
+
Added:
3
+
Added:
These types model the physical quantities recorded during training: the load
4
+
Added:
on the bar ({!Weight}), the number of repetitions performed ({!Reps}), and a
5
+
Added:
prescribed target repetition band ({!Rep_range}, e.g. Mentzer's 6-8).
6
+
Added:
7
+
Added:
Every type in this module is [abstract]: values can only be created through
8
+
Added:
smart constructors that reject nonsensical inputs (negative loads, zero or
9
+
Added:
negative reps, inverted ranges). Once you hold a value of one of these
10
+
Added:
types, it is guaranteed to be valid — illegal measurements are
11
+
Added:
unrepresentable.
12
+
Added:
13
+
Added:
This module is part of the pure domain core: it has no dependency on any web
14
+
Added:
framework, database, or serialization library. *)
15
+
Added:
16
+
Added:
(** A validation error explaining why a smart constructor rejected its input.
17
+
Added:
Rendered for developers and, where appropriate, surfaced to users. *)
18
+
Added:
type error =
19
+
Added:
| Negative of string (** A quantity that must be >= 0 was negative. *)
20
+
Added:
| Not_positive of string (** A quantity that must be > 0 was <= 0. *)
21
+
Added:
| Inverted_range of string
22
+
Added:
(** A range whose lower bound exceeded its upper bound. *)
23
+
Added:
24
+
Added:
val pp_error : Format.formatter -> error -> unit
25
+
Added:
(** [pp_error fmt e] pretty-prints a human-readable description of [e]. *)
26
+
Added:
27
+
Added:
(** Barbell / dumbbell load, stored internally in kilograms.
28
+
Added:
29
+
Added:
Weight is a nonnegative quantity: a body-weight movement may legitimately
30
+
Added:
carry a load of [0.0], but a negative load is never valid. *)
31
+
Added:
module Weight : sig
32
+
Added:
type t
33
+
Added:
(** An abstract, always-valid weight. *)
34
+
Added:
35
+
Added:
val of_kg : float -> (t, error) result
36
+
Added:
(** [of_kg kg] is [Ok w] when [kg] is finite and [>= 0.0], otherwise
37
+
Added:
[Error (Negative _)]. *)
38
+
Added:
39
+
Added:
val to_kg : t -> float
40
+
Added:
(** [to_kg w] is the load of [w] expressed in kilograms. *)
41
+
Added:
42
+
Added:
val zero : t
43
+
Added:
(** [zero] is a load of [0.0] kg, for body-weight movements. *)
44
+
Added:
45
+
Added:
val compare : t -> t -> int
46
+
Added:
(** Total ordering by kilograms. *)
47
+
Added:
48
+
Added:
val equal : t -> t -> bool
49
+
Added:
(** [equal a b] is [true] when [a] and [b] denote the same load. *)
50
+
Added:
51
+
Added:
val pp : Format.formatter -> t -> unit
52
+
Added:
(** Pretty-prints a weight, e.g. ["60.0 kg"]. *)
53
+
Added:
end
54
+
Added:
55
+
Added:
(** A count of completed repetitions.
56
+
Added:
57
+
Added:
Reps are a strictly positive whole number: a logged set has at least one
58
+
Added:
rep. (An abandoned attempt is modelled elsewhere, not as zero reps.) *)
59
+
Added:
module Reps : sig
60
+
Added:
type t
61
+
Added:
(** An abstract, always-valid repetition count. *)
62
+
Added:
63
+
Added:
val of_int : int -> (t, error) result
64
+
Added:
(** [of_int n] is [Ok r] when [n > 0], otherwise [Error (Not_positive _)]. *)
65
+
Added:
66
+
Added:
val to_int : t -> int
67
+
Added:
(** [to_int r] is the underlying repetition count. *)
68
+
Added:
69
+
Added:
val compare : t -> t -> int
70
+
Added:
(** Total ordering by count. *)
71
+
Added:
72
+
Added:
val equal : t -> t -> bool
73
+
Added:
(** [equal a b] is [true] when [a] and [b] denote the same count. *)
74
+
Added:
75
+
Added:
val pp : Format.formatter -> t -> unit
76
+
Added:
(** Pretty-prints a rep count, e.g. ["8 reps"]. *)
77
+
Added:
end
78
+
Added:
79
+
Added:
(** An inclusive target repetition band, such as Mentzer's canonical 6-8.
80
+
Added:
81
+
Added:
A range is valid when its lower bound is [<=] its upper bound; both bounds
82
+
Added:
are themselves valid {!Reps.t} values (hence strictly positive). *)
83
+
Added:
module Rep_range : sig
84
+
Added:
type t
85
+
Added:
(** An abstract, always-valid inclusive rep range. *)
86
+
Added:
87
+
Added:
val make : min:Reps.t -> max:Reps.t -> (t, error) result
88
+
Added:
(** [make ~min ~max] is [Ok range] when [min <= max], otherwise
89
+
Added:
[Error (Inverted_range _)]. *)
90
+
Added:
91
+
Added:
val min : t -> Reps.t
92
+
Added:
(** The inclusive lower bound. *)
93
+
Added:
94
+
Added:
val max : t -> Reps.t
95
+
Added:
(** The inclusive upper bound. *)
96
+
Added:
97
+
Added:
val contains : t -> Reps.t -> bool
98
+
Added:
(** [contains range r] is [true] when [r] falls within [range] inclusive. *)
99
+
Added:
100
+
Added:
val equal : t -> t -> bool
101
+
Added:
(** [equal a b] is [true] when [a] and [b] have equal bounds. *)
102
+
Added:
103
+
Added:
val pp : Format.formatter -> t -> unit
104
+
Added:
(** Pretty-prints a range, e.g. ["6-8 reps"]. *)
105
+
Added:
end