1 of 34

Haskell and Yesod

Michael Snoyman

2 of 34

What is Haskell?

  • Purely functional
  • Type-safe
  • Lazy
  • Immutable

Note: you can add "by default" after each bullet.

3 of 34

What is Yesod?

  • Web framework built on Haskell
  • Is not revolutionary: sticks to common MVC pattern
  • Tries to pull in Haskell's benefits to the standard MVC approach

4 of 34

Today's talk

We'll focus on three of these strengths:

  • Type safety
  • Simple concurrency/parallelism
  • User friendly*

* Yes, Haskell is user friendly!

5 of 34

Type safety

  • Far more than just static typing
  • Based on programming style more than the language itself
  • Haskell makes it easier to be type safe

Full story: http://www.youtube.com/watch?v=0HczswtAVhg

6 of 34

XSS attacks: Description

  • Writing a blog with user comments.
  • User enters comment, it gets stored in database.
  • Later we pull this out and show it to the user.
  • Question: what do we do with the following values:

<script>alert("XSS Attack!")</script>

I was good &amp; I escaped my entities.

7 of 34

XSS attacks: Explained

  • Need to track "what kind of text is this"
  • Doing so is a tricky, error-prone process
  • If you incorrectly guess that the data is escaped, you have a XSS attack.
  • If you incorrectly guess that the data is not escaped, you have a double-escape bug.
  • Humans suck at this.

8 of 34

XSS attacks: Yesod

  • Represent different possible kinds of text with data types.
  • Exists purely at compile time, no runtime overhead.
  • Represent this in the database model, and you're done.

9 of 34

XSS attacks: Database

Comment

name Text

textualContent Textarea

htmlContent Html

10 of 34

XSS attacks: Form/HTML

commentForm = renderBootstrap $ Comment

<$> areq textField "Name" Nothing

<*> areq textareaField "Textual content" Nothing

<*> areq nicHtmlField "HTML content" Nothing

<p>Name: #{commentName comment}

<p>Textual: #{commentTextualContent comment}

<p>HTML: #{commentHtmlContent comment}

Yes, the namespacing issues here are annoying. It's a downside in Haskell right now, we're hoping to fix it.

11 of 34

XSS attacks: summary

  • Textarea automatically escapes entities, converts newlines to <br>s.
  • HTML is automatically XSS-sanitized
    • Yesod philosophy: check data at the boundary
  • High-level forms keep type safety
    • Can't accidently use a "date" field for the name
  • No need to use explicit conversions in HTML
    • The types tell the compiler everything it needs to know

12 of 34

XSS attacks: input/output

13 of 34

Type-safe URLs

  • URLs are inherently textual and untyped
  • Yesod sanitizes paths into type-safe structures for you
  • High-level, declarative routing syntax
  • Plays well with type-safe database

14 of 34

Routing (2) blogs <- runDB $ selectList [] []

defaultLayout [whamlet|

<h1>Blog Posts

<ul>

$forall Entity id blog <- blogs

<li>

<a href=@{BlogPostR id}>#{blogTitle blog}

|]

Type-safe URLs: routing

/blog/#BlogId BlogPostR GET

Blog

title Text

slug Text

content Html

UniqueBlog slug

15 of 34

Type-safe URLs: handler

getBlogPostR :: BlogId -> Handler RepHtml

getBlogPostR id = do

blog <- runDB $ get404 id

defaultLayout [whamlet|

<h1>#{blogTitle blog}

<article>#{blogContent blog}

|]

16 of 34

Type-safe URLs: linking

blogs <- runDB $ selectList [] []

defaultLayout [whamlet|

<h1>Blog Posts

<ul>

$forall Entity id blog <- blogs

<li>

<a href=@{BlogPostR id}>#{blogTitle blog}

|]

17 of 34

Type-safe URLs: summary

  • Inside your application, always link based on datatype
  • Update the URL, code still works
  • Use the wrong kind of database ID, fails at compile time
  • Change URL structure dramatically, fails at compile time
  • Bugs are easier to catch at compile time
  • And no unit tests required

18 of 34

Type safety: other examples

  • Use Fay (Haskell subset) on client side
    • Guaranteed match between server and client data types
    • Hoping to make improvements going forward
  • Sum types properly express unions
    • Completely avoid null pointer exceptions
    • Dictionary lookup functions express failure in the types
  • Differentiate between easily-confused arguments
    • newtype Width = Width Int; newtype Height = Height Int

19 of 34

Concurrency and parallelism

  • Actually two very different problems
  • Parallelism attempts to make code faster by splitting the work across multiple workers (CPUs, cores, computers, etc)
    • Example: matrix operations on a large dataset
  • Concurrency is for handling multiple tasks at the same time, possibly all done by a single worker
    • Example: creating a responsive GUI

20 of 34

Parallelism

  • At the OS level, we need to create and coordinate multiple threads/processes.
  • Managing this communication can be complicated
    • Deadlocks
    • Race conditions
  • Haskell provides easy parallelism with the par function.
  • This is possible due to immutability.
  • We build better abstractions on top of that.

21 of 34

Parallel map

import Control.Parallel.Strategies

someValues = [1..10]

factorial 1 = 1

factorial i = i * factorial (i - 1)

main = do

-- sequential

print $ map factorial someValues

-- parallel

print $ parMap rseq factorial someValues

22 of 34

Other parallelism tools

  • Software Transactional Memory
  • async library
  • Data Parallel Haskell
  • accelerate (GPU programming)

23 of 34

Concurrency

  • Need to handle multiple events
  • Threading is a useful abstraction
    • OS threads very heavy-weight
  • Evented programming is more efficient
    • Also much more complicated to program
    • Callback hell
    • Cooperative multithreading
  • Haskell solution: light-weight green threads, based on an evented runtime
    • Similar to Erlang
    • Simple thread-like code, efficient evented code

24 of 34

Example: nodejs file server

http.createServer(function(request, response) {

var uri = url.parse(request.url).pathname;

var filename = path.join(process.cwd(), uri);

path.exists(filename, function(exists) {

if(exists) {

fs.readFile(filename, function(err, data) {

response.writeHead(200);

response.end(data);

});

} else {

util.log('File not found: ' + filename);

response.writeHead(404);

response.end();

}

});

}).listen(3000);

25 of 34

Equivalent Haskell code

main = run 3000 $ \req -> do

let filename = "./" ++ S8.unpack (rawPathInfo req)

exists <- liftIO $ doesFileExist filename

if exists

then return $ ResponseSource status200 []

$ sourceFile' filename

else return $ responseLBS status404 [] "Not found"

sourceFile' = mapOutput (Chunk . fromByteString) . sourceFile

26 of 34

Real Haskell version

main = run 3000 $ \req -> return $ ResponseFile

status200

[]

("./" ++ S8.unpack (rawPathInfo req))

Nothing

27 of 34

Speed comparison

28 of 34

More speed comparison

  • More information in the upcoming Performance of Open Source Applications chapter on Warp.
  • Warp is the HTTP engine which powers both Yesod and the Mighty web server.
  • Weighs in at ~1200 SLOC.

29 of 34

User friendly

  • Haskell
    • Light-weight syntax
    • Simple DSLs
    • Override most built-in syntax for special uses
  • Yesod
    • Devel server
    • Scaffolding
    • Consistent set of DSLs
  • However: there is a learning curve.
    • Haskell has different semantics than most languages
    • Not just a rebranding of the same stuff

30 of 34

Basic syntax

  • Significant whitespace
  • Automatic function application
  • Pattern matching

someValues = [1..10]

factorial 1 = 1

factorial i = i * factorial (i - 1)

main = do

-- sequential

print $ map factorial someValues

31 of 34

DSLs

  • Embedded DSLs are easy to create
  • Quasi-Quoted DSLs give maximum flexibility
  • All of them are type safe and compiled to efficient runtime code.

Full story: http://oreillynet.com/pub/e/2400

32 of 34

Yesod's user friendliness

  • Inspired by other popular frameworks
  • Get started easily with "yesod init"
    • Codifies best practices
    • Get started quickly
  • "yesod add-handler" to add a new route
  • "yesod devel" to run development server
    • Code automatically reloads
    • Avoid recompiles when possible (CSS/JS changes)

33 of 34

Templating languages

  • Hamlet is a whitespace-sensitive HTML
  • Lucius is a superset of CSS
  • Julius is a passthrough JS language

34 of 34

Questions?