Typed Polymorphism
A Comparison Between Existing Languages
Haskell: Typeclass
class FromJSON a where
parseJSON :: Value -> Parser a
class ToJSON a where
toJSON :: a -> Value
data Person = Person {
name :: Text
, age :: Int
}
instance FromJSON Person where
parseJSON = withObject "Person" $ \v -> Person
<$> v .: "name"
<*> v .: "age"
instance ToJSON Person where
toJSON (Person name age) =
object ["name" .= name, "age" .= age]
# PRESENTING CODE
Haskell: Typeclass & Constraints
class Semigroup a where
(<>) :: a -> a -> a
instance Semigroup (List a) where
Nil <> ys = ys
(Cons x xs) <> ys = Cons (xs <> ys)
class Semigroup a => Monoid a where
mempty :: a
mappend :: a -> a -> a
instance Monoid (List a) where
mempty = []
mappend = (<>)
# PRESENTING CODE
Haskell: Typeclass
class Functor f where
fmap :: (a -> b) -> f a -> f b
data List a = Nil | Cons a (List a)
instance Functor List where
fmap f Nil = Nil
fmap f (Cons x xs) = Cons (f x) (fmap f xs)
# PRESENTING CODE
Rust: Trait
pub trait Summary {
fn summarize(&self) -> String;
}
pub struct Tweet {
pub username: String,
pub content: String,
pub reply: bool,
pub retweet: bool,
}
impl Summary for Tweet {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}
# PRESENTING CODE
Rust: Trait & Associated Type
trait Iterator {
type Item; // Associated Type
fn next(&mut self) -> Option<Self::Item>;
...
}
pub trait IntoIterator {
type Item;
type IntoIter: Iterator<Item = Self::Item>;
fn into_iter(self) -> Self::IntoIter;
}
impl<'a, T, A: Allocator> IntoIterator for &'a Vec<T, A> {
type Item = &'a T;
type IntoIter = slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
# PRESENTING CODE
Rust: Trait & Associated Type
pub struct Map<I, F> {
pub(crate) iter: I,
f: F,
}
impl<I, F> Map<I, F> {
pub(in crate::iter) fn new(iter: I, f: F) -> Map<I, F> {
Map { iter, f }
}
}
let v: Vec<i32> = [1, 2, 3].into_iter().map(|x| x + 1).rev().collect();
# PRESENTING CODE
Rust: Trait & Associated Type
trait Iterator {
type Item; // Associated Type
fn next(&mut self) -> Option<Self::Item>;
fn map<B, F>(self, f: F) -> Map<Self, F>
where
Self: Sized,
F: FnMut(Self::Item) -> B,
{
Map::new(self, f)
}
...
}
pub struct Map<I, F> {
pub(crate) iter: I,
f: F,
}
# PRESENTING CODE
Rust vs Haskell: map is...different
# PRESENTING CODE
- In Rust: it's a struct, too many packed in one trait
- In Haskell: inheritance, inheritance, inheritance
The Secret Sauce: More Types!
-- *Kind* signature
class Functor (f :: * -> *) where
fmap :: (a -> b) -> f a -> f b
# PRESENTING CODE
Can you do this in Rust? No.
Rust: Problem with Trait Design
trait GeeksforGeeks<T> {
fn gfg_func(self, _: T);
}
# PRESENTING CODE
Rust: Trait Bounds
impl<'a, T, A: Allocator> IntoIterator for &'a Vec<T, A> {
type Item = &'a T;
type IntoIter = slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
# PRESENTING CODE
Rust: Trait Bounds
impl<'a, T, A: Allocator> IntoIterator for &'a Vec<T, A> {
type Item = &'a T;
type IntoIter = slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
# PRESENTING CODE
C++: Template & Generic
template <class T> void bubbleSort(T a[], int n)
{
for (int i = 0; i < n - 1; i++)
for (int j = n - 1; i < j; j--)
if (a[j] < a[j - 1])
swap(a[j], a[j - 1]);
}
int main()
{
int a[5] = { 10, 50, 30, 40, 20 };
int n = sizeof(a) / sizeof(a[0]);
bubbleSort<int>(a, n);
for (int i = 0; i < n; i++)
cout << a[i] << " ";
cout << endl;
return 0;
}
# PRESENTING CODE
C++ Constraints: Concept
#include <string>
#include <cstddef>
#include <concepts>
template<typename T>
concept Hashable = requires(T a)
{
{ std::hash<T>{}(a) } -> std::convertible_to<std::size_t>;
};
struct meow {};
int main()
{
using std::operator""s;
f("abc"s); // OK, std::string satisfies Hashable
// f(meow{}); // Error: meow does not satisfy Hashable
}
# PRESENTING CODE
# CHAPTER 2
Conclusion & Q&A
- Typeclass: Genericity, Constraints, all in one go
- C++ Concept: Not Good
Code
By zekt
Code
- 171