@AndrewRadev
Old haskell(ish) talk
Return-type polymorphism?
Algebraic data types?
Modeling event loops as infinite lists?
M
Mo
on
na
ad
ds
s?
?
Slide 6
Slide 6 text
@AndrewRadev
Old haskell(ish) talk
Runtime errors!
“Mathematically impossible to have errors in
”
Your language has too much freedom!
You can’t be trusted with it!
Slide 7
Slide 7 text
@AndrewRadev
Ruby: It’s Really Not That Bad
# This is good:
Person.walk_the_dog(Dog.new)
# But WHAT IF?
Person.walk_the_dog(Cat.new)
# Solution: don’t do that ¯\_( ツ )_/¯
Slide 8
Slide 8 text
@AndrewRadev
Ruby: It’s Really Not That Bad
Processes, tools, tests
Peer-review
Error tracking in prod
Slide 9
Slide 9 text
@AndrewRadev
Some things really are dangerous
https://twitter.com/thegrugq/status/1141945136122740736
free()
free()
Slide 10
Slide 10 text
@AndrewRadev
Static typing as a…
necessary limitation? :/
Slide 11
Slide 11 text
@AndrewRadev
Static typing as a
form of freedom! :)
Slide 12
Slide 12 text
@AndrewRadev
Enums
(Algebraic data types)
(Sum types)
@AndrewRadev
enum Option {
Some(T),
None,
}
fn main() {
let args = vec![1, 2, 3];
if let Some(value) = args.get(2) {
println!("Third arg is: {}", value);
} else {
println!("Nope, nothing here");
}
}
Slide 15
Slide 15 text
@AndrewRadev
enum Result {
Ok(T),
Err(E),
}
fn read_file(path: &str) -> Result {
// ...
}
fn main() {
// settings: Result
let settings = read_file("~/settings.json");
// But why not a pair (T, E)?
let (settings, error) = read_file("~/settings.json");
}
@AndrewRadev
class Result
attr_reader :value, :error
def initialize(value: nil, error: nil)
@value = value
@error = error
end
end
Slide 18
Slide 18 text
@AndrewRadev
class Result
def and_then(&block)
if value
block.call(value)
else
self
end
end
def or_else(&block)
if error
block.call(error)
else
self
end
end
end
Slide 19
Slide 19 text
@AndrewRadev
# Successful result:
Result.new(value: "Pumpkin spice latte")
# Errorful result:
Result.new(error: "Ran out of pumpkins!")
# Is this possible? :/ What do we do here?
Result.new(
value: "Everything is great!",
error: "Everything is broken!"
)
Slide 20
Slide 20 text
@AndrewRadev
class Result
def and_then(&block)
if value
# ...
end
end
def or_else(&block)
if error
# ...
end
end
end
# Add runtime checks?
Slide 21
Slide 21 text
@AndrewRadev
class Event
def initialize(...)
@type = "initialization"
@type = "cascade"
# ...
@alchemiter = ...
@laserstation = ...
@punch_designix = ...
# ...
end
end
@AndrewRadev
Types as containers
Classes/Structs
Pairs/Tuples
Lists
Dictionaries
Slide 24
Slide 24 text
@AndrewRadev
Enums: not just containers
Enums express exclusivity
Slide 25
Slide 25 text
@AndrewRadev
class Success
def and_then(&block)
block.call(value)
end
def or_else
# do nothing
end
end
class Error
def and_then(&block)
# do nothing
end
def or_else
block.call(error)
end
end
Slide 26
Slide 26 text
@AndrewRadev
Dynamic typing
Implicit knowledge:
Success and Error should have the same methods.
Implicit knowledge:
Result has either a value or an error
Slide 27
Slide 27 text
@AndrewRadev
Static typing
Explicit rule:
Result is either an Ok or an Err
@AndrewRadev
Monkey-patching
✨F
Fr
re
ee
ed
do
om
m-patching!
Slide 32
Slide 32 text
@AndrewRadev
# In code
class UserRegistration
def facebook_client
FacebookClient.new
end
end
# In tests
class UserRegistration
def facebook_client
FakeFacebookClient.new
end
end
Slide 33
Slide 33 text
@AndrewRadev
class TrueClass
def if_true(&block)
block.call
self
end
def if_false
self
end
end
Slide 34
Slide 34 text
@AndrewRadev
class FalseClass
def if_true
self
end
def if_false(&block)
block.call
self
end
end
Slide 35
Slide 35 text
@AndrewRadev
(2 + 2 == 5).
if_true { print "Radiohead were right!\n" }.
if_false { print "Math is still boring, I guess\n" }
@AndrewRadev
require 'active_support/core_ext/string/inflections'
"table".pluralize # => "tables"
"ox".pluralize # => "oxen"
# String#pluralize is now *everywhere*
# (Refinements are a possible solution)
Slide 42
Slide 42 text
@AndrewRadev
use inflector::Inflector;
fn main() {
println!("{}", "table".to_plural());
// => "tables"
println!("{}", "ox".to_plural());
// => "oxen"
}
// "Use" statement only affects a limited scope
Slide 43
Slide 43 text
@AndrewRadev
Itertools
Slide 44
Slide 44 text
@AndrewRadev
The Iterator trait
"The quick brown Firefox...".chars() // => Chars
"jumps over the lazy Netscape".bytes() // => Bytes
vec!["Veni", "Vidi", "Vec!"].into_iter() // => vec::IntoIter
let mut passwords = HashMap::new();
passwords.insert("root", "admin123");
passwords.values() // => hash_map::Values
Slide 45
Slide 45 text
@AndrewRadev
impl Itertools for T where T: Iterator { }
Slide 46
Slide 46 text
@AndrewRadev
impl Itertools for T where T: Iterator { }
Slide 47
Slide 47 text
@AndrewRadev
impl Itertools for T where T: Iterator { }
Slide 48
Slide 48 text
@AndrewRadev
impl Itertools for T where T: Iterator { }
@AndrewRadev
fn main() {
let pattern = Regex::new(r"\S+@\S+\.\S+").unwrap();
let input = Article::new(
"Hello!",
r#"
My email is [email protected]!
I've got another one at [email protected]!
"#
);
let is_match: bool = input.search(&pattern);
let actual_match: Option = input.search(&pattern);
let all_the_matches: Vec = input.search(&pattern);
}
Slide 61
Slide 61 text
@AndrewRadev
fn main() {
let pattern = Regex::new(r"\S+@\S+\.\S+").unwrap();
let input = Article::new(
"Hello!",
r#"
My email is [email protected]!
I've got another one at [email protected]!
"#
);
if input.search(&pattern) {
println!("Yes, a match!");
}
if let Some(m) = input.search(&pattern) {
println!("Yes, a match: {}!", m);
}
}
Slide 62
Slide 62 text
@AndrewRadev
fn main() {
let pattern = Regex::new(r"\S+@\S+\.\S+").unwrap();
let input = Article::new(
"Hello!",
r#"
My email is [email protected]!
I've got another one at [email protected]!
"#
);
let all_the_matches: Vec = input.search(&pattern);
for m in all_the_matches {
println!("One of the matches is: {}", m);
}
}
@AndrewRadev
GTK-rs
let label1 = gtk::Label::new("Hello");
let label2 = gtk::Label::new(None);
impl Label {
fn new(s: impl Into>) -> Label {
let s = s.into();
// ...
}
}
Slide 72
Slide 72 text
@AndrewRadev
GTK-rs
There are two reasons behind going back on this feature:
The first one is about [...] error messages. [...] annoying
situations where you had to try to figure out what was going
on.
The second one is to go back to some Rust fundamentals:
being explicit. […]
– The GTK-rs team
Slide 73
Slide 73 text
@AndrewRadev
The freedom to make mistakes
fn schroedingers_cat() -> Box;
// This makes sense:
fn schroedingers_cat() -> Box { }
fn schroedingers_cat() -> Box { }
// But WHAT IF?
fn schroedingers_cat() -> Box { }
// Don’t do that ;) ;) ;)
Slide 74
Slide 74 text
@AndrewRadev
Freedom...
...means allowing you to express concepts in code
...means allowing you to write bad code :)
...is not just about type signatures
...is not one dimensional