Tests vs Types
Colt Frederickson
@coltfred
Who am I?
- Senior Software Engineer at Rubicon Project
- Over 10 years working in the code mines
- Striving to make better software, always

Types?

Crappy Types?
public boolean equals(Object anObject) {...}List<? extends Number> l = new ArrayList<>();
l.add(new Integer(3)); //ERROR
l.add(new Double(3.3)); // ERRORprivate final List<Map<String,Object>> myList =
new ArrayList<Map<String,Object>>();
Crappy Types?
public class Person {
private String firstName;
private String lastName;
@Override public String toString() {
return "Person{" +
"firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
'}';
}
@Override public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Person person = (Person) o;
if (firstName != null ? !firstName.equals(person.firstName) : person.firstName != null)
return false;
return !(lastName != null ? !lastName.equals(person.lastName) : person.lastName != null);
}
@Override public int hashCode() {
int result = firstName != null ? firstName.hashCode() : 0;
result = 31 * result + (lastName != null ? lastName.hashCode() : 0);
return result;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}Crappy Types?

No Types?
>>> ["foo", "bar", "baz"].index(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: 1 is not in list
No Types?
>>> x = "hello"
>>> 1 + x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s)
for +: 'int' and 'str'
Types are just labels!
Text

What are good types?
- Implicit
- Terse
- Easy to use for prototyping
- Self documenting
- Easy for people to read
Implicit
scala> val x = 1
x: Int = 1
λ: let x = 100
λ: :t x
x :: Num a => a
addOne x = x + 1
λ: :t addOne
addOne :: Num a => a -> a
λ: addOne 100
101
λ: addOne 100.01
101.01
Terse
addOne x = x + 1
-- Or even shorter
addOne = (+) 1data Maybe a = Nothing | Just a
data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday case class Person(firstName:String, lastName:String)Prototyping
def getFilesInDir(dir: Path) = ???
def getFilesInPath(dirOrFile: Path) =
if(dirOrFile.isDir) getFilesInDir(dir) else List(dirOrFile)doit x y = undefined
f x y
| x > 1 = doit x y
| otherwise = x + y
--Example usage
*Main λ: f 1 100
101
*Main λ: f 2 100
*** Exception: Prelude.undefined
Self Documenting
def getFilesDir(dir: Path): List[Path] = ???
def getFilesInPath(dirOrFile: Path): List[Path] =
if(dirOrFile.isDir) getFiles(dir) else List(dirOrFile)*Main λ: :t elemIndex
elemIndex :: Eq a => a -> [a] -> Maybe Int
-- Will fail to compile
elemIndex 1 ["foo", "bar", "baz"]
-- But these work great!
*Main λ: elemIndex "foo" ["foo", "bar", "baz"]
Just 0
*Main λ: elemIndex "foobar" ["foo", "bar", "baz"]
Nothing
I came here for a fight!

Let's begin!
Given a list and a value, return the index of the value in the list or signify that it is not found.
- Documentation
- Example usage
- Some of legal values
- All legal values
- Specification
- Works for one case
- Returned value is valid
- Common Errors
- No typos
- No unexpected null
- Caller deals with failure
- Guarantees
- Forced to call with correct type
- No side effects
- No exceptions
- No infinite loops
The Contenders
- Python
- Python + Tests
- Haskell
- Haskell + Type Annotation
- Haskell + Type Annotation + Tests
- Idris
- Idris + Tests

Python w/o Tests
def find(xs, x):
#implementation omitted - Documentation
Example usageSome of legal valuesAll legal values
- Specification
Works for one caseReturned value is valid
- Common Errors
No TyposNo Unexpected nullCaller deals with failure
- Guarantees
Forced to call with correct typeNo side effectsNo ExceptionsNo infinite loops
Python with Tests
def testHappy():
assert find(["foo", "bar"], "bar") == 1
def testSad():
assert find(["foo", "bar"], "baz") is None- Documentation
- Example usage
- Some of legal values
All legal values
- Specification
- Works for one case
Returned value is valid
- Common Errors
No TyposNo Unexpected nullCaller deals with failure
- Guarantees
Forced to call with correct typeNo side effectsNo ExceptionsNo infinite loops
Haskell
find xs x =
--Implementation ommitted- Documentation
Example usageSome of legal valuesAll legal values
- Specification
Works for one caseReturned value is valid
- Common Errors
- No Typos
- No Unexpected null
- Caller deals with failure
- Guarantees
- Forced to call with correct type
- No side effects
No ExceptionsNo infinite loops
Haskell + Annotation
find :: Eq a => [a] -> a -> Maybe Int
find xs x =
--Implementation ommitted- Documentation
Example usage- Some of legal values
- All legal values
- Specification
Works for one caseReturned value is valid
- Common Errors
- No Typos
- No Unexpected null
- Caller deals with failure
- Guarantees
- Forced to call with correct type
- No side effects
No ExceptionsNo infinite loops
Haskell + Annotation + Test
testHappy = assertEqual "Should find foo" (Just 0) (find ["foo","bar"] "foo")
testSad = assertEqual "Should not find baz" (Nothing) (find ["foo","bar"] "baz")- Documentation
- Example usage
- Some of legal values
- All legal values
- Specification
- Works for one case
Returned value is valid
- Common Errors
- No Typos
- No Unexpected null
- Caller deals with failure
- Guarantees
- Forced to call with correct type
- No side effects
No ExceptionsNo infinite loops
Idris
%default total
find : Eq a => Vect n a -> a -> Maybe (Fin n)
find xs x = ...- Documentation
Example usage- Some of legal values
- All legal values
- Specification
Works for one caseReturned value is valid
- Common Errors
- No Typos
- No Unexpected null
- Caller deals with failure
- Guarantees
- Forced to call with correct type
- No side effects
- No Exceptions
- No infinite loops
Idris + Tests
ex : find ["foo", "bar"] "bar" = Just 1
ex = Refl- Documentation
- Example usage
- Some of legal values
- All legal values
- Specification
- Works for one case
Returned value is valid
- Common Errors
- No Typos
- No Unexpected null
- Caller deals with failure
- Guarantees
- Forced to call with correct type
- No side effects
- No Exceptions
- No infinite loops
Summary
| Python | Python + Tests | Haskell | Haskell + Annotations | Haskell + Tests | Idris | Idris + Tests | |
|---|---|---|---|---|---|---|---|
| Example Usage | |||||||
| Some legal Values | |||||||
| All legal values | |||||||
| Works for one case | |||||||
| Returned value is valid | |||||||
| No typos | |||||||
| No unexpected null | |||||||
| Caller deals with failure. | |||||||
| Forced to call with correct type | |||||||
| No side effects | |||||||
| No exceptions | |||||||
| No infinite loops |




















































































Thank you!

Please don't throw things!
Tests vs Types
By Colt Frederickson
Tests vs Types
- 1,664