A set of questions asked together, answered at exactly the right types.
One request carrying several questions is both cheaper and faster than one request each, and it is the shape the service is designed around. Ask keeps that batching while preserving each question's own answer type:
val (urgent, dept, mood) = client.ask(
Ask(
Question.noul("Does this convey urgency?"),
Question.choice[Dept]("Which team should handle this?"),
Question.score("How frustrated?", Seq("Calm", "Cross", "Livid"))
),
state
)
urgent is a NoulAnswer, dept a ChoiceAnswer[Dept] and mood a ScoreAnswer -- destructured positionally, with no casts and no lookups by string. The map-based algebra.Evaluation remains available for cases where the questions are not known statically.
Question keys are generated positionally, so callers never name them. Use withKeys if you need them to match something else.
Project a response back into the positional answer tuple.
Project a response back into the positional answer tuple.
A refused question fails the whole projection with Verdict4sError.Refused, naming every refused question at once and carrying the full response, unless the question was built with orRefused, in which case it reads as None. Any other problem is reported as the first one found.
Its cast, and the one in Ask.apply, are justified by the same fact: the list is built by walking the very tuple that Ask.Answers is computed from, in the same order, so element i has the type the match type assigns to position i.