comparing haskell and scala
TRANSCRIPT
Comparing Haskell & Scala
Martin Ockajak from Zürich
Software Engineer
@martin_ockajak
Outline
● Similarities
● Conceptual differences
● Haskell – extra features
● Scala – extra features
● Practical considerations
Similarities
Overview
● High-level functional language
● Product of programming language research● Haskell: Purely functional with strong side-effect handling
● Scala: Functional and object-oriented
● Automatic memory management
● Static type checking
● Type inference• Haskell: global, variant of Hindley-Milner
• Scala: local, arguments require type annotations
Expressions
● Everything is an expression producing a value
● No operators, only infix functions with symbolic names
● Haskell
let list = [1 + 2, 3]in list !! 0
(#%) :: String -> String(#%) x = x ++ "!"
● Scala
val list = List(1 + 2, 3)list(0)
def #%(x: String): String = x + "!"
Higher order functions
Functions as arguments and values
● Haskell
applyAndInc f x = f x + 1
double x = x * 2
applyAndInc double 3
● Scala
def applyAndInc(f: Int => Int, x: Int) = f(x) + 1
def double(x: Int): Int = x * 2
applyAndInc(double, 3)
Anonymous functions
Lambda expressions
● Haskell
map (\x -> x + 1) [1, 2, 3]
map (+ 1) [1, 2, 3]
● Scala
List(1, 2, 3) map(x => x + 1)
List(1, 2, 3) map(_ + 1)List(1, 2, 3) map(x => x + 1)
List(1, 2, 3) map(_ + 1)
// partial functionList(1, 2, "3") collect { case x: Int => x + 1 }
Currying
Partial function application
● Haskell
curried x y = x + y + 1
let partial = curried 1in partial 2
uncurried = uncurry curriedcurriedAgain = curry uncurried
● Scala
def curried(x: Int)(y: Int): Int = x + y + 1
val partial = curried(1) _partial(2)
val uncurried = Function.uncurried(curried _)val curriedAgain = uncurried.curried
Encapsulation
Modules and access modifiers
● Haskell
module Module (foo) where
foo x = bar x + 1
bar x = x * 2
● Scala
object Encapsulation { def foo(x: Int) = bar(x) + 1
private def bar(x: Int) = x * 2}
Pattern matching with guards
● Haskell
factorial 0 = 1factorial n = n * factorial (n - 1)
pattern :: [Int] -> Intpattern l | [x] <- l, x /= 0 = 1 | length l > 1 = 2 | otherwise = 0
● Scala
def factorial(n: Int): Int = n match { case 0 => 1 case n => n * factorial(n - 1)}
def pattern(l: List[Int]): Int = l match { case List(x) if x != 0 => 1 case l if l.size > 1 => 1 case _ => 0}
List comprehensions
● Haskell
[ (x, y) | x <- [1, 2, 3], y <- [4, 5, 6], x * y > 7 ]
[0 | _ <- [1..10] ]
● Scala
for { x <- List(1, 2, 3) y <- List(4, 5, 6) if x * y > 7} yield (x, y)
for (_ <- List(1 to 10)) yield 0
Monadic composition notation
● Haskell
do x <- [1, 2, 3] y <- [4, 5, 6] let z = (x + y) return z
● Scala
for { x <- List(1, 2, 3) y <- List(4, 5, 6) z = x + y} yield z
Type system
● Parametric polymorphism
● Ad hoc polymorphism● Haskell: Typeclasses
● Scala: Typeclasses, method overloading, subtyping
● Generalized algebraic data types
● Multi-parameter type classes● Functional dependencies
Type system
● Compound types
● Type aliases
● Existential types
● Higher order types● Kinds
Algebraic data types
● Haskell
data Tree a = Empty | Node a (Tree a) (Tree a)
● Scala
sealed trait Tree[A]
case class Empty[A]() extends Tree[A]
case class Node[A](value: A, left: Tree[A], right: Tree[A]) extends Tree[A]
Typeclasess● Haskell
class Equals a where eq :: a -> a -> Bool
instance Equals Integer where eq x y = x == y
tcEquals :: (Equals a) => a -> a -> BooltcEquals a b = eq a b
● Scala
trait Equals[T] { def eq(x: T, y: T): Boolean}
implicit object EqualsInt extends Equals[Int] { override def eq(x: Int, y: Int): Boolean = x == y}
def tcEquals[T: Equals](x: T, y: T): Boolean = implicitly[Equals[T]].eq(x, y)
Additional features
● Implicit parameters
● Annotations
● Compile-time metaprogramming● Haskell: Template Haskell
● Scala: Macros
Conceptual Differences
Purity
● Haskell● Referential transparency● Side effects modeled via monads● Functions are pure
● Scala● No referential transparency guarantees● Side effects allowed anywhere● Functions cannot be considered pure
Default evaluation strategy
● Haskell● Non-strict lazy evaluation● Optional eager evaluation
● Bang patterns● Strict modules
● Scala● Strict eager evaluation● Optional lazy evaluation
● Call-by-name parameter with lazy val pattern
Control flow
● Haskell● Completely declarative● Exception handling via monads● No way to jump off current scope
● Scala● Imperative constructs are supported● Exception handling at language level● Early return from a method
Haskell – extra features
Pointfree style
● Omitting arguments in function definition
-- Point-wise
incp x = x + 1
expp x = 1 + 2 * x
-- Pointfree
incf = (+ 1)
expf = (1 +) . (2 *)
Polymorphic string literals
● String syntax for various string-like types
string1 :: String
string1 = "string"
string2 :: Text
string2 = "text"
"test" :: IsString a => a
class IsString a where
fromString :: String -> a
Extended list comprehensions
● Parallel (zip)[ (x, y) | x <- [1, 2, 3] | y <- [4, 5, 6]]
● Transformcities = [ ("Berlin", 4.1), ("Milan", 5.2), ("Zurich", 1.3) ]
[ name ++ "!" | (name, size) <- cities,
then sortWith by size,
then drop 1 ]
● Monad[ x + y | x <- Just 1, y <- Just 2 ]
Deriving typeclass instances
● Typeclasses: Eq, Ord, Enum, Bounded, Show, Read
data Coordinate = Planar Float Float | Spatial Float Float Float deriving (Eq, Ord, Show)
Planar 1 2 == Planar 1 2
Spatial 1 2 1 < Spatial 1 2 3
show (Planar 1 2)
Higher rank polymorphism
● Polymorphic function argument specified by the callee
id :: a -> a
id x = x
rank1 :: forall a. a -> a
rank1 x = x
rank2 :: (forall a. Num a => a -> a) -> (Int, Float)
rank2 f = (f 1, f 1.0)
rank2 (* 2)
Compiler extensions
● View patterns & pattern synonyms● Type families● Data type generic programming● Kind polymorphism● And many more …
Scala – extra features
Imperative programming
● Mutable state● Code blocks● While loops
var mutable = 1mutable = 2
while (mutable < 3) { List(1, 2, 3).foreach(_ => println("Missile fired")) mutable += 1}
throw new IllegalStateException("Abandon ship")
Object-oriented programming
● Subtyping● Type variance, type bounds
● Class-based inheritance system● Classes, traits, objects, abstract type members
● Mixin class composition● Multiple inheritance with traits, self-typed references
Named and default arguments
● Just like in Python
def very(standard: String, default: String = "value"): Unit = ()
very("text")
def handy(default: String = "value", standard: String): Unit = ()
handy(standard = "text")
String interpolation
● Programmable via custom interpolators
val neat = 7
// standard libraryval substitution = s"Is $neat"
val printf = f"Number is $neat%02d"
val noEscaping = raw"some\nthing"
// external libraryval document = json"""{ "hello": "world" }"""
Flexible scoping
● Fields in traits● Abstract members● Nested function definitions● Multiple classes & objects in one source file
trait FlexibleScoping { def abstractMethod(text: String): String
val abstractField: Int
def get: String = { def compute: String = "result"
abstractMethod(compute) }}
Implicit conversion
● Automated conversion of method arguments● Searches in current and various outer scopes● Method argument is converted using the implicit method
def more(value: Int): Int = 2 * value
implicit def convert(value: Vector[_]): Int = value.size
more(Vector(1, 2, 3))
// equivalent call with explicit conversionmore(convert(Vector(1, 2, 3)))
Structural typing
● Type checking based on type contents not its name● Resolved at compile-time as opposed to duck-typing
def oneMore(countable: { def size: Int }): Unit = { countable.size + 1}
oneMore(List(1, 2, 3))
Dynamic typing
● Programmable late binding at run-time
import scala.language.dynamics
object Accepts extends Dynamic { def selectDynamic(name: String): Int = name.size
def applyDynamic(name: String)(args: Any*): String = name}
Accepts.something
Accepts.hello("World")
Type system features
● Type lambdas
● F-bounded types● Self-recursive types
● Path-dependent types● Instance bound types
● Value classes
● Type specialization
Practical considerations
Practicality - Haskell
● Great productivity
● High runtime performance
● Clever community
● Solid library and tooling ecosystem
● Reasoning about can be tricky
● Steep learning curve (real or imagined)
Practicality - Scala
● Great productivity
● High runtime performance
● Clever community
● Largest library and tooling ecosystem
● Easy to transition from Java, C++, C#
● Java interoperability somewhat pollutes the language
Thank you :-)