30 days with

Haskell



- A Pure Functional Language

Presentation by Danny Su

reveal.js

slides made with reveal.js

http://lab.hakim.se/reveal-js

Agenda

  1. What did I do
  2. The Motivation
  3. Cool Haskell Stuff
  4. PHP Equivalent or Lessons
  5. Repeat 3 & 4

What

Motivation

Learn at least one new language every year.
- The Pragmatic Programmer, p14

  • Scala, Clojure, Erlang, Common Lisp, etc
  • No side effects
  • Concurrency
  • Functional Programming way of thinking

Haskell

  • Static & strongly typed
  • First-class functions
  • Higher-order functions
  • Help avoid side effects in functions


Other Cool Stuff

  • Lazy evaluation
  • Type inference

Static & Strongly Typed

  • Variable types checked during compile time or when using REPL
  • Can't mix different variable types
  • Type inference

Haskell:

Can't mix types or use undefined variables


Prelude> a

<interactive>:1:1: Not in scope: `a'



Prelude> (1 :: Integer) + (1 :: Double)

<interactive>:1:19:
    Couldn't match expected type `Integer' with actual type `Double'
    ...
						

Haskell:

Must convert before adding


Prelude> read "3" + 1
4
						

Type inference


Prelude> read "3" + 1.0
4.0

Prelude> read "3"

<interactive>:1:1:
    Ambiguous type variable `a0' in the constraint:
    ...

Prelude> read "3" :: Double
3.0
						

PHP:

Undefined constant becomes string and you get:


php > echo UNDEFINED_PHP_CONST + 900;
900
						
  • Caused bugs undetected and pushed to production
  • Potential bugs and dead code kept around unnoticed


With PHP notices on:


php > echo UNDEFINED_PHP_CONST + 900;
PHP Notice:  Use of undefined constant UNDEFINED_PHP_CONST - assumed 'UNDEFINED_PHP_CONST' in
php shell code on line 1
900
						

First-Class Function

Higher-Order Function (HOF)

  • Functions treated the same as everything else
  • Functions that take functions or produce functions

Quick Sort in Haskell


quicksort []     = []

quicksort (p:xs) = (quicksort lesser) ++ [p] ++ (quicksort greater)
    where
	lesser  = filter (< p) xs
	greater = filter (>= p) xs
						


  • pattern matching
  • filter is a HOF
  • (< p) and (>= p) are partially applied functions

HOF in PHP


function process_with_clean_environment($do_process) {
	// backup existing session
	$current_data = $_SESSION['data'];

	// call function
	$do_process();

	// restore
	$_SESSION['data'] = $current_data;
}

process_with_clean_environment(function() {
	// do whatever I want with $_SESSION['data']
});
						
Necessary due to existing code difficult to reuse


Similar: Strategy design pattern in OO
Downside: Closure involving $this doesn't work until 5.4

filter in Haskell


filter is_this_good the_list
						
  • Apply function on list
  • Get a new list with elements that are good

filter in PHP


$result = array_filter($the_list, "is_this_good");
						

vs


$result = array();
foreach ($the_list as $element) {
    if (is_this_good($element)) {
        $result[] = $element;
    }
}
return $result;
						

map & reduce


Prelude> let the_list = [1, 3 .. 7]
Prelude> the_list
[1,3,5,7]

Prelude> {- map applying lambda to the_list -}
Prelude> map (\x -> x + 1) the_list
[2,4,6,8]

Prelude> {- aka "reduce" down to a single result -}
Prelude> foldl (+) 0 the_list
16
						

map & reduce in PHP

array_map,
array_reduce
See array functions.



Similar: Visitor design pattern in OO

function composition


Prelude> toUpper . head . tail $ "Me"
'E'
Prelude> toUpper (head (tail "Me"))
'E'
Prelude> let uppercase = toUpper . head . tail
						


list comprehension


{- Project Euler Problem #1 -}
{- Find the sum of all natural numbers below 1000 and are multiples of 3 or 5 -}
sum [x | x <- [1 .. 999], x `mod` 3 == 0 || x `mod` 5 == 0]
						

No side effects

  • Same input, same output all the time
  • Easy to test
  • Easy to reuse
  • Can't go modifying global states and other things
  • Haskell is pure

Haskell

Keep things separated via type system:


Prelude> {- getLine returns IO String of what user types -}
Prelude> :type getLine
getLine :: IO String

{- A pure function -}
uppercase :: String -> String
uppercase str = map toUpper str

{- Trying to stuff a IO String to String will fail -}
main = do
    return $ uppercase getLine

{- Take String out of IO String first, then you can use it -}
main = do
    str <- getLine
    return $ uppercase str
						

Deal with states by passing them around explicitly

PHP

Careful that function can have side effects:


function bad_php_code() {
	global $db;

	$data = $_SESSION['data'];

	$_SESSION['data'] = 0;
}
						
makes it difficult to test or reuse

Things to look into:
  • Dependency Injection
  • "Decoupling and the Law of Demeter" - The Pragmatic Programmer, p138

Expression must return a value

  • Not Valid:
    
    add_if_greater a = if a > 100
                         then a + 1
    						
  • Valid:
    
    add_if_greater a = if a > 100
    		     then a + 1
                         else a
    						
  • PHP

    Be careful with:

    
    // code in our codebase
    function mixed_return() {
    	if (!good) {
    		return FALSE;
    	}
    
    	perform_action();
    	// PHP returns NULL for you
    }
    
    $result = mixed_return();
    if (!$result) {
    	// Both cases are "false"
    }
    
    // forced to distinguish
    if ($result === FALSE) {
    }
    						

    Haskell Types & Typeclasses

    Defining a type:

    
    {- Type with 2 value constructors -}
    data Maybe a = Just a
    	     | Nothing
    
    {- Using value constructors -}
    let result = Just 1
    let result = Nothing
    						

    Typeclasses

    Think of this like an interface

    
    class Eq a where 
      (==)                  :: a -> a -> Bool
    						


    Actual implementation of == for Integer & Float

    
    instance Eq Integer where 
      x == y                =  x `integerEq` y
    
    instance Eq Float where
      x == y                =  x `floatEq` y
    						

    Haskell Web Frameworks

    • Yesod
    • Happstack
    • Snap

    More at Haskell wiki

    Yesod Web Framework


    http://www.yesodweb.com
    tutorial by Yann Esposito

    Routing

    
    /             HomeR   GET
    /echo/#Text   EchoR   GET
    						

    Handlers

    aka Controller

    
    getEchoR :: Text -> Handler RepHtml
    getEchoR theText = do
        defaultLayout $ do
            $(widgetFile "echo")
    						

    Templates

    aka View

    
    <h1> #{theText}
    						
    templates/echo.hamlet

    Persistent

    storage (DB, etc)

    
    {- Define a Developer datatype -}
    mkPersist sqlSettings [persist|
    Developer
        firstname String
        lastname String
        deriving Show
    |]
    
    {- Add a developer -}
    main = withSqliteConn ":memory:" $ runSqlConn do
        dannyId <- insert $ Developer "Danny" "Su"
    						

    Could also do raw SQL

    Yesod Demo

    Link

    ...

    THE END