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.Guess
It should do the following:
- Read the user input from the console
- Check its length
- Check if it's a real word using the
Set Text
representing 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
readLine
readLine
We 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.Guess
basic.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.