Skip to content

Instantly share code, notes, and snippets.

@kamil-adam
Forked from DarinM223/typeclassses.md
Created December 5, 2019 13:51
Show Gist options
  • Save kamil-adam/d6a1811143d1b17d2838f19d2da7f2ab to your computer and use it in GitHub Desktop.
Save kamil-adam/d6a1811143d1b17d2838f19d2da7f2ab to your computer and use it in GitHub Desktop.
Typeclasses in Haskell, Scala, and Rust

Type classes

Type classes have some advantages over Java style interfaces. One advantage is the ability to implement functions that do not need to take in an instance of the interface.

For example, (FromString v) takes in a String and returns v, and it doesn't have to take v as a parameter.

class FromString v where
    fromString :: String -> v
    
instance FromString Int where
    fromString s = read s :: Int

instance FromString Double where
    fromString s = read s :: Double
    
add :: (FromString a, Num a) => String -> String -> a
add a b = fromString a + fromString b

In Scala, type classes are represented as traits that take in a type parameter and constraints in functions are written as implicit variables.

trait FromString[N] {
  def fromString(s: String): N
}

implicit object IntFromString extends FromString[Int] {
  override def fromString(s: String): Int = s.toInt
}
implicit object DoubleFromString extends FromString[Double] {
  override def fromString(s: String): Double = s.toDouble
}

def add[N](a: String, b: String)(implicit ev1: Numeric[N], ev2: FromString[N]): N =
  ev1.plus(ev2.fromString(a), ev2.fromString(b))

In Rust, traits are very similar to typeclasses. A trait must either take in a parameter with self/Self or return something with Self. This sounds constraining, but remember that Haskell can't do this either:

class Foo a where
    hello :: b

This means that in Haskell the functions of a typeclass must contain the original type somewhere. So the way to implement FromString in Rust is to return Self.

pub trait FromString {
    fn from_string(s: String) -> Self;
}

impl FromString for i32 {
    fn from_string(s: String) -> i32 {
        s.parse::<i32>().unwrap()
    }
}

impl FromString for f32 {
    fn from_string(s: String) -> f32 {
        s.parse::<f32>().unwrap()
    }
}

pub fn add<B>(a: String, b: String) -> B where B: Add<Output=B> + FromString {
    B::from_string(a) + B::from_string(b)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment