Last active
November 14, 2018 08:29
-
-
Save noelwelsh/179cf1192835c59cbdfa85872d11f114 to your computer and use it in GitHub Desktop.
A simple questionnaire system
This file contains 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
scalaVersion := "2.12.7" |
This file contains 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
final case class Id(value: String) extends AnyVal | |
sealed trait Question[A] { | |
def id: Id | |
def prompt: String | |
} | |
object Question { | |
final case class IntQuestion(id: Id, prompt: String) extends Question[Int] | |
final case class StringQuestion(id: Id, prompt: String) extends Question[String] | |
final case class MultipleChoiceQuestion(id: Id, prompt: String, choices: List[String]) extends Question[String] | |
def int(id: Id, prompt: String): Question[Int] = | |
IntQuestion(id, prompt) | |
def string(id: Id, prompt: String): Question[String] = | |
StringQuestion(id, prompt) | |
def multipleChoice(id: Id, prompt: String, choices: List[String]): Question[String] = | |
MultipleChoiceQuestion(id, prompt, choices) | |
} | |
sealed trait Questionnaire[A] { | |
import Questionnaire._ | |
def :::[B](head: Question[B]): Questionnaire[(B, A)] = | |
CompoundQuestionnaire(head, this) | |
} | |
object Questionnaire { | |
final case object EmptyQuestionnaire extends Questionnaire[Unit] | |
final case class CompoundQuestionnaire[A,B]( | |
head: Question[A] , | |
tail: Questionnaire[B] | |
) extends Questionnaire[(A, B)] | |
val empty: Questionnaire[Unit] = EmptyQuestionnaire | |
} | |
sealed trait Answer[A] { | |
def id: Id | |
def value: A | |
} | |
object Answer { | |
final case class IntAnswer(id: Id, value: Int) extends Answer[Int] | |
final case class StringAnswer(id: Id, value: String) extends Answer[String] | |
def int(id: Id, value: Int): Answer[Int] = | |
IntAnswer(id, value) | |
def string(id: Id, value: String): Answer[String] = | |
StringAnswer(id, value) | |
} | |
sealed trait Answers[A] { | |
import Answers._ | |
def :::[B](head: Answer[B]): Answers[(B, A)] = | |
CompoundAnswers(head, this) | |
} | |
object Answers { | |
final case object EmptyAnswers extends Answers[Unit] | |
final case class CompoundAnswers[A,B]( | |
head: Answer[A], | |
tail: Answers[B] | |
) extends Answers[(A, B)] | |
val empty: Answers[Unit] = EmptyAnswers | |
} | |
trait Interpreter { | |
def apply[A](questionnaire: Questionnaire[A]): Answers[A] | |
} | |
object DummyInterpreter extends Interpreter { | |
def ask[A](question: Question[A]): Answer[A] = { | |
import Question._ | |
question match { | |
case IntQuestion(id, _) => Answer.int(id, 42) | |
case StringQuestion(id, _) => Answer.string(id, "Yeah!") | |
case MultipleChoiceQuestion(id, _, c) => Answer.string(id, c.head) | |
} | |
} | |
def apply[A](questionnaire: Questionnaire[A]): Answers[A] = { | |
import Answers._ | |
import Questionnaire._ | |
questionnaire match { | |
case EmptyQuestionnaire => EmptyAnswers | |
case CompoundQuestionnaire(hd, tl) => | |
ask(hd) ::: apply(tl) | |
} | |
} | |
} | |
object ConsoleInterpreter extends Interpreter { | |
def printPrompt(prompt: String, format: String): Unit = { | |
println(prompt) | |
print(s"$format >") | |
} | |
def ask[A](question: Question[A]): Answer[A] = { | |
import Question._ | |
import scala.io.StdIn | |
question match { | |
case IntQuestion(id, p) => | |
printPrompt(p, "int") | |
Answer.int(id, StdIn.readInt()) | |
case StringQuestion(id, p) => | |
printPrompt(p, "text") | |
Answer.string(id, StdIn.readLine()) | |
case MultipleChoiceQuestion(id, p, c) => | |
printPrompt(p ++ c.mkString("\n", "\n", ""), "multiple choice") | |
Answer.string(id, StdIn.readLine()) | |
} | |
} | |
def apply[A](questionnaire: Questionnaire[A]): Answers[A] = { | |
import Answers._ | |
import Questionnaire._ | |
questionnaire match { | |
case EmptyQuestionnaire => EmptyAnswers | |
case CompoundQuestionnaire(hd, tl) => | |
ask(hd) ::: apply(tl) | |
} | |
} | |
} | |
object Example { | |
val questionnaire = | |
Question.string(Id("Name"), "What is your name?") ::: | |
Question.int(Id("Year"), "What year were you born?") ::: | |
Question.multipleChoice(Id("Icecream"), "What is your favourite icecream flavour?", List("Vanilla", "Chocolate", "Pistachio", "Lemon")) ::: | |
Questionnaire.empty | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment