Haskell For Normal People

Haskell: Purely Functional, Lazy, & Statically Typed

  • Purely functional - Functions in Haskell are first class values, and all values are immutable, in the same manner as "immutable strings" in other languages.

Haskell: Purely Functional, Lazy, & Statically Typed

  • Lazy - All evaluation is lazy.
    • This means Haskell can evaluate only a specific part of a very large data structure without loading the whole thing into memory.
    • This allows for some very interesting algorithms, as well as allowing for cool tricks like safely working with an infinite list of values.

Haskell: Purely Functional, Lazy, & Statically Typed

  • Statically typed - You probably know what this is. Though it is statically typed, Haskell does a good job of feeling almost dynamic. Far less verbose than Java/C# in this regard.

Haskell Origins

  • Haskell isn't for people with doctorates, though it was designed by people with doctorates.
  • Haskell has been around for 25 years, but most of that was spent gestating in academia.
  • Because of that, it has maintained unusually strong theoretical roots and made few compromises.
  • For the longest time Haskell didn't even do IO because no one could figure out how to do it without violating core tenets of functional programming.

..But Haskell Isn't An 'Ivory Tower' Language

  • Google, Facebook, Alcatel-Lucent, AT&T, Merril Lynch, Credit Suisse, Desutsche Bank, Amgen, Intel, Qualcomm, The New York Times, Siemens, and several high-frequency trading firms are among the companies that use Haskell in some fashion.

  • Performs comparably to C# and Java in benchmarks and is 10-50 times faster than Python and Ruby while staying a very high-level language.

Haskell History

  • By 1987 there were a lot of lazy-evaluation functional languages being developed independently for research purposes.
  • So a committee was formed to come up with the One True Lazy Functional Language.
  • And Haskell was the result of that effort, with Haskell 1.0 being released in 1990.

Haskell History

  • Haskell 1.0 wasn't terribly useful, so in 1997 we got Haskell 98.
  • Haskell 2010 is the most recent version of the standard.

An Elegant Weapon: Tools

  • The Homepage.
  • The de-facto standard for Haskell compilers is the Glasgow Haskell Compiler (GHC).
  • GHC includes both a Haskell-to-native binary compiler (ghc) and a bytecode interpreter/REPL (ghci).
  • A good text editor will do for the rest, there are a few IDEs as well as an Eclipse plugin, but that's outside the scope of what we're looking at.

Nothing To See Here

  • Ridiculously simple GHCi demo.
  • Function Definition demos
    • No return, no parenthesis
    • Function with multiple parameters
    • Hello world
    • Conditionals

Pure, Artisanal Functions

  • Every Haskell function is pure. This is an unbreakable rule of the language.
    • All functions can do is call other functions to compute values, and return a value
    • Cannot modify state
    • Cannot depend on state

There is no state

Purity, Cont.

  • Printing a string to the console?
    • Not pure
  • Reading a file?
    • Not pure
  • Compute the length of a string?
    • Pure, no state
  • Get the current time?
    • Not pure, return value not predictable
  • Get random number?
    • Ditto

Don't Shoot Until You See The Side Effects Of Their Functions

  • General programmer's rule of thumb: If you can detect that a function has been executed without having to use a debugger, it's not pure.

Looping Is For Suckers

  • In Haskell, as in most functional languages, there are no loops.
  • Everything that can be done with loops can be done with recursion.
  • A rare and dangerous trick in imperative programming becomes your bread and butter in Haskell.
  • Lets you write very simple code.
  • Is initially hard for programmers weaned on imperative languages

Recursion

  • Demos

Lists, Strings And Other Things

  • List demos
  • Strings
    • Strings are just lists of characters
    • You remember the concatenation operator, '++'?
    • Since strings are just char lists, wouldn't that work for other kinds of lists?
    • GHCi demo

Heads I Win, Tails You Recurse

  • Accessing list elements - GHCi demos
    • Head demo
    • Tail demo
    • Head+Tail demo
    • Every element of any list can be accessed in this way using recursion.
  • Testing nullity with null
  • Operating on a list with recursion demo

More About Lists

  • Lists in Haskell must be homogeneous. Can't mix types in one list.
  • There is no concept of an untyped list or 'list of objects'.
  • This seems restrictive until you understand Haskell's type system. You rarely need such a list.

Tuples

  • Tuples can hold more than one kind of type
  • They can also have multiple elements
  • They have a fixed length, unlike lists
  • Better to use lists if you have tuples with more than 3-4 values
  • GHCi demo
  • So how do you access tuple elements?

Pattern Matching

  • Pattern matching is used to access tuple elements.
  • It's also used for a lot of other things in Haskell.
  • Again, pattern matching isn't something you see in imperative languages.
  • Tuple pairs demo
  • Null implementation demo
  • Double re-implement demo

Guards

  • Guards make pattern matching even more powerful.
  • Redoing the pow2 and removeOdd functions with guards

Lazy Evaluation

Hang On To Yer Lazy Butts

  • Consider the following pseudocode
    • foo(alpha(1), beta(2))
  • How would this be evaluated in normal imperative languages?
  • In Haskell this would be
    • foo (alpha 1) (beta 2)
  • How do you think Haskell evaluates this?

What if I told you there was no standard evaluation order?

Lazy Functions, Cont.

  • Given this Haskell function:
    • foo (alpha 1) (beta 2)
  • alpha may be evaluated a bit, then beta, the alpha again...

  • If, say, the value of beta is never needed, beta will never be evaluated.

  • This way of doing things actually works in practice, because we're dealing with pure functions.

  • This allows for some interesting performance benefits.

Evaluating Infinity

  • Because of lazy evaluation, Haskell can operate on infinite data structures.
  • Haskell only evaluates what it needs to get the answer, and no more.
  • GHCi demo

A Bit About Partial Application

  • So, you know how in most programming languages you have to specify all the arguments to a function?
  • Haskell is totally OK with not doing that.
    • If you call a function with an incomplete argument set, you just get a function that accepts the missing arguments.
  • This lets you create higher-order functions, among other things.
  • GHCi demo

The Haskell Type System

  • Remember, Haskell is statically typed.
    • This gives us good compile-time checking.
  • But thus far we haven't really had to declare type identifiers.
    • This is also good, the downside of static typing is usually excessive verbosity.
  • Also remember that Haskell doesn't have inheritance, or 'object' types.
  • So how does Haskell do that?

I Infer We're Talking About Inferece

  • As you might have guessed, Haskell uses type inference.
  • This just means that everything must have a type, but the compiler infers the type, and checks that it's used consistently throughout the program.
  • GHCi Demo

Type Inference, Cont.

  • Since Haskell infers the types, you can write 'polymorphic functions' that apply to a variety of types, instead of creating a bunch of type-specific functions.
  • This is basically where C# generics came from.

Back Up Hombre

  • We know that Haskell infers types, right?
  • But how does Haskell know that it shouldn't multiply strings or concat functions?
  • Remember when we looked at the types of variables a few slides back?

TYPE CLASSES!

Type Classes

  • "Type classes" are the paradigm Haskell uses to address this.
  • Type classes are a Haskell original, and are at the core of a lot of its more elegant aspects.

Type Classes, Cont

  • "Class" in this context has nothing to do with object oriented classes.
  • Object-oriented languages tend to mix data and operations that can be performed on the data in the same logical code block (objects or classes)

Type Classes, Redux

  • Haskell's type classes are basically a set of operations (functions) grouped together with an identifier.
  • Variables can belong to one or more type classes, and must support all the operations defined by the type class.

Yep, Still Type Classes

class Eq a where
  (==) :: a -> a -> Bool
  (/=) :: a -> a -> Bool
  • This says that type 'a' belongs to type class 'Eq' if there are functions named '==', and '/=', of the appropriate types, defined on it.

  • To put it another way, when we say a type has a type class we basically mean "all types which can do X are considered to have this type class"

Guess What, Type Classes

  • Think about the values we've been printing to the GHCi console. 
  • To print a type you need to know a way to get a string representation of that type, right?
  • There's a type class for that, named 'Show'.
    • Any type that can be represented as a string is part of the Show class.
    • Any type that supports numerical operations belongs to the Num class.
    • Etc.

Constraints

  • You can specify what kinds of type classes the functions you write will match against.
sum :: Num a => [a] -> a
sum []  = 0
sum (x : xs) = x + sum xs

Type Class Summary

  • Remember:
    • Type classes make up Haskell's type system.
    • Types can have multiple type classes
    • You can constrain function arguments with type classes
    • You can write your own type classes
    • Type classes and types are separate entities
    • This is a very different paradigm from what you see in OO languages. No hierarchies or inheritance graphs.

Type Synonyms

  • Type synonyms let you refer to a type by a different name.
type String = [Char]
  • Here we're defining 'String' as a type synonym of a Char array using the 'type' keyword.

Type Synonyms, Cont

  • Demo
  • Type synonyms are 100% interchangeable with their definitions.
  • Type synonyms are 'syntactic sugar', the compiler will replace them with the definitions.
  • There's an alternative called 'newtype'

Okay, we're done.

We Covered..

  • History
  • Tools
  • Basic syntax
  • Calling and defining functions
  • Function purity
  • Recursion
  • Lists
  • Tuples
  • Pattern matching
  • Lazy evaluation
  • Type inference
  • Type classes
  • Type class constraints

We Didn't Cover

  • Monads/IO
  • Algebraic data types
  • newtype
  • Records
  • Type class instances
  • Parameterized types
  • Lots of other stuff.

There's a lot more to Haskell I didn't cover.

You've got questions, Haskell has elegant answers, even if I don't.

This entire presentation was mostly cribbed from the Haskell Fundamentals course on Pluralsight by Benson Joeris.


Go watch that course. It's clearer and more exhaustive than this presentation.

Thank you and goodnight.

Haskell For Normal People

By Ben Leggett

Haskell For Normal People

  • 1,393