While we can now create basic.Target and basic.Guess values and print the Result of their evaluation to the console, we're missing a key part of the Wordle experience! A user should not be able to guess a word that's shorter or longer than five characters, and the user should not be able to guess a nonsense word. If the user tries to do this, they should get a helpful message and have the opportunity to guess again.
Given the signature below, write the function, stubs.getUserInput:
stubs.getUserInput : '{IO, Exception, Ask (Set Text)} stubs.GuessIt should do the following:
- Read the user input from the console
- Check its length
- Check if it's a real word using the
Set Textrepresenting valid 5 letter words. - Let the user override the dictionary validation (pluralization and past-tense aren't well reflected)
- Check if it's a real word using the
- Continue to prompt the user until the user enters a valid guess
readLinereadLineWe learned how to print a response to the console with IO using printLine—now we need to get the user's response. The function for that is readLine from the base library.
readLine is a delayed computation, so it won't try to read the user's input unless we call it with the ! operator. The ! operator is syntactic sugar for calling a function with no arguments, () -> a.
Watch an example of running input and output in a loop:
Ask abilityAsk abilityIn the signature provided for stubs.getUserInput, we're using the Ask ability to represent the data we receive when we read the dictionary file into memory. Ask has one request constructor, ask which we can call when we need to inspect the dictionary data. Otherwise, the Ask ability requirement can simply be passed through the function call chain.
Use the Ask ability when you need to pass around data in an environment, but don't want to provide it explicitly as an argument to each upstream function.
Resources
Solutions
The implementation for basic.getUserInput is a series of if/else clauses. (Alternatively, you could use pattern matching.)
basic.getUserInput : '{IO, Exception, Ask (Set Text)} basic.Guessbasic.getUserInput : '{IO, Exception, Ask (Set Text)} basic.Guess
basic.getUserInput _ =
use Nat !=
use basic getUserInput
use basic.Guess fromText
input = readLine()
normalized = Text.normalize input
if Text.size normalized != 5 then
printLine "🖐 guess must be a 5 letter word"
getUserInput()
else
dict = ask
if Set.contains normalized dict then fromText normalized
else
acceptOverride = basic.overrideLookup normalized ()
if acceptOverride then fromText normalized else getUserInput()We have to call the readLine function with ! because it's a delayed computation and would otherwise never prompt the user for input. We then strip trailing whitespace and lowercase the input by default with Text.normalize.
The first condition that we check is the length of the user's input. If it's not 5, we start the basic.getUserInput function over again.
The second condition is if the user's guess is in the dictionary, represented by a Set of Text. We're using the Ask ability to provide this information. We'll provide the dictionary to the Ask ability in a later step.
Finally, we provide the user an override option with basic.overrideLookup. The dictionary provided doesn't do a great job at identifying pluralized versions of words or past-tense verb forms. (See the challenge options for some ideas about how to solve this.)
Only if the word is 5 letters long, present in the dictionary Set, or if the user accepts the override will we then call the basic.Guess.fromText function we wrote earlier.