Types Lab
In this lab we’ll explore the idea of functional programming and data types. In functional programming, we think of functions as transformations. They take any number of parameters and they change them into a return value.
Remember the structure of a Python function?
def double(number):
return 2 * number
Here,
numberis the paramter and2 * numberis the return value.As its name suggests, this function returns twice,
number.
Here is a visualization of the function double. We are not so focused on how the function gets its
work done as we are on what goes in and what comes out:

This unlocks a powerful new way of breaking down problems: we can think about how functions could be connected together. How do you quadruple a number? Just double it, and then double the result:

def quadruple(number):
return double(double(number))
You will soon see that much more difficult problems can often be broken down into simpler functions by thinking about how each function transforms an input into an output.
Thinking about types
You have already met a bunch of different kinds of objects in Python: 1,
2.55, True, "pizza", None. Each of these has a type. When we
talk about what goes in to functions and what comes out, we won’t talk about
particular inputs.
Who cares if you double
7or9? The point is you can’t doubleFalse.
We’ll talk about the types of objects that functions operate on. Everything has a type. Here are a few types that you already know:
| Type | Examples | Description |
|---|---|---|
int |
145, -200 | Integers, just like you know them from math class :) |
float |
1.1115, 9.08 | Decimal numbers. They’re called float because of how they are stored |
str |
“apple”, “x” | Strings, or sequences of characters. Careful! 5 is an int but "5" is a str. |
bool |
True, False |
Booleans, or True/False values. Named for George Boole, who spent a lot of time thinking about them. |
list |
[1, 2, 3] |
A list holds other objects. |
✏️
Open ~/Desktop/making_with_code/pedprog/unit01/lab00/types_notes.md.
For each function in the table below, describe what type the function would take as an input,
and what type the function would return as output.
| Function | Input type(s) | Output type |
|---|---|---|
| Square root of a number | float |
float |
| Multiplication of two integers | ||
| Average of a list of numbers | ||
| A function that gives all the factors of a number | ||
| A function to check if a word is a curse word | ||
| A function that assigns a friendliness score to a sentence | ||
| A function that takes a website URL and returns the page content | ||
| A function that counts the number of sentences in a paragraph | ||
> |
||
not |
⚡✨
When you finish, save your work, commit it, and push your changes so that Chris can see them.
The basic workflow is still the same:
git statusto determine what has changegit addto add the files you’ve editedgit committo write a message about what you’ve changedgit pushto push your changes to GithubFrom now on, Chris will see your progress by checking what has been committed.
Building sentences with grammar
You already know how to simplify arithmetic expressions like (2 + 10) / (5 - 2) by applying functions:

In this lab, we’re going to think about language in the same way. Instead of
functions like add and subtract, which combine numbers, we will use
functions like noun_phrase, which combines words into parts of speech.
Instead of types like int and bool, we will be using new types like Verb
or NounPhrase. (You’ll learn how to create new types in the next unit.)

Here’s an example of how we can use these functions to build a sentence. These functions are very strict about what kinds of inputs they accept, because we want to make sure we don’t create ill-formed sentences like “The my mouse hungry milk milk milk.”

Checking types in Python
In order to write these functions in Python, we will need to check the types of
each function’s inputs. We will use the built-in functions isinstance and type
to check types, and assert to intentionally crash the program when a function
receives the wrong type.
isinstance(thing, a_type) checks whether thing is a_type, returning True
or False. Types exist in a hierarchy: PluralNoun is a particular kind of
Noun, and Noun is a particular kind of NounPhrase. When you use
isinstance, you are checking whether thing belongs to a_type or any of its
subtypes. If you want to check whether thing’s type exactly, use type to get
its type. For example:
>>> food = PluralNoun("potatoes")
>>> isinstance(food, PluralNoun)
True
>>> isinstance(food, Noun)
True
>>> isinstance(food, VerbPhrase)
False
>>> type(food)
<class 'PluralNoun'>
>>> type(food) == PluralNoun
True
>>> type(food) == Noun
False
assert condition will do nothing if condition is True, but it will crash
the program if condition is False. Try it out–Python will keep going after
an error in interactive mode, but an uncaught error will crash a script.
>>> assert 1 + 2 == 3
>>> assert 1 + 2 == 4
AssertionError
>>> assert isinstance(13, int)
>>> assert isinstance("cowboy", int)
AssertionError
With these tools, we can start writing grammar functions like the ones we saw above. For example:
def noun_phrase(adjective, np):
assert isinstance(adjective, Adjective)
assert isinstance(np, NounPhrase)
return NounPhrase(adjective + ' ' + np)
This is what you will do next.
Auto-poetry
Love poems
This lab’s repo contains four Python files:
grammar.pyhas a bunch of functions for combining and transforming grammatical types. These are unfinished; it’s your job to write them.poetry.pyhas functions for writing beautiful auto-poems. Oncegrammar.pyis complete, you will be able to generate poems. You don’t need to edit this file.vocabulary.pyhas a bunch of functions for picking random words. This is explored in the extension activity.grammatical_types.pydefines types likeNoun,Adjective, andTransitiveVerb. You won’t edit this file, but it is useful as a reference for parts of speech.
💻
Open grammar.py in Atom. This module is full of functions
which transform parts of speech. Let’s look at the first function:
|
|
- The docstring (line 21) describes this function’s type signature, or what goes in and
what comes out.
pluralizereceives aNounand returns aPluralNoun. - Line 22 checks the input type. If it’s not a
Noun, the program crashes. - Line 23 contains a conditional checking the final letters of the noun. Most English nouns are pluralized by adding “s” (“tree” becomes “trees”). But nouns ending in “s”, “ch”, “sh”, or “x” are pluralized by adding “es” (“beach” becomes “beaches”).
- Lines 24 and 26 create a new
PluralNounusing the appropriate ending. All the grammatical types can be combined like normal strings using the+operator.
💻
Now open poetry.py in Atom. Let’s look at love_poem:
|
|
- The docstring (line 2) says this function takes no inputs and returns a
Poem. pluralizeis already finished, and it’s the only function other thanrandom_wordwe’ll need. So this function will work!- See if you can predict what this function will do.
💻
Let’s try it out! Run python -i poetry.py to load the
poetry module. Now let’s have some love poems!
👾 💬
Runningpythonwith the-iflag means that you want to enter the interactive mode after running the module. This is useful, as in the example below, when you want to play around with all the variables and functions defined in a module.
>>> print(love_poem())
Roses are red
Violets are blue
Evenings are poisonous
And so are you.
Couplet
Before we can run couplet, the next kind of poem, we’ll need to implement a
few more grammar functions in grammar.py. Your task is to complete each of the
following functions. Right now, they just raise a NotImplementedError, which
crashes the program. Remove these and replace them with the needed code.
The sections below describe what each function should do.
noun_phrasedetermine_noun_phrasemake_definitemake_indefinite
💻 noun_phrase
>>> noun_phrase(Adjective("spooky"), Noun("closet"))
"spooky closet"
- Check that the first argument is an
Adjective. - Check that the second argument is a
NounPhrase. - Return a
NounPhrasecontaining the adjective added to the noun (don’t forget a space between them).
💻 determine_noun_phrase
>>> determine_noun_phrase(Determiner("that"), Noun("eagle"))
"that eagle"
- Check that the first argument is a
Determiner. - Check that the second argument is a
NounPhrase. - Return a
DeterminedNounPhrasecontaining the determiner added to the noun phrase (again, don’t forget a space between them).
💻 make_definite
>>> make_definite(NounPhrase("evil squirrel"))
"the evil squirrel"
- Check that the first argument is a
NounPhrase. - Use
determine_noun_phraseto combine the noun phrase withDeterminer("the"). - Return the
DeterminedNounPhraseyou created.
💻 make_indefinite
>>> make_indefinite(NounPhrase("evil squirrel"))
"an evil squirrel"
>>> make_indefinite(NounPhrase("squirrel"))
"a squirrel"
- Check that the first argument is a
NounPhrase. - Check whether the noun phrase starts with a vowel sound
(using
starts_with_vowel_sound, described above).- If so, use
Determiner("an") - If not, use
Determiner("a")
- If so, use
- Use
determine_noun_phraseto combine the noun phrase with the determiner. - Return the
DeterminedNounPhraseyou created.
💻
Once these functions are finished, run python -i poetry.py
again and try out the couplet function.
>>> print(couplet())
⚡✨
Once you havecoupletworking, it’s a good time to check in your work. Rungit statusto confirm that onlygrammar.pyhas changed. Rungit diffto see the changes you’ve made. If everything looks right,git add grammar.pyto add this file to the commit you’re preparing. Finally,git commit, write your commit message, andgit pushto push your commit to the server.
Limerick
There’s one more kind of poem, a limerick. We’ll need to implement a few more grammar rules. Your group will need to complete the following functions:
verb_phrasepase_tense_transitivepast_tense_intrasitiveverb_phrase_intrasitive
💻 verb_phrase
>>> verb_phrase(Adverb("easily"), VerbPhrase("crushed her enemies"))
"easily crushed her enemies"
- Check that the first argument is an
Adverb. - Check that the second argument is a
VerbPhrase. - Combine them into a
VerbPhrase. - Return the
VerbPhraseyou created.
💻 past_tense_transitive
>>> past_tense_transitive(VerbTransitive("avoid"))
"avoided"
- Check that the first argument is a
TransitiveVerb. Make sure it’s not aPastTenseTransitiveVerbor you might accidentally conjugate a verb twice, resulting in something like “avoideded”. - Choose an appropriate ending to conjugate the verb in the past tense. If the verb ends in ’e’, add ’d’. Otherwise, add ’ed'.
- This won’t work for irregular verbs (e.g. the past tense of ‘go’ is ‘went’), but let’s just ignore that for now. You can come back later and add logic for some irregular verbs if you’re feeling ambitious.
- Return a
PastTenseTransitiveVerbconsisting of the verb plus its new ending.
💻 past_tense_intransitive
>>> past_tense_intransitive(VerbTransitive("molt"))
"molted"
- Check that the first argument is an
IntransitiveVerb. Again, make sure it’s not aPastTenseIntransitiveVerb. - Choose an appropriate ending to conjugate the verb in the past tense. If the verb ends in ’e’, add ’d’. Otherwise, add ’ed’. Again, don’t worry about irregular verbs. The English language has only itself to blame for this mess.
- Return a
PastTenseIntransitiveVerbconsisting of the verb plus its new ending.
💻 verb_phrase_transitive
Intransitive verbs like “quit” are already verb phrases because they can form complete sentences (I quit). But transitive verbs like “take” need a noun phrase. You can’t just take, you have to take something.
>>> verb = past_tense_transitive(TransitiveVerb("transform"))
>>> np = make_definite(noun_phrase(Adjective("evil"), Noun("squirrel")))
>>> verb_phrase_transitive(verb, np)
"transformed the evil squirrel"
- Check that the first argument is a
TransitiveVerb. - Check that the second argument is a
NounPhrase. - Return a VerbPhrase consisting of the verb and the noun phrase.
💻
Once these functions are finished, run python -i poetry.py
again and try out the limerick function. Note that limerick has three
arguments, a name and two pronouns.
>>> print(limerick("Alex", "he", "his"))
⚡✨
Once you havelimerickworking, commit your changes and push them to the server.
If you found this lab interesting, you might be interested in exploring computational linguistics, or using CS to explore how language works. This lab just scratched the tiniest layer of the surface of this wonderful field. Here, we generated sentences. What about the reverse, trying to understand language produced by humans?
Extention: A New Kind of Poem
Let’s take a look at poetry.py. Each poem in poetry.py is a function utilizing the functions from grammar.py (which you’ve seen) and vocabulary.py (which you haven’t). The documentation below explains how to use the functions in vocabulary.py. To play with the examples, run
python -i vocabulary.py to open a Python shell and load the vocabulary module.
💻 Use these functions to define a new kind of poem, or another kind of text.
Here are some possible ideas:
- A new verse for a song you like
- Detailed insults
- Birthday wishes
- Weird recipes
- Dreams or nonsense stories
random_word
Returns one or more random words, matching the specified conditions.
Arguments:
- word_type (type): Must be one of
Noun,TransitiveVerb,IntransitiveVerb,Adjective, orAdverb. - count (int): Optional. How many words you want. Normally, the result of
random_wordis a word of the requested type, but whencountis provided, the result is a list of such words. You’ll learn about lists in the next lab. - rhymes_with (string): Optional. A word the result should rhyme with.
- meter (Meter): Optional. The pattern of stresses that should be in the result’s
pronounciation. A Meter is a string of digits, where
1represents a word’s main stress,2represents secondary stress, and0represents unstressed syllables. The meter used below,"1020", matches words whose sound is like “BUM-bah-Bum-bah.” Try saying ’territory’, ‘storyteller’, and ‘motorcycle’ out loud. TERR-i-Tor-y, STOR-y-Tell-er, MO-tor-Cy-cle. - syllables (int): Optional. The number of syllables that should be in the result.
Returns:
- A word of word_type. If
countis provided, returns a list of such words.
>>> random_word(Noun)
'bayonet'
>>> random_word(Noun, rhymes_with="soup")
'loop'
>>> random_word(Noun, syllables=6)
'microorganism'
>>> random_word(Noun, count=3, meter=Meter("1020"))
['territory', 'storyteller', 'motorcycle']
>>> random_word(Noun, rhymes_with="orange")
NoWordError: Couldn't find a Noun with conditions: rhymes with orange
>>>
rhyming_pair
Returns two rhyming words of the specified types.
Arguments:
- first_word_type (type): Must be one of
Noun,TransitiveVerb,IntransitiveVerb,Adjective, orAdverb. - second_word_type (type): Must be one of
Noun,TransitiveVerb,IntransitiveVerb,Adjective, orAdverb. - meter (Meter): Optional. The pattern of stresses that should be in the result’s pronounciation.
- syllables (int): Optional. The number of syllables that should be in the result.
Returns:
- Two words of (first_word_type, second_word_type). See the example usage for how to capture each return value in a separate variable.
>>> rhyming_pair(TransitiveVerb, IntransitiveVerb, meter=Meter("10"))
('fumble', 'grumble')
>>> adj, noun = rhyming_pair(Adjective, Noun, syllables=2)
>>> adj
'graphic'
>>> noun
'traffic'
count_syllables
Counts the syllables in a word.
Arguments:
- word (str)
Returns:
- An int, the number of syllables
>>> count_syllables("bogus")
2
get_meter
Returns the meter (the pattern of stresses in pronunciation) for a word.
Arguments:
- word (str)
Returns:
- Meter, a string of digits representing stresses in the word’s pronunciation.
1represents a word’s main stress,2represents secondary stress, and0represents unstressed syllables. The examples above show that the words are pronounced ‘A-ni-mal’ (‘100’), ‘HE-li-Cop-ter’ (‘1020’), and ‘In-ex-CUS-a-ble’ (‘20100’).
>>> get_meter("animal")
'100'
>>> get_meter("helicopter")
'1020'
>>> get_meter("inexcusable")
'20100'
starts_with_vowel_sound
Determines whether the word starts with a vowel sound.
Arguments:
- text (str)
Returns:
- bool,
Truewhen the first word oftextstarts with a vowel sound.Falseotherwise.
>>> starts_with_vowel_sound("horrible hounds")
False
>>> starts_with_vowel_sound("awesome owls")
True
⚡✨
If you decide to do the extension, add your changes to a new commit and push them to the server. For this commit, you will have changes inpoetry.pyand maybe alsogrammar.py.