Add Ask: keyed questions typed by their answer - #19
Merged
Merged
Conversation
An Ask pairs a key, a question, and the type of its answer, so the key and enum are declared once and response.answer(ask) reads the answer back typed: NoulAnswer, ChoiceAnswer<E>, or ScoreAnswer. Ask is a sealed abstract class whose subtypes NoulAsk, ChoiceAsk, and ScoreAsk mirror the question types. Each reads its own answer through a package-private method, so answer() needs no unchecked cast, and the factories are the only way to create one. An enum ChoiceAsk rejects a label that is not a constant of its enum when it is created. Asks go in through systemOne/systemOneAsync(state, asks...), TypeSafeRequest.of(state, asks...), and TypeSafeRequest.Builder.ask. A request rejects a second question under an asked key; keys added without an Ask still replace each other. Builder.state(key, value) now starts an object state when none is set. Closes #17
The Use section now opens with asks: declare the questions once, ask them, and read the answers back through them. Typed choices fold into a new Asks section; the string-keyed form moves to Keyed questions, framed as the form shared with the Python and JavaScript SDKs and for keys known only at runtime. Async, per-call options, and the starter's example read answers through asks. The enum example uses a switch expression, since only an expression has to cover every constant on Java 17.
This file contains hidden or 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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #17.
What
Ask<A>pairs a key, a question, and the type of its answer. Declare it once, ask it, and read the answer back through it:Ask.noul/Ask.choice/Ask.scorefactories, as proposed in the issue.systemOne/systemOneAsync(state, first, more...),TypeSafeRequest.of(state, first, more...), andTypeSafeRequest.Builder.ask(...). Per-callRequestOptionsgo through the builder form.response.answer(ask)returnsNoulAnswer,ChoiceAnswer<E>, orScoreAnswer.Answers to the issue's open questions
Ask.Ask<NoulAnswer>, consistent with choice and score returning their answer records.CriteriaQuestionSet: unchanged; its keys are generated at runtime and stay on String keys.Design
Askis a sealed abstract class whose subtypesNoulAsk,ChoiceAsk<E>, andScoreAskmirror the question types:read, soanswer()has no unchecked casts.question()is typed on each subtype (NoulAsk.question()is aNoul), andChoiceAsk.labels()exposes the label type.switchover anAskis exhaustive.We also compared a single final class and a sealed interface with records. The interface version needs a pattern
switchinanswer(), which doesn't compile at the SDK's Java 17 target, plus an unchecked cast. The final class can't expose the question's type or its labels.Runtime checks
ChoiceAskrejects a label that isn't a constant of its enum when the ask is created. Before, the mismatch only surfaced when the answer was read (Choice's constructor accepts any label for anyE).Askstill replace each other, so existing behavior is unchanged.Askthat wasn't in the request fails like reading a missing key.Also fixed
Builder.state(key, value)now starts an object state when none is set, instead of throwingIllegalStateException. The issue's builder example relied on this.systemOne(state)doesn't compile, because the state-plus-asks overloads require at least one ask.README
The Use section now leads with asks, and typed choices fold into a new Asks section. The string-keyed form moves to "Keyed questions", described as the form shared with the Python and JavaScript SDKs and the one to use for keys known only at runtime. The starter README's example now uses an ask.
The enum example now uses a switch expression. The old statement form's "a missing case is a compile error" comment wasn't true on Java 17: only a switch expression must cover every constant.
Testing
./gradlew buildpasses: 61 SDK tests, 8 new, plus the Spring Boot starter.Choiceof a different enum, the String overload given an enumChoice, and readinganswer(DEPT).choice()as another enum.javac --release 17.