SlideShare ist ein Scribd-Unternehmen logo
1 von 63
Downloaden Sie, um offline zu lesen
Type-Directed TDD in Rust
A case study using FizzBuzz
Franklin Chen
http://franklinchen.com/
July 21, 2014
Pittsburgh Code and Supply
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 1 / 78
Outline
1 Introduction
2 Original FizzBuzz problem
3 FizzBuzz 2: user configuration
4 FizzBuzz 3: FizzBuzzPop and beyond
5 Parallel FizzBuzz
6 Conclusion
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 2 / 78
Outline
1 Introduction
2 Original FizzBuzz problem
3 FizzBuzz 2: user configuration
4 FizzBuzz 3: FizzBuzzPop and beyond
5 Parallel FizzBuzz
6 Conclusion
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 3 / 78
Goals of this presentation
Give a taste of a practical software development process that is:
test-driven
type-directed
Show everything for real (using Rust):
project build process
testing frameworks
all the code
Use FizzBuzz because:
problem: easy to understand
modifications: easy to understand
fun!
Encourage you to explore a modern typed language; now is the time!
Recently, Apple ditched Objective C for its new language Swift!
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 4 / 78
Test-driven development (TDD)
Think.
Write a test that fails.
Write code until test succeeds.
Repeat, and refactor as needed.
Is TDD dead?
Short answer: No.
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 5 / 78
Type systems
What is a type system?
A syntactic method to prove that bad things can’t happen.
“Debating” types “versus” tests?
Let’s use both types and tests!
But: use a good type system, not a bad one.
Some decent practical typed languages
OCaml: 20 years old
Haskell: 20 years old
Scala: 10 years old
Swift: < 2 months old
Rust (still not at 1.0!)
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 6 / 78
Outline
1 Introduction
2 Original FizzBuzz problem
3 FizzBuzz 2: user configuration
4 FizzBuzz 3: FizzBuzzPop and beyond
5 Parallel FizzBuzz
6 Conclusion
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 8 / 78
Original FizzBuzz problem
FizzBuzz defined
Write a program that prints the numbers from 1 to 100.
But for multiples of three, print “Fizz” instead of the number.
And for the multiples of five, print “Buzz”.
For numbers which are multiples of both three and five, print “FizzBuzz”.
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 9 / 78
Starter code: main driver
Rust: a modern systems programming language for efficiency and safety in
time, space, and concurrency.
fn main() {
// Will not compile yet!
for result in run_to_seq(1i, 100).iter() {
println!("{}", result)
}
}
Type-directed design: separate out effects (such as printing to
terminal) from the real work.
Type-directed feedback: compilation fails when something is not
implemented yet.
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 10 / 78
Compiling and testing with Cargo
Cargo: build tool for Rust
Features
Library dependency tracking.
cargo build
cargo test
My wish list, based on Scala SBT
Triggered compilation and testing
Interactive REPL
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 11 / 78
First compilation failure
src/main.rs:
$ cargo build
src/main.rs:16:19:
error: unresolved name ‘run_to_seq‘
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 12 / 78
Write type-directed stub
fn main() {
for result in run_to_seq(1i, 100).iter() {
println!("{}", result)
}
}
fn run_to_seq(start: int, end: int) -> Vec<String> {
fail!()
}
Write wanted type signature
fail! is convenient for stubbing.
In Rust standard library
Causes whole task to fail
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 13 / 78
Write acceptance test (simplified)
#[test]
fn test_1_to_16() {
let expected = vec![
"1", "2", "Fizz", "4", "Buzz", "Fizz",
"7", "8", "Fizz", "Buzz", "11", "Fizz",
"13", "14", "FizzBuzz", "16",
]
.iter()
.map(|&s| s.to_string())
.collect();
assert_eq!(run_to_seq(1, 16), expected)
}
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 14 / 78
Test passes type check, but fails
$ cargo test
task ’test::test_1_to_16’ failed at ’write run_to_seq’,
...src/main.rs:37
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 16 / 78
Outside-in: for a fizzbuzz module
Types are shapes to assemble logically.
fn run_to_seq(start: int, end: int) -> Vec<String> {
range_inclusive(start, end)
.map(fizzbuzz::evaluate)
.collect()
}
range(include, exclude) returns an iterator.
map takes an iterator of one type to an iterator of another:
Therefore: need to implement function
fizzbuzz::evaluate: int -> String.
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 17 / 78
Implement new fizzbuzz module
A failing acceptance test drives discovery of
A unit, fizzbuzz
A function with a particular type, int -> String
pub fn evaluate(i: int) -> String {
fail!()
}
Types are better than comments as documentation!
Comments are not checkable, unlike types and tests.
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 19 / 78
First part of unit test: example-based
Manually write some examples.
#[test] fn test_15() {
assert_eq!(evaluate(15), "FizzBuzz".to_string())
}
#[test] fn test_20() {
assert_eq!(evaluate(20), "Buzz".to_string())
}
#[test] fn test_6() {
assert_eq!(evaluate(6), "Fizz".to_string())
}
#[test] fn test_17() {
assert_eq!(evaluate(17), "17".to_string())
}
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 20 / 78
The joy of property-based tests
QuickCheck for Rust: a framework for writing property-based tests.
#[quickcheck]
fn multiple_of_both_3_and_5(i: int) -> TestResult {
if i % 3 == 0 && i % 5 == 0 {
TestResult::from_bool(evaluate(i) ==
"FizzBuzz".to_string())
} else {
TestResult::discard()
}
}
Winning features
Auto-generates random tests for each property (100 by default).
Type-driven: here, generates random int values.
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 21 / 78
Property-based tests (continued)
#[quickcheck]
fn multiple_of_only_3(i: int) -> TestResult {
if i % 3 == 0 && i % 5 != 0 {
TestResult::from_bool(evaluate(i) == "Fizz".to_string())
} else {
TestResult::discard()
}
}
#[quickcheck]
fn not_multiple_of_3_and_5(i: int) -> TestResult {
if i % 3 != 0 && i % 5 != 0 {
TestResult::from_bool(evaluate(i) == i.to_string())
} else {
TestResult::discard()
}
}
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 23 / 78
A buggy and ugly solution
// Buggy and ugly!
if i % 3 == 0 {
"Fizz".to_string()
} else if i % 5 == 0 {
"Buzz".to_string()
} else if i % 3 == 0 && i % 5 == 0 {
"FizzBuzz".to_string()
} else {
i.to_string()
}
$ cargo test
task ’fizzbuzz::test::test_15’ failed at
’assertion failed: ‘(left == right) && (right == left)‘
(left: ‘Fizz‘, right: ‘FizzBuzz‘)’, .../src/fizzbuzz.rs:21
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 24 / 78
Booleans are evil!
Maze of twisty little conditionals, all different
Too easy to write incorrect sequences of nested, combined
conditionals.
Overuse of Booleans is a type smell.
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 25 / 78
Pattern matching organizes information
pub fn evaluate(i: int) -> String {
match (i % 3 == 0, i % 5 == 0) {
(true, false) => "Fizz".to_string(),
(false, true) => "Buzz".to_string(),
(true, true) => "FizzBuzz".to_string(),
(false, false) => i.to_string(),
}
}
Winning features
Visual beauty and clarity.
No duplicated conditionals.
No ordering dependency.
Type checker verifies full coverage of cases.
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 27 / 78
Example of non-exhaustive pattern matching
pub fn evaluate(i: int) -> String {
match (i % 3 == 0, i % 5 == 0) {
(true, false) => "Fizz".to_string(),
(false, true) => "Buzz".to_string(),
(true, true) => "FizzBuzz".to_string(),
// (false, false) => i.to_string(),
}
}
$ cargo test
.../src/fizzbuzz.rs:16:5: 21:6 error:
non-exhaustive patterns: ‘(false, false)‘ not covered
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 28 / 78
Acceptance test passes
$ cargo test
test test::test_1_to_16 ... ok
Done?
No. Client wants more features.
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 30 / 78
Outline
1 Introduction
2 Original FizzBuzz problem
3 FizzBuzz 2: user configuration
4 FizzBuzz 3: FizzBuzzPop and beyond
5 Parallel FizzBuzz
6 Conclusion
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 31 / 78
Adding new features
Client wants to:
Choose two arbitrary divisors in place of 3 and 5
such as 4 and 7
Choose other arbitrary words in place of "Fizz" and "Buzz"
such as "Moo" and "Quack"
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 32 / 78
Type-driven refactoring
Types mean: refactoring is much more fun!
Add new tests.
Change types and code: to make new tests type check.
Refactor original code and tests: use new APIs.
Keep passing the old tests.
Delay writing code for new features.
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 33 / 78
More features means more types
Change fizzbuzz::evaluate to defaults::fizzbuzzer:
mod defaults;
fn run_to_seq(start: int, end: int) -> Vec<String> {
range_inclusive(start, end)
.map(defaults::fizzbuzzer)
.collect()
}
Add new types to FizzBuzz module:
pub type Pair<’a> = (int, &’a str);
pub struct Config<’a>(pub Pair<’a>, pub Pair<’a>);
pub fn evaluate(Config((d1, w1), (d2, w2)): Config, i: int)
-> String {
fail!()
}
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 34 / 78
New default configuration
// We can store a static Config as in C.
static fizzbuzzer_config: Config<’static> =
Config((3, "Fizz"),
(5, "Buzz"));
pub fn fizzbuzzer(i: int) -> String {
fizzbuzz::evaluate(fizzbuzzer_config, i)
}
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 35 / 78
More types means more tests
Write new property-based test over arbitrary user configurations:
#[quickcheck]
fn d1_but_not_d2((d1, w1): (int, String),
(d2, w2): (int, String),
i: int) -> TestResult {
let config = Config((d1, w1.as_slice()),
(d2, w2.as_slice()));
if i % d1 == 0 && i % d2 != 0 {
TestResult::from_bool(fizzbuzzer(i) == w1)
} else {
TestResult::discard()
}
}
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 36 / 78
Problem: coarse Config type
$ cargo test
task ’fizzbuzz::test::d1_but_not_d2’ failed at
’[quickcheck] TEST FAILED (runtime error).
Arguments: ((0, ), (0, ), 0)’
0 as a divisor crashes!
We discovered client’s underspecification.
Client says: meant to allow only divisors within 2 and 100.
We need to:
Add runtime validation when constructing Config.
Refine Config random generator.
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 37 / 78
Add (runtime) validation
Runtime precondition contract: Rust’s assert! (very primitive; fails a
task on failure):
static DIVISOR_MIN: int = 2; static DIVISOR_MAX: int = 100;
fn validate_pair(&(d, _): &Pair) {
assert!(d >= DIVISOR_MIN,
"divisor {} must be >= {}", d, DIVISOR_MIN);
assert!(d <= DIVISOR_MAX,
"divisor {} must be <= {}", d, DIVISOR_MAX);
}
impl<’a> Config<’a> {
pub fn new(pair1: Pair, pair2: Pair) -> Config {
validate_pair(&pair1); validate_pair(&pair2);
Config(pair1, pair2)
}
}
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 38 / 78
A note on error handling
Rust does not have exceptions!
Exceptions are evil because they escape the type system.
Rust task failures are brutal.
Outside scope of this presentation: principled type-based error
handling using Result<T, E>:
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 39 / 78
Some changes to defaults setup
// Cannot be static variable because of runtime
// validation and also use of Vector.
fn fizzbuzzer_config<’a>() -> Config<’a> {
Config::new(vec![(3, "Fizz"),
(5, "Buzz")])
}
pub fn fizzbuzzer(i: int) -> String {
fizzbuzz::evaluate(fizzbuzzer_config(), i)
}
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 41 / 78
Improve Config random generator
#[quickcheck]
fn d1_but_not_d2((d1, w1): (int, String),
(d2, w2): (int, String),
i: int) -> TestResult {
if (d1 >= DIVISOR_MIN && d1 <= DIVISOR_MAX)
&& (d2 >= DIVISOR_MIN && d2 <= DIVISOR_MAX) {
let config = Config::new(vec![(d1, w1.as_slice()),
(d2, w2.as_slice())]);
if i % d1 == 0 && i % d2 != 0 {
TestResult::from_bool(evaluate(config, i) == w1)
} else { TestResult::discard() }
} else { TestResult::discard() }
}
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 42 / 78
New test runs further, stills fails
Refactor old code to fizzbuzz::evaluate, to pass old tests and new
test.
pub fn evaluate(Config((d1, w1), (d2, w2)): Config, i: int)
-> String {
match (i % d1 == 0, i % d2 == 0) {
(true, false) => w1.to_string(),
(false, true) => w2.to_string(),
(true, true) => w1.to_string().append(w2),
(false, false) => i.to_string(),
}
}
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 43 / 78
Outline
1 Introduction
2 Original FizzBuzz problem
3 FizzBuzz 2: user configuration
4 FizzBuzz 3: FizzBuzzPop and beyond
5 Parallel FizzBuzz
6 Conclusion
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 44 / 78
Generalizing to more than two divisors
Client wants FizzBuzzPop!
Given three divisors (such as 3, 5, 7).
Given three words (such as "Fizz", "Buzz", "Pop").
Example: 21 should output "FizzPop".
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 45 / 78
More features means more tests
Write new tests for new defaults::fizzbuzzpopper:
#[test] fn test_fizzbuzzpopper_2() {
assert_eq!(fizzbuzzpopper(2), "2".to_string())
}
#[test] fn test_fizzbuzzpopper_21() {
assert_eq!(fizzbuzzpopper(21), "FizzPop".to_string())
}
#[test] fn test_fizzbuzzpopper_9() {
assert_eq!(fizzbuzzpopper(9), "Fizz".to_string())
}
#[test] fn test_fizzbuzzpopper_7() {
assert_eq!(fizzbuzzpopper(7), "Pop".to_string())
}
#[test] fn test_fizzbuzzpopper_35() {
assert_eq!(fizzbuzzpopper(35), "BuzzPop".to_string())
}
Change configuration: to Seq of pairs, instead of just two:Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 47 / 78
More tests means more (or changed) types
error: this function takes 2 parameters
but 1 parameter was supplied
Config::new(vec![(3, "Fizz"),
(5, "Buzz")])
Change type Config to allow a sequence of pairs:
pub struct Config(pub Vec<Pair>);
impl<’a> Config<’a> {
pub fn new(pairs: Vec<Pair>) -> Config {
for pair in pairs.iter() {
validate_pair(pair);
}
Config(pairs)
}
}
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 48 / 78
Fix remaining type errors
Refactoring reveals need to implement case of more than two divisors.
pub fn evaluate(Config(pairs): Config, i: int)
-> String {
// Can crash! And incorrect except for 2 pairs.
let (d1, w1) = pairs[0];
let (d2, w2) = pairs[1];
match (i % d1 == 0, i % d2 == 0) {
(true, false) => w1.to_string(),
(false, true) => w2.to_string(),
(true, true) => w1.to_string().append(w2),
(false, false) => i.to_string(),
}
}
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 49 / 78
More computation means more types
Associate each divisor with a “rule” that awaits input.
// Make the decision to allocate a String here.
fn rule(&(n, word): &Pair, i: int) -> String {
if i % n == 0 {
word.to_string()
} else {
String::new()
}
}
FizzBuzz demo time!
Two volunteers: each to play role of Rule.
One “manager” to combine two sub-results.
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 51 / 78
Assemble the types
pub fn evaluate(Config(pairs): Config, i: int)
-> String {
let combined: String = pairs.iter()
.map(|pair| rule(pair, i))
.fold(String::new(),
|result, s| result.append(s.as_slice()));
if combined.is_empty() {
i.to_string()
} else {
combined
}
}
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 53 / 78
A note on fold
For any value of type Iterator<A>, we can apply
fold: (B, |B, A| -> B) -> B.
Example: for Vec<String>, fold with string concatenation + returns the
concatenation of all the strings in the vector.
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 54 / 78
Test failure: coarse types again
$ cargo test
task ’fizzbuzz::test::d1_but_not_d2’ failed at
’[quickcheck] TEST FAILED.
Arguments: ((2, ), (3, ), 2)’
Demo time!
Configuration: vec![(3, ""), (5, "Buzz")]
Input: 9 (note: divisible by 2)
Output: should be "" (because of the part divisible by 3)
Output was: "9"
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 55 / 78
Assemble the types
pub fn evaluate(Config(pairs): Config, i: int)
-> String {
let combined: String = pairs.iter()
.map(|pair| rule(pair, i))
.fold(String::new(),
|result, s| result.append(s.as_slice()));
if combined.is_empty() {
i.to_string()
} else {
combined
}
}
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 56 / 78
Property-based testing rescued us again!
Be honest: would you have caught this bug manually?
I didn’t.
I never wrote FizzBuzzPop examples testing empty strings.
Property-based testing reveals unexpected corner cases.
(Empty “fizz” and “buzz” word strings).
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 57 / 78
An empty string is not equivalent to no string
Presence of something “empty” is not equivalent to no thing.
Sending someone an empty email versus not sending any email.
Many programming languages get this wrong.
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 58 / 78
Option<A> type
Option<A> is one of two possibilities:
None
Some(a) wraps a value a of type A.
For example, Some(String::empty()) is not the same as None.
let fizzFor3 = Some(String::new()) // multiple of 3
let buzzFor3 = None // not multiple of 5
let fizzbuzzFor3 = Some(String::new()) // fizzed ""
let fizzFor2 = None // not multiple of 3
let buzzFor2 = None // not multiple of 5
let fizzbuzzFor2 = None // not multiple of any
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 59 / 78
Cleaning up the types
// rule type was: |&Pair, int| -> String
// changed to: |&Pair, int| -> Option<String>
Useful type errors:
mismatched types: expected ‘Option<String>‘ but found
‘String‘
if i % n == 0 {
word.to_string()
} else {
String::new()
}
failed to find an implementation of trait Str for
Option<String>
result + s
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 60 / 78
Fix the type errors: our rule builder
fn rule(&(n, ref word): &Pair, i: int) -> Option<String> {
if i % n == 0 {
Some(word.to_string())
} else {
None
}
}
Demo time!
(Instructions: for result Some(s), hold up the string, else don’t hold
up anything)
Configuration: vec![(3, ""), (5, "Buzz")]
Input: 9
Output: now correctly is ""
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 61 / 78
Fix the type errors: our compiler
pub fn evaluate(Config(pairs): Config, i: int)
-> String {
let combined: Option<String> = pairs.iter()
.map(|pair| rule(pair, i))
.fold(None, add_option);
combined.unwrap_or_else(|| i.to_string())
}
We need to write: addOption
Rust standard library provides: unwrap_or_else
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 62 / 78
“Addition” for Option[String]
fn add_option(a1: Option<String>, a2: Option<String>)
-> Option<String> {
match (a1, a2) {
(Some(s1), None) => Some(s1),
(None, Some(s2)) => Some(s2),
(Some(s1), Some(s2)) => Some(s1.append(s2)),
(None, None) => None,
}
}
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 63 / 78
Getting A back out of Option<A>
Do not lose information!
unwrap_or_else inspects the and either
returns the value v inside a Some(v),
or else returns the value from a closure.
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 64 / 78
Transform information; don’t destroy it
Our complete code only uses if in one place.
Bug cause: destroying information, using if
if i % n == 0 { word } else { String::new() }
if combined.is_empty() {i.to_string()} else {combined}
Transforming information
To Option[String]:
if i % n == 0 { Some(word.to_string()) } else { None }
Back to String: combined.unwrap_or_else(|| i.to_string())
Type-directed design tip
We could have saved trouble up front, by using precise types.
Avoid if, when possible.
Avoid String (but required at I/O boundaries of program).
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 67 / 78
Outline
1 Introduction
2 Original FizzBuzz problem
3 FizzBuzz 2: user configuration
4 FizzBuzz 3: FizzBuzzPop and beyond
5 Parallel FizzBuzz
6 Conclusion
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 68 / 78
Parallelism
Some easy parallelism possible (not yet in Rust standard libraries):
Use of map.
Use of fold: parallelizable because of the monoid property:
Option<String> is a Monoid
There is an identity element (None).
There is a binary associative operator (add_option).
Fantastically important in practice!
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 69 / 78
Final (hypothetical) parallelized code
pub fn evaluate(Config(pairs): Config, i: int)
-> String {
pairs.par
.iter()
.map(|pair| rule(pair, i))
.reduce(add_option)
.unwrap_or_else(|| i.to_string())
}
Coding style tip
This level of conciseness is not always best: maybe too “clever”?
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 70 / 78
Final demo!
Demo time!
Configuration:
vec![(3, "Fizz"), (5, "Buzz"), (7, "Pop"), (2, "Boom")]
Tree of volunteers to simulate concurrency:
Four at leaves.
Two “middle managers” each handling two leaves.
One top-level manager handling two middle managers.
Input: 42
Output: "FizzPopBoom"
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 71 / 78
Parallelism summary
We discovered a theoretical speedup for generalized FizzBuzz:
Sequential: O(n)
Parallel: O(log n) (given log n processors, and omitting some
technical subtleties)
Also, driver outer loop can be sped up:
Sequential loop on 1 to m: O(m)
Parallel loop: O(1) (given m processors)
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 72 / 78
Current (July 2014) Rust limitations
Rust closures: still limited (work in progress!!).
Scala/Swift/Haskell/etc. have unrestricted closures: less complex
types, easy staged compilation.
Needs standard libraries for parallelism, using concurrency primitives
such as spawn.
Need faster compiler, build system.
Need better test frameworks.
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 73 / 78
Outline
1 Introduction
2 Original FizzBuzz problem
3 FizzBuzz 2: user configuration
4 FizzBuzz 3: FizzBuzzPop and beyond
5 Parallel FizzBuzz
6 Conclusion
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 76 / 78
Conclusion
Tests are great.
Types are great.
Tests and types work hand in hand, driving design and program
evolution.
Modern typed languages such as Rust promote fun, correct
programming!
It’s a great time to be learning and using a modern typed language.
Code, slides, article
https://github.com/franklinchen/type-directed-tdd-rust
The article has more detail omitted in the presentation.
The hyperlinks in all provided PDFs are clickable.
Scala: https://github.com/franklinchen/
talk-on-type-directed-tdd-using-fizzbuzz
Swift: https://github.com/franklinchen/fizzbuzz-swift
Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 77 / 78

Weitere ähnliche Inhalte

Was ist angesagt?

The why and how of moving to php 7.x
The why and how of moving to php 7.xThe why and how of moving to php 7.x
The why and how of moving to php 7.xWim Godden
 
The why and how of moving to php 7.x
The why and how of moving to php 7.xThe why and how of moving to php 7.x
The why and how of moving to php 7.xWim Godden
 
The First C# Project Analyzed
The First C# Project AnalyzedThe First C# Project Analyzed
The First C# Project AnalyzedPVS-Studio
 
Of complicacy of programming, or won't C# save us?
Of complicacy of programming, or won't C# save us?Of complicacy of programming, or won't C# save us?
Of complicacy of programming, or won't C# save us?PVS-Studio
 
Picking Mushrooms after Cppcheck
Picking Mushrooms after CppcheckPicking Mushrooms after Cppcheck
Picking Mushrooms after CppcheckAndrey Karpov
 
Piloting processes through std IO at the Ruby Drink-up of Sophia, January 2012
Piloting processes through std IO at the Ruby Drink-up of Sophia, January 2012Piloting processes through std IO at the Ruby Drink-up of Sophia, January 2012
Piloting processes through std IO at the Ruby Drink-up of Sophia, January 2012rivierarb
 
Software Vulnerabilities in C and C++ (CppCon 2018)
Software Vulnerabilities in C and C++ (CppCon 2018)Software Vulnerabilities in C and C++ (CppCon 2018)
Software Vulnerabilities in C and C++ (CppCon 2018)Patricia Aas
 
Tdd with python unittest for embedded c
Tdd with python unittest for embedded cTdd with python unittest for embedded c
Tdd with python unittest for embedded cBenux Wei
 
Documenting Bugs in Doxygen
Documenting Bugs in DoxygenDocumenting Bugs in Doxygen
Documenting Bugs in DoxygenPVS-Studio
 
Checking the Source Code of FlashDevelop with PVS-Studio
Checking the Source Code of FlashDevelop with PVS-StudioChecking the Source Code of FlashDevelop with PVS-Studio
Checking the Source Code of FlashDevelop with PVS-StudioPVS-Studio
 
Print Testing
Print TestingPrint Testing
Print Testingdonwelch
 
2021.laravelconf.tw.slides2
2021.laravelconf.tw.slides22021.laravelconf.tw.slides2
2021.laravelconf.tw.slides2LiviaLiaoFontech
 
Analysis of the Trans-Proteomic Pipeline (TPP) project
Analysis of the Trans-Proteomic Pipeline (TPP) projectAnalysis of the Trans-Proteomic Pipeline (TPP) project
Analysis of the Trans-Proteomic Pipeline (TPP) projectPVS-Studio
 
Code Vulnerabilities & Attacks
Code Vulnerabilities & AttacksCode Vulnerabilities & Attacks
Code Vulnerabilities & AttacksMarcus Botacin
 
Fundamentals of programming angeli
Fundamentals of programming angeliFundamentals of programming angeli
Fundamentals of programming angelibergonio11339481
 

Was ist angesagt? (19)

The why and how of moving to php 7.x
The why and how of moving to php 7.xThe why and how of moving to php 7.x
The why and how of moving to php 7.x
 
The why and how of moving to php 7.x
The why and how of moving to php 7.xThe why and how of moving to php 7.x
The why and how of moving to php 7.x
 
The First C# Project Analyzed
The First C# Project AnalyzedThe First C# Project Analyzed
The First C# Project Analyzed
 
Of complicacy of programming, or won't C# save us?
Of complicacy of programming, or won't C# save us?Of complicacy of programming, or won't C# save us?
Of complicacy of programming, or won't C# save us?
 
Picking Mushrooms after Cppcheck
Picking Mushrooms after CppcheckPicking Mushrooms after Cppcheck
Picking Mushrooms after Cppcheck
 
Metaprogramming
MetaprogrammingMetaprogramming
Metaprogramming
 
Piloting processes through std IO at the Ruby Drink-up of Sophia, January 2012
Piloting processes through std IO at the Ruby Drink-up of Sophia, January 2012Piloting processes through std IO at the Ruby Drink-up of Sophia, January 2012
Piloting processes through std IO at the Ruby Drink-up of Sophia, January 2012
 
Gotchas and Stack Traces in Ruby
Gotchas and Stack Traces in RubyGotchas and Stack Traces in Ruby
Gotchas and Stack Traces in Ruby
 
Software Vulnerabilities in C and C++ (CppCon 2018)
Software Vulnerabilities in C and C++ (CppCon 2018)Software Vulnerabilities in C and C++ (CppCon 2018)
Software Vulnerabilities in C and C++ (CppCon 2018)
 
Guadalajara con 2012
Guadalajara con 2012Guadalajara con 2012
Guadalajara con 2012
 
Tdd with python unittest for embedded c
Tdd with python unittest for embedded cTdd with python unittest for embedded c
Tdd with python unittest for embedded c
 
Documenting Bugs in Doxygen
Documenting Bugs in DoxygenDocumenting Bugs in Doxygen
Documenting Bugs in Doxygen
 
Checking the Source Code of FlashDevelop with PVS-Studio
Checking the Source Code of FlashDevelop with PVS-StudioChecking the Source Code of FlashDevelop with PVS-Studio
Checking the Source Code of FlashDevelop with PVS-Studio
 
Print Testing
Print TestingPrint Testing
Print Testing
 
2021.laravelconf.tw.slides2
2021.laravelconf.tw.slides22021.laravelconf.tw.slides2
2021.laravelconf.tw.slides2
 
Analysis of the Trans-Proteomic Pipeline (TPP) project
Analysis of the Trans-Proteomic Pipeline (TPP) projectAnalysis of the Trans-Proteomic Pipeline (TPP) project
Analysis of the Trans-Proteomic Pipeline (TPP) project
 
Code Vulnerabilities & Attacks
Code Vulnerabilities & AttacksCode Vulnerabilities & Attacks
Code Vulnerabilities & Attacks
 
Bluespec @waseda
Bluespec @wasedaBluespec @waseda
Bluespec @waseda
 
Fundamentals of programming angeli
Fundamentals of programming angeliFundamentals of programming angeli
Fundamentals of programming angeli
 

Andere mochten auch

Servo: The parallel web engine
Servo: The parallel web engineServo: The parallel web engine
Servo: The parallel web engineBruno Abinader
 
Rust Workshop - NITC FOSSMEET 2017
Rust Workshop - NITC FOSSMEET 2017 Rust Workshop - NITC FOSSMEET 2017
Rust Workshop - NITC FOSSMEET 2017 pramode_ce
 
Rust: Unlocking Systems Programming
Rust: Unlocking Systems ProgrammingRust: Unlocking Systems Programming
Rust: Unlocking Systems ProgrammingC4Media
 
What the &~#@&lt;!? (Memory Management in Rust)
What the &~#@&lt;!? (Memory Management in Rust)What the &~#@&lt;!? (Memory Management in Rust)
What the &~#@&lt;!? (Memory Management in Rust)David Evans
 
Rust Programming Language
Rust Programming LanguageRust Programming Language
Rust Programming LanguageJaeju Kim
 
Machine Learning in Rust with Leaf and Collenchyma
Machine Learning in Rust with Leaf and CollenchymaMachine Learning in Rust with Leaf and Collenchyma
Machine Learning in Rust with Leaf and CollenchymaMichael Hirn
 
Kernel-Level Programming: Entering Ring Naught
Kernel-Level Programming: Entering Ring NaughtKernel-Level Programming: Entering Ring Naught
Kernel-Level Programming: Entering Ring NaughtDavid Evans
 
Hey! There's OCaml in my Rust!
Hey! There's OCaml in my Rust!Hey! There's OCaml in my Rust!
Hey! There's OCaml in my Rust!Kel Cecil
 
Ruby is Awesome and Rust is Awesome and Building a Game in Both is AWESOME
Ruby is Awesome and Rust is Awesome and Building a Game in Both is AWESOMERuby is Awesome and Rust is Awesome and Building a Game in Both is AWESOME
Ruby is Awesome and Rust is Awesome and Building a Game in Both is AWESOMEJulien Fitzpatrick
 
RUSTing -- Partially Ordered Rust Programming Ruminations
RUSTing -- Partially Ordered Rust Programming RuminationsRUSTing -- Partially Ordered Rust Programming Ruminations
RUSTing -- Partially Ordered Rust Programming RuminationsAngelo Corsaro
 
Present-perfect
Present-perfectPresent-perfect
Present-perfectVicky
 
Exploring type-directed, test-driven development: a case study using FizzBuzz
Exploring type-directed, test-driven development: a case study using FizzBuzzExploring type-directed, test-driven development: a case study using FizzBuzz
Exploring type-directed, test-driven development: a case study using FizzBuzzFranklin Chen
 
Mission mercury
Mission mercuryMission mercury
Mission mercuryLisa Baird
 
PRBS - Where YOU can make a difference
PRBS - Where YOU can make a differencePRBS - Where YOU can make a difference
PRBS - Where YOU can make a differencePRBS
 
Tmh5 rahasia-kitab-tujuh
Tmh5 rahasia-kitab-tujuhTmh5 rahasia-kitab-tujuh
Tmh5 rahasia-kitab-tujuhRidwan Gucci
 
Ch3, sec 1 matter
Ch3, sec 1 matterCh3, sec 1 matter
Ch3, sec 1 mattermshenry
 

Andere mochten auch (20)

Servo: The parallel web engine
Servo: The parallel web engineServo: The parallel web engine
Servo: The parallel web engine
 
Rust Workshop - NITC FOSSMEET 2017
Rust Workshop - NITC FOSSMEET 2017 Rust Workshop - NITC FOSSMEET 2017
Rust Workshop - NITC FOSSMEET 2017
 
Rust: Unlocking Systems Programming
Rust: Unlocking Systems ProgrammingRust: Unlocking Systems Programming
Rust: Unlocking Systems Programming
 
What the &~#@&lt;!? (Memory Management in Rust)
What the &~#@&lt;!? (Memory Management in Rust)What the &~#@&lt;!? (Memory Management in Rust)
What the &~#@&lt;!? (Memory Management in Rust)
 
OOP in Rust
OOP in RustOOP in Rust
OOP in Rust
 
Rust Programming Language
Rust Programming LanguageRust Programming Language
Rust Programming Language
 
Machine Learning in Rust with Leaf and Collenchyma
Machine Learning in Rust with Leaf and CollenchymaMachine Learning in Rust with Leaf and Collenchyma
Machine Learning in Rust with Leaf and Collenchyma
 
Kernel-Level Programming: Entering Ring Naught
Kernel-Level Programming: Entering Ring NaughtKernel-Level Programming: Entering Ring Naught
Kernel-Level Programming: Entering Ring Naught
 
Rust-lang
Rust-langRust-lang
Rust-lang
 
Hey! There's OCaml in my Rust!
Hey! There's OCaml in my Rust!Hey! There's OCaml in my Rust!
Hey! There's OCaml in my Rust!
 
Ruby is Awesome and Rust is Awesome and Building a Game in Both is AWESOME
Ruby is Awesome and Rust is Awesome and Building a Game in Both is AWESOMERuby is Awesome and Rust is Awesome and Building a Game in Both is AWESOME
Ruby is Awesome and Rust is Awesome and Building a Game in Both is AWESOME
 
RUSTing -- Partially Ordered Rust Programming Ruminations
RUSTing -- Partially Ordered Rust Programming RuminationsRUSTing -- Partially Ordered Rust Programming Ruminations
RUSTing -- Partially Ordered Rust Programming Ruminations
 
Present-perfect
Present-perfectPresent-perfect
Present-perfect
 
Exploring type-directed, test-driven development: a case study using FizzBuzz
Exploring type-directed, test-driven development: a case study using FizzBuzzExploring type-directed, test-driven development: a case study using FizzBuzz
Exploring type-directed, test-driven development: a case study using FizzBuzz
 
Rene descartes
Rene descartesRene descartes
Rene descartes
 
Mission mercury
Mission mercuryMission mercury
Mission mercury
 
PRBS - Where YOU can make a difference
PRBS - Where YOU can make a differencePRBS - Where YOU can make a difference
PRBS - Where YOU can make a difference
 
Tmh5 rahasia-kitab-tujuh
Tmh5 rahasia-kitab-tujuhTmh5 rahasia-kitab-tujuh
Tmh5 rahasia-kitab-tujuh
 
Cloud webinar final
Cloud webinar finalCloud webinar final
Cloud webinar final
 
Ch3, sec 1 matter
Ch3, sec 1 matterCh3, sec 1 matter
Ch3, sec 1 matter
 

Ähnlich wie Type-Directed TDD in Rust: a case study using FizzBuzz

O CPAN tem as ferramentas que você precisa para fazer TDD em Perl, o Coding D...
O CPAN tem as ferramentas que você precisa para fazer TDD em Perl, o Coding D...O CPAN tem as ferramentas que você precisa para fazer TDD em Perl, o Coding D...
O CPAN tem as ferramentas que você precisa para fazer TDD em Perl, o Coding D...Rodolfo Carvalho
 
Test-driven Development (TDD)
Test-driven Development (TDD)Test-driven Development (TDD)
Test-driven Development (TDD)Bran van der Meer
 
Bdd: Tdd and beyond the infinite
Bdd: Tdd and beyond the infiniteBdd: Tdd and beyond the infinite
Bdd: Tdd and beyond the infiniteGiordano Scalzo
 
Js fwdays unit tesing javascript(by Anna Khabibullina)
Js fwdays unit tesing javascript(by Anna Khabibullina)Js fwdays unit tesing javascript(by Anna Khabibullina)
Js fwdays unit tesing javascript(by Anna Khabibullina)Anna Khabibullina
 
JS Frameworks Day April,26 of 2014
JS Frameworks Day April,26 of 2014JS Frameworks Day April,26 of 2014
JS Frameworks Day April,26 of 2014DA-14
 
Why Rust? - Matthias Endler - Codemotion Amsterdam 2016
Why Rust? - Matthias Endler - Codemotion Amsterdam 2016Why Rust? - Matthias Endler - Codemotion Amsterdam 2016
Why Rust? - Matthias Endler - Codemotion Amsterdam 2016Codemotion
 
Static types on javascript?! Type checking approaches to ensure healthy appli...
Static types on javascript?! Type checking approaches to ensure healthy appli...Static types on javascript?! Type checking approaches to ensure healthy appli...
Static types on javascript?! Type checking approaches to ensure healthy appli...Arthur Puthin
 
Fuzzing: The New Unit Testing
Fuzzing: The New Unit TestingFuzzing: The New Unit Testing
Fuzzing: The New Unit TestingDmitry Vyukov
 
COSCUP2012: How to write a bash script like the python?
COSCUP2012: How to write a bash script like the python?COSCUP2012: How to write a bash script like the python?
COSCUP2012: How to write a bash script like the python?Lloyd Huang
 
Debugging concurrency programs in go
Debugging concurrency programs in goDebugging concurrency programs in go
Debugging concurrency programs in goAndrii Soldatenko
 
Whitebox testing of Spring Boot applications
Whitebox testing of Spring Boot applicationsWhitebox testing of Spring Boot applications
Whitebox testing of Spring Boot applicationsYura Nosenko
 
Lisp in a Startup: the Good, the Bad, and the Ugly
Lisp in a Startup: the Good, the Bad, and the UglyLisp in a Startup: the Good, the Bad, and the Ugly
Lisp in a Startup: the Good, the Bad, and the UglyVsevolod Dyomkin
 
DSR Testing (Part 1)
DSR Testing (Part 1)DSR Testing (Part 1)
DSR Testing (Part 1)Steve Upton
 
Testing in Python: doctest and unittest (Updated)
Testing in Python: doctest and unittest (Updated)Testing in Python: doctest and unittest (Updated)
Testing in Python: doctest and unittest (Updated)Fariz Darari
 

Ähnlich wie Type-Directed TDD in Rust: a case study using FizzBuzz (20)

Basic TDD moves
Basic TDD movesBasic TDD moves
Basic TDD moves
 
O CPAN tem as ferramentas que você precisa para fazer TDD em Perl, o Coding D...
O CPAN tem as ferramentas que você precisa para fazer TDD em Perl, o Coding D...O CPAN tem as ferramentas que você precisa para fazer TDD em Perl, o Coding D...
O CPAN tem as ferramentas que você precisa para fazer TDD em Perl, o Coding D...
 
Test-driven Development (TDD)
Test-driven Development (TDD)Test-driven Development (TDD)
Test-driven Development (TDD)
 
Bdd: Tdd and beyond the infinite
Bdd: Tdd and beyond the infiniteBdd: Tdd and beyond the infinite
Bdd: Tdd and beyond the infinite
 
Js fwdays unit tesing javascript(by Anna Khabibullina)
Js fwdays unit tesing javascript(by Anna Khabibullina)Js fwdays unit tesing javascript(by Anna Khabibullina)
Js fwdays unit tesing javascript(by Anna Khabibullina)
 
JS Frameworks Day April,26 of 2014
JS Frameworks Day April,26 of 2014JS Frameworks Day April,26 of 2014
JS Frameworks Day April,26 of 2014
 
Why Rust? - Matthias Endler - Codemotion Amsterdam 2016
Why Rust? - Matthias Endler - Codemotion Amsterdam 2016Why Rust? - Matthias Endler - Codemotion Amsterdam 2016
Why Rust? - Matthias Endler - Codemotion Amsterdam 2016
 
Static types on javascript?! Type checking approaches to ensure healthy appli...
Static types on javascript?! Type checking approaches to ensure healthy appli...Static types on javascript?! Type checking approaches to ensure healthy appli...
Static types on javascript?! Type checking approaches to ensure healthy appli...
 
Unit testing - A&BP CC
Unit testing - A&BP CCUnit testing - A&BP CC
Unit testing - A&BP CC
 
C# to python
C# to pythonC# to python
C# to python
 
Fuzzing: The New Unit Testing
Fuzzing: The New Unit TestingFuzzing: The New Unit Testing
Fuzzing: The New Unit Testing
 
COSCUP2012: How to write a bash script like the python?
COSCUP2012: How to write a bash script like the python?COSCUP2012: How to write a bash script like the python?
COSCUP2012: How to write a bash script like the python?
 
Debugging concurrency programs in go
Debugging concurrency programs in goDebugging concurrency programs in go
Debugging concurrency programs in go
 
Whitebox testing of Spring Boot applications
Whitebox testing of Spring Boot applicationsWhitebox testing of Spring Boot applications
Whitebox testing of Spring Boot applications
 
Lisp in a Startup: the Good, the Bad, and the Ugly
Lisp in a Startup: the Good, the Bad, and the UglyLisp in a Startup: the Good, the Bad, and the Ugly
Lisp in a Startup: the Good, the Bad, and the Ugly
 
Python and test
Python and testPython and test
Python and test
 
DSR Testing (Part 1)
DSR Testing (Part 1)DSR Testing (Part 1)
DSR Testing (Part 1)
 
Golang dot-testing-lite
Golang dot-testing-liteGolang dot-testing-lite
Golang dot-testing-lite
 
Testing in Python: doctest and unittest (Updated)
Testing in Python: doctest and unittest (Updated)Testing in Python: doctest and unittest (Updated)
Testing in Python: doctest and unittest (Updated)
 
report
reportreport
report
 

Kürzlich hochgeladen

Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackVICTOR MAESTRE RAMIREZ
 
Hand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxHand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxbodapatigopi8531
 
Asset Management Software - Infographic
Asset Management Software - InfographicAsset Management Software - Infographic
Asset Management Software - InfographicHr365.us smith
 
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio, Inc.
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdfWave PLM
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityNeo4j
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software DevelopersVinodh Ram
 
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdfThe Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdfkalichargn70th171
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...gurkirankumar98700
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...kellynguyen01
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsAlberto González Trastoy
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...Christina Lin
 
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataAdobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataBradBedford3
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...OnePlan Solutions
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Modelsaagamshah0812
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...soniya singh
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVshikhaohhpro
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...stazi3110
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)OPEN KNOWLEDGE GmbH
 

Kürzlich hochgeladen (20)

Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStack
 
Hand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxHand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptx
 
Exploring iOS App Development: Simplifying the Process
Exploring iOS App Development: Simplifying the ProcessExploring iOS App Development: Simplifying the Process
Exploring iOS App Development: Simplifying the Process
 
Asset Management Software - Infographic
Asset Management Software - InfographicAsset Management Software - Infographic
Asset Management Software - Infographic
 
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered Sustainability
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software Developers
 
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdfThe Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
 
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataAdobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTV
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)
 

Type-Directed TDD in Rust: a case study using FizzBuzz

  • 1. Type-Directed TDD in Rust A case study using FizzBuzz Franklin Chen http://franklinchen.com/ July 21, 2014 Pittsburgh Code and Supply Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 1 / 78
  • 2. Outline 1 Introduction 2 Original FizzBuzz problem 3 FizzBuzz 2: user configuration 4 FizzBuzz 3: FizzBuzzPop and beyond 5 Parallel FizzBuzz 6 Conclusion Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 2 / 78
  • 3. Outline 1 Introduction 2 Original FizzBuzz problem 3 FizzBuzz 2: user configuration 4 FizzBuzz 3: FizzBuzzPop and beyond 5 Parallel FizzBuzz 6 Conclusion Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 3 / 78
  • 4. Goals of this presentation Give a taste of a practical software development process that is: test-driven type-directed Show everything for real (using Rust): project build process testing frameworks all the code Use FizzBuzz because: problem: easy to understand modifications: easy to understand fun! Encourage you to explore a modern typed language; now is the time! Recently, Apple ditched Objective C for its new language Swift! Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 4 / 78
  • 5. Test-driven development (TDD) Think. Write a test that fails. Write code until test succeeds. Repeat, and refactor as needed. Is TDD dead? Short answer: No. Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 5 / 78
  • 6. Type systems What is a type system? A syntactic method to prove that bad things can’t happen. “Debating” types “versus” tests? Let’s use both types and tests! But: use a good type system, not a bad one. Some decent practical typed languages OCaml: 20 years old Haskell: 20 years old Scala: 10 years old Swift: < 2 months old Rust (still not at 1.0!) Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 6 / 78
  • 7. Outline 1 Introduction 2 Original FizzBuzz problem 3 FizzBuzz 2: user configuration 4 FizzBuzz 3: FizzBuzzPop and beyond 5 Parallel FizzBuzz 6 Conclusion Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 8 / 78
  • 8. Original FizzBuzz problem FizzBuzz defined Write a program that prints the numbers from 1 to 100. But for multiples of three, print “Fizz” instead of the number. And for the multiples of five, print “Buzz”. For numbers which are multiples of both three and five, print “FizzBuzz”. Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 9 / 78
  • 9. Starter code: main driver Rust: a modern systems programming language for efficiency and safety in time, space, and concurrency. fn main() { // Will not compile yet! for result in run_to_seq(1i, 100).iter() { println!("{}", result) } } Type-directed design: separate out effects (such as printing to terminal) from the real work. Type-directed feedback: compilation fails when something is not implemented yet. Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 10 / 78
  • 10. Compiling and testing with Cargo Cargo: build tool for Rust Features Library dependency tracking. cargo build cargo test My wish list, based on Scala SBT Triggered compilation and testing Interactive REPL Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 11 / 78
  • 11. First compilation failure src/main.rs: $ cargo build src/main.rs:16:19: error: unresolved name ‘run_to_seq‘ Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 12 / 78
  • 12. Write type-directed stub fn main() { for result in run_to_seq(1i, 100).iter() { println!("{}", result) } } fn run_to_seq(start: int, end: int) -> Vec<String> { fail!() } Write wanted type signature fail! is convenient for stubbing. In Rust standard library Causes whole task to fail Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 13 / 78
  • 13. Write acceptance test (simplified) #[test] fn test_1_to_16() { let expected = vec![ "1", "2", "Fizz", "4", "Buzz", "Fizz", "7", "8", "Fizz", "Buzz", "11", "Fizz", "13", "14", "FizzBuzz", "16", ] .iter() .map(|&s| s.to_string()) .collect(); assert_eq!(run_to_seq(1, 16), expected) } Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 14 / 78
  • 14. Test passes type check, but fails $ cargo test task ’test::test_1_to_16’ failed at ’write run_to_seq’, ...src/main.rs:37 Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 16 / 78
  • 15. Outside-in: for a fizzbuzz module Types are shapes to assemble logically. fn run_to_seq(start: int, end: int) -> Vec<String> { range_inclusive(start, end) .map(fizzbuzz::evaluate) .collect() } range(include, exclude) returns an iterator. map takes an iterator of one type to an iterator of another: Therefore: need to implement function fizzbuzz::evaluate: int -> String. Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 17 / 78
  • 16. Implement new fizzbuzz module A failing acceptance test drives discovery of A unit, fizzbuzz A function with a particular type, int -> String pub fn evaluate(i: int) -> String { fail!() } Types are better than comments as documentation! Comments are not checkable, unlike types and tests. Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 19 / 78
  • 17. First part of unit test: example-based Manually write some examples. #[test] fn test_15() { assert_eq!(evaluate(15), "FizzBuzz".to_string()) } #[test] fn test_20() { assert_eq!(evaluate(20), "Buzz".to_string()) } #[test] fn test_6() { assert_eq!(evaluate(6), "Fizz".to_string()) } #[test] fn test_17() { assert_eq!(evaluate(17), "17".to_string()) } Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 20 / 78
  • 18. The joy of property-based tests QuickCheck for Rust: a framework for writing property-based tests. #[quickcheck] fn multiple_of_both_3_and_5(i: int) -> TestResult { if i % 3 == 0 && i % 5 == 0 { TestResult::from_bool(evaluate(i) == "FizzBuzz".to_string()) } else { TestResult::discard() } } Winning features Auto-generates random tests for each property (100 by default). Type-driven: here, generates random int values. Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 21 / 78
  • 19. Property-based tests (continued) #[quickcheck] fn multiple_of_only_3(i: int) -> TestResult { if i % 3 == 0 && i % 5 != 0 { TestResult::from_bool(evaluate(i) == "Fizz".to_string()) } else { TestResult::discard() } } #[quickcheck] fn not_multiple_of_3_and_5(i: int) -> TestResult { if i % 3 != 0 && i % 5 != 0 { TestResult::from_bool(evaluate(i) == i.to_string()) } else { TestResult::discard() } } Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 23 / 78
  • 20. A buggy and ugly solution // Buggy and ugly! if i % 3 == 0 { "Fizz".to_string() } else if i % 5 == 0 { "Buzz".to_string() } else if i % 3 == 0 && i % 5 == 0 { "FizzBuzz".to_string() } else { i.to_string() } $ cargo test task ’fizzbuzz::test::test_15’ failed at ’assertion failed: ‘(left == right) && (right == left)‘ (left: ‘Fizz‘, right: ‘FizzBuzz‘)’, .../src/fizzbuzz.rs:21 Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 24 / 78
  • 21. Booleans are evil! Maze of twisty little conditionals, all different Too easy to write incorrect sequences of nested, combined conditionals. Overuse of Booleans is a type smell. Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 25 / 78
  • 22. Pattern matching organizes information pub fn evaluate(i: int) -> String { match (i % 3 == 0, i % 5 == 0) { (true, false) => "Fizz".to_string(), (false, true) => "Buzz".to_string(), (true, true) => "FizzBuzz".to_string(), (false, false) => i.to_string(), } } Winning features Visual beauty and clarity. No duplicated conditionals. No ordering dependency. Type checker verifies full coverage of cases. Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 27 / 78
  • 23. Example of non-exhaustive pattern matching pub fn evaluate(i: int) -> String { match (i % 3 == 0, i % 5 == 0) { (true, false) => "Fizz".to_string(), (false, true) => "Buzz".to_string(), (true, true) => "FizzBuzz".to_string(), // (false, false) => i.to_string(), } } $ cargo test .../src/fizzbuzz.rs:16:5: 21:6 error: non-exhaustive patterns: ‘(false, false)‘ not covered Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 28 / 78
  • 24. Acceptance test passes $ cargo test test test::test_1_to_16 ... ok Done? No. Client wants more features. Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 30 / 78
  • 25. Outline 1 Introduction 2 Original FizzBuzz problem 3 FizzBuzz 2: user configuration 4 FizzBuzz 3: FizzBuzzPop and beyond 5 Parallel FizzBuzz 6 Conclusion Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 31 / 78
  • 26. Adding new features Client wants to: Choose two arbitrary divisors in place of 3 and 5 such as 4 and 7 Choose other arbitrary words in place of "Fizz" and "Buzz" such as "Moo" and "Quack" Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 32 / 78
  • 27. Type-driven refactoring Types mean: refactoring is much more fun! Add new tests. Change types and code: to make new tests type check. Refactor original code and tests: use new APIs. Keep passing the old tests. Delay writing code for new features. Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 33 / 78
  • 28. More features means more types Change fizzbuzz::evaluate to defaults::fizzbuzzer: mod defaults; fn run_to_seq(start: int, end: int) -> Vec<String> { range_inclusive(start, end) .map(defaults::fizzbuzzer) .collect() } Add new types to FizzBuzz module: pub type Pair<’a> = (int, &’a str); pub struct Config<’a>(pub Pair<’a>, pub Pair<’a>); pub fn evaluate(Config((d1, w1), (d2, w2)): Config, i: int) -> String { fail!() } Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 34 / 78
  • 29. New default configuration // We can store a static Config as in C. static fizzbuzzer_config: Config<’static> = Config((3, "Fizz"), (5, "Buzz")); pub fn fizzbuzzer(i: int) -> String { fizzbuzz::evaluate(fizzbuzzer_config, i) } Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 35 / 78
  • 30. More types means more tests Write new property-based test over arbitrary user configurations: #[quickcheck] fn d1_but_not_d2((d1, w1): (int, String), (d2, w2): (int, String), i: int) -> TestResult { let config = Config((d1, w1.as_slice()), (d2, w2.as_slice())); if i % d1 == 0 && i % d2 != 0 { TestResult::from_bool(fizzbuzzer(i) == w1) } else { TestResult::discard() } } Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 36 / 78
  • 31. Problem: coarse Config type $ cargo test task ’fizzbuzz::test::d1_but_not_d2’ failed at ’[quickcheck] TEST FAILED (runtime error). Arguments: ((0, ), (0, ), 0)’ 0 as a divisor crashes! We discovered client’s underspecification. Client says: meant to allow only divisors within 2 and 100. We need to: Add runtime validation when constructing Config. Refine Config random generator. Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 37 / 78
  • 32. Add (runtime) validation Runtime precondition contract: Rust’s assert! (very primitive; fails a task on failure): static DIVISOR_MIN: int = 2; static DIVISOR_MAX: int = 100; fn validate_pair(&(d, _): &Pair) { assert!(d >= DIVISOR_MIN, "divisor {} must be >= {}", d, DIVISOR_MIN); assert!(d <= DIVISOR_MAX, "divisor {} must be <= {}", d, DIVISOR_MAX); } impl<’a> Config<’a> { pub fn new(pair1: Pair, pair2: Pair) -> Config { validate_pair(&pair1); validate_pair(&pair2); Config(pair1, pair2) } } Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 38 / 78
  • 33. A note on error handling Rust does not have exceptions! Exceptions are evil because they escape the type system. Rust task failures are brutal. Outside scope of this presentation: principled type-based error handling using Result<T, E>: Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 39 / 78
  • 34. Some changes to defaults setup // Cannot be static variable because of runtime // validation and also use of Vector. fn fizzbuzzer_config<’a>() -> Config<’a> { Config::new(vec![(3, "Fizz"), (5, "Buzz")]) } pub fn fizzbuzzer(i: int) -> String { fizzbuzz::evaluate(fizzbuzzer_config(), i) } Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 41 / 78
  • 35. Improve Config random generator #[quickcheck] fn d1_but_not_d2((d1, w1): (int, String), (d2, w2): (int, String), i: int) -> TestResult { if (d1 >= DIVISOR_MIN && d1 <= DIVISOR_MAX) && (d2 >= DIVISOR_MIN && d2 <= DIVISOR_MAX) { let config = Config::new(vec![(d1, w1.as_slice()), (d2, w2.as_slice())]); if i % d1 == 0 && i % d2 != 0 { TestResult::from_bool(evaluate(config, i) == w1) } else { TestResult::discard() } } else { TestResult::discard() } } Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 42 / 78
  • 36. New test runs further, stills fails Refactor old code to fizzbuzz::evaluate, to pass old tests and new test. pub fn evaluate(Config((d1, w1), (d2, w2)): Config, i: int) -> String { match (i % d1 == 0, i % d2 == 0) { (true, false) => w1.to_string(), (false, true) => w2.to_string(), (true, true) => w1.to_string().append(w2), (false, false) => i.to_string(), } } Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 43 / 78
  • 37. Outline 1 Introduction 2 Original FizzBuzz problem 3 FizzBuzz 2: user configuration 4 FizzBuzz 3: FizzBuzzPop and beyond 5 Parallel FizzBuzz 6 Conclusion Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 44 / 78
  • 38. Generalizing to more than two divisors Client wants FizzBuzzPop! Given three divisors (such as 3, 5, 7). Given three words (such as "Fizz", "Buzz", "Pop"). Example: 21 should output "FizzPop". Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 45 / 78
  • 39. More features means more tests Write new tests for new defaults::fizzbuzzpopper: #[test] fn test_fizzbuzzpopper_2() { assert_eq!(fizzbuzzpopper(2), "2".to_string()) } #[test] fn test_fizzbuzzpopper_21() { assert_eq!(fizzbuzzpopper(21), "FizzPop".to_string()) } #[test] fn test_fizzbuzzpopper_9() { assert_eq!(fizzbuzzpopper(9), "Fizz".to_string()) } #[test] fn test_fizzbuzzpopper_7() { assert_eq!(fizzbuzzpopper(7), "Pop".to_string()) } #[test] fn test_fizzbuzzpopper_35() { assert_eq!(fizzbuzzpopper(35), "BuzzPop".to_string()) } Change configuration: to Seq of pairs, instead of just two:Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 47 / 78
  • 40. More tests means more (or changed) types error: this function takes 2 parameters but 1 parameter was supplied Config::new(vec![(3, "Fizz"), (5, "Buzz")]) Change type Config to allow a sequence of pairs: pub struct Config(pub Vec<Pair>); impl<’a> Config<’a> { pub fn new(pairs: Vec<Pair>) -> Config { for pair in pairs.iter() { validate_pair(pair); } Config(pairs) } } Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 48 / 78
  • 41. Fix remaining type errors Refactoring reveals need to implement case of more than two divisors. pub fn evaluate(Config(pairs): Config, i: int) -> String { // Can crash! And incorrect except for 2 pairs. let (d1, w1) = pairs[0]; let (d2, w2) = pairs[1]; match (i % d1 == 0, i % d2 == 0) { (true, false) => w1.to_string(), (false, true) => w2.to_string(), (true, true) => w1.to_string().append(w2), (false, false) => i.to_string(), } } Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 49 / 78
  • 42. More computation means more types Associate each divisor with a “rule” that awaits input. // Make the decision to allocate a String here. fn rule(&(n, word): &Pair, i: int) -> String { if i % n == 0 { word.to_string() } else { String::new() } } FizzBuzz demo time! Two volunteers: each to play role of Rule. One “manager” to combine two sub-results. Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 51 / 78
  • 43. Assemble the types pub fn evaluate(Config(pairs): Config, i: int) -> String { let combined: String = pairs.iter() .map(|pair| rule(pair, i)) .fold(String::new(), |result, s| result.append(s.as_slice())); if combined.is_empty() { i.to_string() } else { combined } } Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 53 / 78
  • 44. A note on fold For any value of type Iterator<A>, we can apply fold: (B, |B, A| -> B) -> B. Example: for Vec<String>, fold with string concatenation + returns the concatenation of all the strings in the vector. Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 54 / 78
  • 45. Test failure: coarse types again $ cargo test task ’fizzbuzz::test::d1_but_not_d2’ failed at ’[quickcheck] TEST FAILED. Arguments: ((2, ), (3, ), 2)’ Demo time! Configuration: vec![(3, ""), (5, "Buzz")] Input: 9 (note: divisible by 2) Output: should be "" (because of the part divisible by 3) Output was: "9" Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 55 / 78
  • 46. Assemble the types pub fn evaluate(Config(pairs): Config, i: int) -> String { let combined: String = pairs.iter() .map(|pair| rule(pair, i)) .fold(String::new(), |result, s| result.append(s.as_slice())); if combined.is_empty() { i.to_string() } else { combined } } Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 56 / 78
  • 47. Property-based testing rescued us again! Be honest: would you have caught this bug manually? I didn’t. I never wrote FizzBuzzPop examples testing empty strings. Property-based testing reveals unexpected corner cases. (Empty “fizz” and “buzz” word strings). Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 57 / 78
  • 48. An empty string is not equivalent to no string Presence of something “empty” is not equivalent to no thing. Sending someone an empty email versus not sending any email. Many programming languages get this wrong. Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 58 / 78
  • 49. Option<A> type Option<A> is one of two possibilities: None Some(a) wraps a value a of type A. For example, Some(String::empty()) is not the same as None. let fizzFor3 = Some(String::new()) // multiple of 3 let buzzFor3 = None // not multiple of 5 let fizzbuzzFor3 = Some(String::new()) // fizzed "" let fizzFor2 = None // not multiple of 3 let buzzFor2 = None // not multiple of 5 let fizzbuzzFor2 = None // not multiple of any Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 59 / 78
  • 50. Cleaning up the types // rule type was: |&Pair, int| -> String // changed to: |&Pair, int| -> Option<String> Useful type errors: mismatched types: expected ‘Option<String>‘ but found ‘String‘ if i % n == 0 { word.to_string() } else { String::new() } failed to find an implementation of trait Str for Option<String> result + s Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 60 / 78
  • 51. Fix the type errors: our rule builder fn rule(&(n, ref word): &Pair, i: int) -> Option<String> { if i % n == 0 { Some(word.to_string()) } else { None } } Demo time! (Instructions: for result Some(s), hold up the string, else don’t hold up anything) Configuration: vec![(3, ""), (5, "Buzz")] Input: 9 Output: now correctly is "" Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 61 / 78
  • 52. Fix the type errors: our compiler pub fn evaluate(Config(pairs): Config, i: int) -> String { let combined: Option<String> = pairs.iter() .map(|pair| rule(pair, i)) .fold(None, add_option); combined.unwrap_or_else(|| i.to_string()) } We need to write: addOption Rust standard library provides: unwrap_or_else Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 62 / 78
  • 53. “Addition” for Option[String] fn add_option(a1: Option<String>, a2: Option<String>) -> Option<String> { match (a1, a2) { (Some(s1), None) => Some(s1), (None, Some(s2)) => Some(s2), (Some(s1), Some(s2)) => Some(s1.append(s2)), (None, None) => None, } } Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 63 / 78
  • 54. Getting A back out of Option<A> Do not lose information! unwrap_or_else inspects the and either returns the value v inside a Some(v), or else returns the value from a closure. Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 64 / 78
  • 55. Transform information; don’t destroy it Our complete code only uses if in one place. Bug cause: destroying information, using if if i % n == 0 { word } else { String::new() } if combined.is_empty() {i.to_string()} else {combined} Transforming information To Option[String]: if i % n == 0 { Some(word.to_string()) } else { None } Back to String: combined.unwrap_or_else(|| i.to_string()) Type-directed design tip We could have saved trouble up front, by using precise types. Avoid if, when possible. Avoid String (but required at I/O boundaries of program). Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 67 / 78
  • 56. Outline 1 Introduction 2 Original FizzBuzz problem 3 FizzBuzz 2: user configuration 4 FizzBuzz 3: FizzBuzzPop and beyond 5 Parallel FizzBuzz 6 Conclusion Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 68 / 78
  • 57. Parallelism Some easy parallelism possible (not yet in Rust standard libraries): Use of map. Use of fold: parallelizable because of the monoid property: Option<String> is a Monoid There is an identity element (None). There is a binary associative operator (add_option). Fantastically important in practice! Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 69 / 78
  • 58. Final (hypothetical) parallelized code pub fn evaluate(Config(pairs): Config, i: int) -> String { pairs.par .iter() .map(|pair| rule(pair, i)) .reduce(add_option) .unwrap_or_else(|| i.to_string()) } Coding style tip This level of conciseness is not always best: maybe too “clever”? Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 70 / 78
  • 59. Final demo! Demo time! Configuration: vec![(3, "Fizz"), (5, "Buzz"), (7, "Pop"), (2, "Boom")] Tree of volunteers to simulate concurrency: Four at leaves. Two “middle managers” each handling two leaves. One top-level manager handling two middle managers. Input: 42 Output: "FizzPopBoom" Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 71 / 78
  • 60. Parallelism summary We discovered a theoretical speedup for generalized FizzBuzz: Sequential: O(n) Parallel: O(log n) (given log n processors, and omitting some technical subtleties) Also, driver outer loop can be sped up: Sequential loop on 1 to m: O(m) Parallel loop: O(1) (given m processors) Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 72 / 78
  • 61. Current (July 2014) Rust limitations Rust closures: still limited (work in progress!!). Scala/Swift/Haskell/etc. have unrestricted closures: less complex types, easy staged compilation. Needs standard libraries for parallelism, using concurrency primitives such as spawn. Need faster compiler, build system. Need better test frameworks. Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 73 / 78
  • 62. Outline 1 Introduction 2 Original FizzBuzz problem 3 FizzBuzz 2: user configuration 4 FizzBuzz 3: FizzBuzzPop and beyond 5 Parallel FizzBuzz 6 Conclusion Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 76 / 78
  • 63. Conclusion Tests are great. Types are great. Tests and types work hand in hand, driving design and program evolution. Modern typed languages such as Rust promote fun, correct programming! It’s a great time to be learning and using a modern typed language. Code, slides, article https://github.com/franklinchen/type-directed-tdd-rust The article has more detail omitted in the presentation. The hyperlinks in all provided PDFs are clickable. Scala: https://github.com/franklinchen/ talk-on-type-directed-tdd-using-fizzbuzz Swift: https://github.com/franklinchen/fizzbuzz-swift Franklin Chen http://franklinchen.com/ Type-Directed TDD in Rust Pittsburgh Code and Supply 77 / 78