Skip to content

feat: typed Choice<E> and ChoiceAnswer<E> with enum constants as labels - #15

Merged
garretpremo merged 1 commit into
mainfrom
feat/typed-choice
Sep 22, 2026
Merged

garretpremo merged 1 commit into
mainfrom
feat/typed-choice

Conversation

@garretpremo

Copy link
Copy Markdown
Contributor

Motivation:
Implements the design accepted on #10: the Java rendering of the JS SDK's ChoiceResponse<T>. A misspelled label becomes a compile error, probabilities are keyed by the enum, and a switch over the answer is exhaustive.

Changes:

  • Choice<E>: of(instructions, Dept.class) builds one option per constant; builder(Dept.class) takes option(Dept.X, …) for a subset with descriptions. of(…, String...), of(…, Map), and the existing builder infer Choice<String>. Record components are unchanged, so the wire form is identical.
  • ChoiceAnswer<E>: as(Dept.class) converts a String-labelled answer to the enum, with probabilities in an EnumMap in declaration order. An unknown label throws IllegalArgumentException naming the label and the enum's constants.
  • TypeSafeResponse.choice(key, Dept.class); choice(key) returns ChoiceAnswer<String> and choices() returns Map<String, ChoiceAnswer<String>>.
  • TypeSafeRequest.Builder.choice(key, Dept.class, c -> …) alongside the existing String form.
  • Tests: enum read with declaration-order probabilities and the unchanged String read; unknown-label rejection; enum construction, subset builder, request builder, and a wire-shape check that no enum metadata leaks.
  • README "Typed choices" section; CHANGELOG entry with the recompile note.

Breaking: ChoiceAnswer gained a type parameter, which changes the erased return type of choice(). Consumers must recompile; a raw ChoiceAnswer needs <String> or var.

Fixes #10

…abels

Choice<E> and ChoiceAnswer<E> carry the label type. An enum builds the
question from its constants and reads the answer back as that enum, so
a misspelled label is a compile error, probabilities are keyed by the
constants in declaration order, and a switch over the answer is
exhaustive. String labels infer Choice<String> and read back unchanged.
The wire form is the same either way: the label is the constant's name.

ChoiceAnswer's type parameter changes the erased return type of
choice(), so consumers of 0.4.0 and earlier must recompile (#10).
@garretpremo
garretpremo merged commit 340c716 into main Sep 22, 2026
1 check passed
garretpremo added a commit that referenced this pull request Sep 22, 2026
Choice<E> and ChoiceAnswer<E> carry the label type: build a question
from an enum's constants and read the answer back as that enum, with
probabilities keyed by it. String labels are unchanged. ChoiceAnswer's
type parameter changes the erased return type of choice(), so consumers
of 0.4.0 and earlier must recompile (#10, #15).

ScoreAnswer.legend is Map<String, Object>, so a Score with an object or
array level no longer fails the whole response (#11, #13). A response
missing usage, or a usage missing a token count, fails with a
TypeSafeException naming the field instead of reading as null or 0
(#12, #14). READMEs point at 0.5.0.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Typed Choice answers, as the JS SDK's ChoiceResponse<T>

1 participant