haskell tour (part 1)
DESCRIPTION
We introduce Haskell. Why is it interesting. Where did it come from. What is it like. How to get started. We show a GHCi session. We introduce simple recursive function and data. And we demo QuickCheck for testing properties of automatically generated data.TRANSCRIPT
HaskellA Whirlwind Tour
William Taysom2011
Haskell
Haskell
Who?
What?
When?
Where?
Why?
How?
Haskell
Who?
What?
When?
Where?
Why?
How?
Why?
Aesthetics
Why? Aesthetics
“We provided DARPA with a copy of our prototype implemented in Haskell without explaining that it was a program, and based on preconceptions from their past experience, they had studied the program under the assumption that it was a mixture of requirements specification and top level design. They were convinced it was incomplete because it did not address issues such as data structure design and execution order.”
— Paul Hudak
Why? Aesthetics
“Take Lisp: you know it's the most beautiful language in the world. At least up until Haskell came along.”
— Larry Wall
Why? Aesthetics
“Reading Haskell is like reading poetry.Writing Haskell is like writing poetry.”
— Oliver Steele
Why? Aesthetics
“I also have interest in Haskell, but my brain just explodes every time I read a Haskell program bigger than ten lines.”
— Matz
Why? Aesthetics
“The biggest advantage of Haskell to me is that it helps me write better programs in other languages.”
— Tenerife Skunkworks
Why? Aesthetics
.NET LINQ
Python List Comprehensions
Java Generics
JavaScript jQuery
Why?
Pragmatics
Why? Pragmatics
Platform Compiler Debugger Profiler Testing
Libraries Network Graphics Hackage
Community Books Documentation Hoogle
Why? Pragmatics
Platform Compiler Debugger Profiler Testing
Libraries Network Graphics Hackage
Community Books Documentation Hoogle
Why? PragmaticsHoogle
Why? PragmaticsHoogle
Why? Pragmatics
Platform Compiler Debugger Profiler Testing
Libraries Network Graphics Hackage
Community Books Documentation Hoogle
Why? Pragmatics
Platform Compiler Debugger Profiler Testing
Libraries Network Graphics Hackage
Community Books Documentation Hoogle
How?
GHCDocumentation
LibrariesCabal
Hackage
Why? Pragmatics
Platform Compiler Debugger Profiler Testing
Libraries Network Graphics Hackage
Community Books Documentation Hoogle
Why? Pragmatics
Why? Pragmatics
Why? Pragmatics
“A programming language must be considered in the context of its community, and Haskell has an exemplary one. I have come to believe, however, that this polite exterior conceals a deep and consuming madness.”
— Avdi Grimm
Why?
Performance
Why? PerformanceThe Computer Language Benchmarks Game
Web ServerPong benchmark, extra large instance, requests/second
Why? Performance
Web ServerPong benchmark, extra large instance, requests/second
Why? Performance
Why?
AestheticsPragmatics
Performance
Haskell
Who?
What?
When?
Where?
Why? Aesthetics Pragmatics Performance
How?
Haskell
Who?
What?
When?
Where?
Why? Aesthetics Pragmatics Performance
How?
Who? When? Where?
September 1987
Who? When? Where?
Portland, Oregon
Who? When? Where?
Functional Programming Languages and Computer Architecture Conference
Who? When? Where?
A Dozen PurelyFunctional Languages
Who? When? Where?
All Similar
Who? When? Where? Committee Formed
Who? When? Where? Committee Formed
Provide faster communication of new ideas.
Stable foundation for real application development.
Vehicle through which others would be encouraged to use functional languages.
Who? When? Where?
Haskell ReportApril 1st 1990
“You know, Haskell actually never liked the name Haskell.”
— Mary Curry
Who? When? Where?
2002
Revised Haskell 98 Report
Who? When? Where?
2010
Haskell'
Haskell
Who? Research Wadler Hudak Peyton-Jones
What?
When? 1987 1990 2002 Now
Where? Portland Glasgow Microsoft
Why? Aesthetics Pragmatics Performance
How?
Haskell
Who? Research Wadler Hudak Peyton-Jones
What?
When? 1987 1990 2002 Now
Where? Portland Glasgow Microsoft
Why? Aesthetics Pragmatics Performance
How?
What?
A non-strict, purely functional programming language with strong,static type inference.
What?
A non-strict, purely functional programming language with strong,static type inference.
What? Programming
Source Code
Formal, Textural Syntax
Static & Runtime Semantics
Data, Variables, Lexical Scope
Interpreter, Compiler
What?
A non-strict, purely functional programming language with strong,static type inference.
What?
A non-strict, purely functional programming language with strong,static type inference.
What? Functional
(Not Imperative)
What? Functional
Imperative Statements Executed Step-by-step
What? Functional
Imperative Statements Executed Step-by-step
Modifying State
What? Functional
Imperative Statements Executed Step-by-step
Modifying State
Turing Machine
What? Functional
Imperative Statements Executed Step-by-step
Modifying State
Von Neumann Architecture
What? Functional
Imperative Statements Executed Step-by-step
Modifying State
Von Neumann Architecture
Functional Expressions Recursively Simplified
What? Functional
Imperative Statements Executed Step-by-step
Modifying State
Von Neumann Architecture
Functional Expressions Recursively Simplified
Reduced Value
What? Functional
Imperative Statements Executed Step-by-step
Modifying State
Von Neumann Architecture
Functional Expressions Recursively Simplified
Reduced Value
Lambda Calculus
Imperative Statements Executed Step-by-step
Modifying State
Von Neumann Architecture
Functional Expressions Recursively Simplified
Reduced Value
Lambda Calculus
What? Functional
Imperative Statements Executed Step-by-step
Modifying State
Von Neumann Architecture
Functional Expressions Recursively Simplified
Reduced Value
Lambda Calculus
What? Functional
What?
A non-strict, purely functional programming language with strong,static type inference.
What?
A non-strict, purely functional programming language with strong,static type inference.
What? Pure
(No Side Effects)
What? Pure
Immutable Only
What? Pure
Referential Transparency
What? Pure
Functions alwaysreturn the same value.
If v = f x, then you canalways replace f x with v.
What? Pure
Functions alwaysreturn the same value.
If v equals f x, then you canalways replace f x with v.
What?
A non-strict, purely functional programming language with strong,static type inference.
What?
A non-strict, purely functional programming language with strong,static type inference.
What? Non-strict
Be lazy.
What? Non-strict
Be lazy.
Ignore evaluation order.
Wait...
if everything is immutable and there
are no side effects and evaluation is lazy,
then how the hell do you do anything?
M O N A D SBring your own Semicolon
What? Monads
What? Monads
What? Monads
What? Monads
What? Monads
What? Monads
“Haskell is the world's finestimperative programming language.”
— Simon Peyton-Jones
What? Monads
“Haskell is the only language I know withfirst-class support for imperative programming.”
— SamB
What? Monads
“Haskell has no preferred imperative semantics, and the monad just lets you swap out the semantics according to your needs.”
— Jared Updike
What?
A non-strict, purely functional programming language with strong,static type inference.
What?
A non-strict, purely functional programming language with strong,static type inference.
What? Types
StrongStatic
Inference
What? Types
StrongStatic
Inference
What? Strong Types
Runtime valueshave types.
What? Strong Types
Runtime valueshave types.
like Java and Rubyunlike C and Assembly
(Not Weak)
What? Types
StrongStatic
Inference
What? Types
StrongStatic
Inference
What? Static Types
Source code expressionshave types.
What? Static Types
Source code expressionshave types.
like C and Javaunlike Ruby and JavaScript
(Not Dynamic)
What? Types
StrongStatic
Inference
What? Types
StrongStatic
Inference
What? Type Inference
Automatically determines types of variables.
What? Type Inference
Automatically determines types of variables.
like C# and Gounlike C and Java
(Not Manifest)
You don’t need totype the type!
S Y N E R G YPurity means types tell you a lot.
What? Type Purity
No side effectsmean,
What? Type Purity
No side effectsmean,
argument and returntypes limit what
a function can do.
What? Type Purity
“Haskell is so strict about type safety that randomly generated snippets of code that successfully type check are likely to do something useful, even if you've no idea what that useful thing is.”
— sigfpe
What? Type Purity
“Since when does "it compiles" equate to "it will run (correctly)"? We're talking about C, after all, not Haskell.”
— Sean Russell
What? Types
StrongStatic
Inference
What?
A non-strict, purely functional programming language with strong,static type inference.
What?
A non-strict, purely functional programming language with strong,static type inference.
Haskell
Who? Research Wadler Hudak Peyton-Jones
What? Non-strict Purely Functional Static Types
When? 1987 1990 2002 Now
Where? Portland Glasgow Microsoft
Why? Aesthetics Pragmatics Performance
How?
Haskell
Who? Research Wadler Hudak Peyton-Jones
What? Non-strict Purely Functional Static Types
When? 1987 1990 2002 Now
Where? Portland Glasgow Microsoft
Why? Aesthetics Pragmatics Performance
How?
How?
Just Downloadand Install
the Platform
How?
Just Downloadand Install
the Platform
Glorious Glasgow Haskell Compilation
System
GHC
Compiler ghc
Interactive ghci
Scripts runghc
GHC
Compiler ghc
Interactive ghci
Scripts runghc
Haskell
Who? Research Wadler Hudak Peyton-Jones
What? Non-strict Purely Functional Static Types
When? 1987 1990 2002 Now
Where? Portland Glasgow Microsoft
Why? Aesthetics Pragmatics Performance
How? Platform Hoogle Hackage
Haskell
Who? Research Wadler Hudak Peyton-Jones
What? Non-strict Purely Functional Static Types
When? 1987 1990 2002 Now
Where? Portland Glasgow Microsoft
Why? Aesthetics Pragmatics Performance
How? Platform Hoogle Hackage
Example
Example
┼ $
$ ghci
$ ghciGHCi, version 7.0.2: http://www.haskell.org/ghc/ :? for helpLoading package ghc-prim ... linking ... done.Loading package integer-gmp ... linking ... done.Loading package base ... linking ... done.Loading package ffi-1.0 ... linking ... done.ghci>
$ ghciGHCi, version 7.0.2: http://www.haskell.org/ghc/ :? for helpLoading package ghc-prim ... linking ... done.Loading package integer-gmp ... linking ... done.Loading package base ... linking ... done.Loading package ffi-1.0 ... linking ... done.ghci> "hello, world"
$ ghciGHCi, version 7.0.2: http://www.haskell.org/ghc/ :? for helpLoading package ghc-prim ... linking ... done.Loading package integer-gmp ... linking ... done.Loading package base ... linking ... done.Loading package ffi-1.0 ... linking ... done.ghci> "hello, world""hello, world"ghci>
"hello, world"ghci>
"hello, world"ghci> 6 * 9
"hello, world"ghci> 6 * 942ghci>
"hello, world"ghci> 6 * 942ghci> [1, 2, 3]
"hello, world"ghci> 6 * 942ghci> [1, 2, 3][1,2,3]ghci>
"hello, world"ghci> 6 * 942ghci> [1, 2, 3][1,2,3]ghci> it
"hello, world"ghci> 6 * 942ghci> [1, 2, 3][1,2,3]ghci> it[1,2,3]ghci>
“Function application is so important in Haskell that we denote it using the quietest possible syntax: nothing at all.”
— Simon Peyton-Jones
"hello, world"ghci> 6 * 942ghci> [1, 2, 3][1,2,3]ghci> it[1,2,3]ghci> reverse it
“Function application is so important in Haskell that we denote it using the quietest possible syntax: nothing at all.”
— Simon Peyton-Jones
“Function application is so important in Haskell that we denote it using the quietest possible syntax: nothing at all.”
— Simon Peyton-Jones
"hello, world"ghci> 6 * 942ghci> [1, 2, 3][1,2,3]ghci> it[1,2,3]ghci> reverse it
"hello, world"ghci> 6 * 942ghci> [1, 2, 3][1,2,3]ghci> it[1,2,3]ghci> reverse it[3,2,1]ghci>
[3,2,1]ghci>
[3,2,1]ghci> let xs = [1..100]
[3,2,1]ghci> let xs = [1..100]ghci>
[3,2,1]ghci> let xs = [1..100]ghci> xs
[3,2,1]ghci> let xs = [1..100]ghci> xs[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100]ghci>
[3,2,1]ghci> let xs = [1..100]ghci> xs[1,2,⋯,99,100]ghci>
[3,2,1]ghci> let xs = [1..100]ghci> xs[1,2,⋯,99,100]ghci> [x | x <- xs, x > 21]
[3,2,1]ghci> let xs = [1..100]ghci> xs[1,2,⋯,99,100]ghci> [x | x <- xs, x > 21][22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100]ghci>
[3,2,1]ghci> let xs = [1..100]ghci> xs[1,2,⋯,99,100]ghci> [x | x <- xs, x > 21][22,23,⋯,99,100]ghci>
[3,2,1]ghci> let xs = [1..100]ghci> xs[1,2,⋯,99,100]ghci> [x | x <- xs, x > 21][22,23,⋯,99,100]ghci> filter (> 21) xs
[3,2,1]ghci> let xs = [1..100]ghci> xs[1,2,⋯,99,100]ghci> [x | x <- xs, x > 21][22,23,⋯,99,100]ghci> filter (> 21) xs[22,23,⋯,99,100]ghci>
[3,2,1]ghci> let xs = [1..100]ghci> xs[1,2,⋯,99,100]ghci> [x | x <- xs, x > 21][22,23,⋯,99,100]ghci> filter (> 21) xs[22,23,⋯,99,100]ghci> filter (\x -> x > 21) xs
TMTOWTDI
[3,2,1]ghci> let xs = [1..100]ghci> xs[1,2,⋯,99,100]ghci> [x | x <- xs, x > 21][22,23,⋯,99,100]ghci> filter (> 21) xs[22,23,⋯,99,100]ghci> filter (\x -> x > 21) xs[22,23,⋯,99,100]ghci>
TMTOWTDI
1. Freshman2. Sophomore3. Junior (Peano)4. Junior (Ban n+k)5. Senior (Leans Right)6. Senior (Leans Left)7. Senior (Leans Around)8. Memoizing9. Points-free10. Iterative11. Iterative one-liner12. Accumulating
13. Continuation-passing14. Boy Scout15. Combinatory16. List-encoding17. Interpretive18. Static19. Beginning Graduate20. Origamist21. Cartesianally-inclined22. Ph.D.23. Post-doc24. Tenured Professor
The Evolution of a Haskell Programmer
TMTOWTDI
TMTOWTDI
[3,2,1]ghci> let xs = [1..100]ghci> xs[1,2,⋯,99,100]ghci> [x | x <- xs, x > 21][22,23,⋯,99,100]ghci> filter (> 21) xs[22,23,⋯,99,100]ghci> filter (\x -> x > 21) xs[22,23,⋯,99,100]ghci>
[22,23,⋯,99,100]ghci>
[22,23,⋯,99,100]ghci> let divides = \x y -> rem y x == 0
[22,23,⋯,99,100]ghci> let divides = \x y -> rem y x == 0ghci>
[22,23,⋯,99,100]ghci> let divides = \x y -> rem y x == 0ghci> let divides x y = rem y x == 0
[22,23,⋯,99,100]ghci> let divides = \x y -> rem y x == 0ghci> let divides x y = rem y x == 0ghci>
[22,23,⋯,99,100]ghci> let divides = \x y -> rem y x == 0ghci> let divides x y = rem y x == 0ghci> divides 3 12
[22,23,⋯,99,100]ghci> let divides = \x y -> rem y x == 0ghci> let divides x y = rem y x == 0ghci> divides 3 12Trueghci>
[22,23,⋯,99,100]ghci> let divides = \x y -> rem y x == 0ghci> let divides x y = rem y x == 0ghci> divides 3 12Trueghci> 3 `divides` 12
[22,23,⋯,99,100]ghci> let divides = \x y -> rem y x == 0ghci> let divides x y = rem y x == 0ghci> divides 3 12Trueghci> 3 `divides` 12Trueghci>
[22,23,⋯,99,100]ghci> let divides = \x y -> rem y x == 0ghci> let divides x y = rem y x == 0ghci> divides 3 12Trueghci> 3 `divides` 12Trueghci> let divisors x = [d | d <- [1..x], d `divides` x]
[22,23,⋯,99,100]ghci> let divides = \x y -> rem y x == 0ghci> let divides x y = rem y x == 0ghci> divides 3 12Trueghci> 3 `divides` 12Trueghci> let divisors x = [d | d <- [1..x], d `divides` x]ghci>
[22,23,⋯,99,100]ghci> let divides = \x y -> rem y x == 0ghci> let divides x y = rem y x == 0ghci> divides 3 12Trueghci> 3 `divides` 12Trueghci> let divisors x = [d | d <- [1..x], d `divides` x]ghci> divisors 100
Fundamentals
[22,23,⋯,99,100]ghci> let divides = \x y -> rem y x == 0ghci> let divides x y = rem y x == 0ghci> divides 3 12Trueghci> 3 `divides` 12Trueghci> let divisors x = [d | d <- [1..x], d `divides` x]ghci> divisors 100[1,2,4,5,10,20,25,50,100]ghci>
Fundamentals
Data Declaration
data Color = Red | Green | Blue
Variables
red = Red-- Lower case for variable and upper case for constructor.
nan = 0 / 0-- Variables stand for values. They do not label locations.-- Don't need let because we aren't at GHCi.-- Declarations go at top-level, not expressions. (Like Java, unlike Ruby.)
Functions
hue Red = 0hue Green = 120hue Blue = 240
Lambda & Case
hue = \c -> case c of Red -> 0 Green -> 120 Blue -> 240
Pattern Wildcard
isRed Red = TrueisRed _ = False
Boolean Data
data Bool = True | False
Boolean Functions
otherwise = True
not True = Falsenot False = True
Boolean Operators
True && x = xFalse && _ = False
(||) True _ = True(||) False x = x-- Parenthesis let us use operators prefix.
Operator Precedence
infixr 3 &&infixr 2 ||
-- Ten precedence levels: 0 binds least tightly, 9 (default) binds most tightly.
-- Three associativities: infixl (default), infixr, infix (non-associative).
Types
“Types in Haskell express high-level design in the same way that UML diagrams do in Object Oriented languages.”
— Simon Peyton-Jones
Types
“Types in Haskell express high-level design in the same way that UML diagrams do in Object Oriented languages.”
— Simon Peyton-Jones
ghci> :l example
ghci> :l example[1 of 1] Compiling Main ( example.hs, interpreted )Ok, modules loaded: Main.ghci>
ghci> :l example[1 of 1] Compiling Main ( example.hs, interpreted )Ok, modules loaded: Main.ghci> :r
ghci> :l example[1 of 1] Compiling Main ( example.hs, interpreted )Ok, modules loaded: Main.ghci> :rOk, modules loaded: Main.ghci>
ghci> :l example[1 of 1] Compiling Main ( example.hs, interpreted )Ok, modules loaded: Main.ghci> :rOk, modules loaded: Main.ghci> :type red
ghci> :l example[1 of 1] Compiling Main ( example.hs, interpreted )Ok, modules loaded: Main.ghci> :rOk, modules loaded: Main.ghci> :type redRed :: Colorghci>
ghci> :l example[1 of 1] Compiling Main ( example.hs, interpreted )Ok, modules loaded: Main.ghci> :rOk, modules loaded: Main.ghci> :type redRed :: Colorghci> :t isRed
red :: Colornan :: Double
hue :: Color -> Double
otherwise :: Boolnot :: Bool -> Bool(&&), (||) :: Bool -> Bool -> Bool
Typesghci> :l example[1 of 1] Compiling Main ( example.hs, interpreted )Ok, modules loaded: Main.ghci> :rOk, modules loaded: Main.ghci> :type redRed :: Colorghci> :t isRedisRed :: Color -> Boolghci>
red :: Colornan :: Double
hue :: Color -> Double
otherwise :: Boolnot :: Bool -> Bool(&&), (||) :: Bool -> Bool -> Bool
Types
Recursive Data
data Color = Red | Green | Blue | Mix Color Color
Recursive Data
data Color = Red | Green | Blue | Mix Color Color
mix :: Color -> Color -> Colormix = Mix-- Mix is a type constructor function.
Recursive Data
yellow, cyan, magenta :: Coloryellow = Mix Red Green
Mix cyan magenta = Mix (Mix Green Blue) (Mix Red Blue)-- Constructor functions can be used for pattern matching, but variables bind.
Recursive Functions
isRed :: Color -> BoolisRed Red = TrueisRed (Mix c c') = isRed c && isRed c'isRed _ = False
Recursive Functions
hue :: Color -> Doublehue Red = 0hue Green = 120hue Blue = 240
Recursive Functions
hue :: Color -> Doublehue Red = 0hue Green = 120hue Blue = 240
hue (Mix c c') = ???
hue (Mix c c') = ???
h
hue (Mix c c') = ???
h
hue (Mix c c') = let h = hue c ???
h
hue (Mix c c') = let h = hue c ???
h'
h
hue (Mix c c') = let h = hue c h' = hue c' ???
h'
h
hue (Mix c c') = let h = hue c h' = hue c' ???
h' m
hue (Mix c c') = let h = hue c h' = hue c' m = average h h' ???
h
h' m
h
h' mhue (Mix c c') = let h = hue c h' = hue c' m = average h h' average x y = abs (x + y) / 2 ???
h
h' mhue (Mix c c') = let h = hue c h' = hue c' m = average h h' average x y = abs (x + y) / 2 in m
h
h' mhue (Mix c c') = let h = hue c h' = hue c' m = average h h' average x y = abs (x + y) / 2 in ??!
h
m
h'
hue (Mix c c') = let h = hue c h' = hue c' m = average h h' average x y = abs (x + y) / 2 in ??!
h
m
h'
hue (Mix c c') = let h = hue c h' = hue c' m = average h h' average x y = abs (x + y) / 2 in ??!
m'
h
m
h' m'
hue (Mix c c') = let h = hue c h' = hue c' m = average h h' m' = m + 180 average x y = abs (x + y) / 2 in ??!
h
m
h' m'
hue (Mix c c') = let h = hue c h' = hue c' m = average h h' m' = m + 180 average x y = abs (x + y) / 2 in ??!
d
h
m
h' m'
dhue (Mix c c') = let h = hue c h' = hue c' m = average h h' m' = m + 180 d = distance h m average x y = abs (x + y) / 2 in ??!
h
m
h' m'
dhue (Mix c c') = let h = hue c h' = hue c' m = average h h' m' = m + 180 d = distance h m average x y = abs (x + y) / 2 distance x y = abs (x - y) in ??!
h
m
h' m'
dhue (Mix c c') = let h = hue c h' = hue c' m = average h h' m' = m + 180 d = distance h m average x y = abs (x + y) / 2 distance x y = abs (x - y) in case compare d 90 of LT -> m EQ -> ??! GT -> m'
h
m
h' m'
dhue (Mix c c') = let h = hue c h' = hue c' m = average h h' m' = m + 180 d = distance h m average x y = abs (x + y) / 2 distance x y = abs (x - y) in case compare d 90 of LT -> m EQ -> nan GT -> m'
h
m
h' m'
dhue (Mix c c') = r where r = case compare d 90 of LT -> m EQ -> nan GT -> m' h = hue c h' = hue c' m = average h h' m' = m + 180 d = distance h m average x y = abs (x + y) / 2distance x y = abs (x - y)
test-framework organize tests
HUnit what you’re used to
QuickCheck test properties with
automatically generated data
Testing
QuickCheck
prop_hue_bounds c = let h = hue c in isNaN h || 0 <= h && h < 360
QuickCheck
prop_hue_bounds c = let h = hue c in isNaN h || 0 <= h && h < 360
prop_hue_mix_reflexivity c = let h = hue c in isNaN h || hue (Mix c c) == h
QuickCheck
prop_hue_bounds c = let h = hue c in isNaN h || 0 <= h && h < 360
prop_hue_mix_reflexivity c = let h = hue c in isNaN h || hue (Mix c c) == h
prop_hue_mix_commutativity c c' = let h = hue (Mix c c') in isNaN h || hue (Mix c' c) == h
QuickCheck
prop_hue_bounds c = let h = hue c in isNaN h || 0 <= h && h < 360
prop_hue_mix_reflexivity c = let h = hue c in isNaN h || hue (Mix c c) == h
prop_hue_mix_commutativity c c' = let h = hue (Mix c c') in isNaN h || hue (Mix c' c) == h
ghci> quickCheck prop_hue_mix_commutativity
ghci> quickCheck prop_hue_mix_commutativity+++ OK, passed 100 tests.ghci>
ghci> quickCheck prop_hue_mix_commutativity+++ OK, passed 100 tests.ghci> quickCheck prop_hue_mix_reflexivity
ghci> quickCheck prop_hue_mix_commutativity+++ OK, passed 100 tests.ghci> quickCheck prop_hue_mix_reflexivity+++ OK, passed 100 tests.ghci>
ghci> quickCheck prop_hue_mix_commutativity+++ OK, passed 100 tests.ghci> quickCheck prop_hue_mix_reflexivity+++ OK, passed 100 tests.ghci> quickCheck prop_hue_bounds
ghci> quickCheck prop_hue_mix_commutativity+++ OK, passed 100 tests.ghci> quickCheck prop_hue_mix_reflexivity+++ OK, passed 100 tests.ghci> quickCheck prop_hue_bounds*** Failed! Falsifiable (after 3 tests): Mix (Mix Red Blue) (Mix Green Red)ghci>
ghci> quickCheck prop_hue_mix_commutativity+++ OK, passed 100 tests.ghci> quickCheck prop_hue_mix_reflexivity+++ OK, passed 100 tests.ghci> quickCheck prop_hue_bounds*** Failed! Falsifiable (after 3 tests): Mix (Mix Red Blue) (Mix Green Red)ghci> hue (Mix magenta yellow)
ghci> quickCheck prop_hue_mix_commutativity+++ OK, passed 100 tests.ghci> quickCheck prop_hue_mix_reflexivity+++ OK, passed 100 tests.ghci> quickCheck prop_hue_bounds*** Failed! Falsifiable (after 3 tests): Mix (Mix Red Blue) (Mix Green Red)ghci> hue (Mix magenta yellow)360.0ghci>
hue (Mix c c') = r where r = case compare d 90 of LT -> m EQ -> nan GT -> m' h = hue c h' = hue c' m = average h h' m' = m + 180 d = distance h m
m'm
hd
h'
hue (Mix c c') = r where r = case compare d 90 of LT -> m EQ -> nan GT -> m' h = hue c h' = hue c' m = average h h' m' = normalize (m + 180) d = distance h m
m'm
hd
h'
hue (Mix c c') = r where r = case compare d 90 of LT -> m EQ -> nan GT -> m' h = hue c h' = hue c' m = average h h' m' = normalize (m + 180) d = distance h m
normalize h | h < 360 = h | otherwise = h - 360
m'm
hd
h'
hue (Mix c c') = r where r = case compare d 90 of LT -> m EQ -> nan GT -> m' h = hue c h' = hue c' m = average h h' m' = normalize (m + 180) d = distance h m
normalize h | h < 360 = h | otherwise = normalize (h - 360)
m'm
hd
h'
hue (Mix c c') = r where r = case compare d 90 of LT -> m EQ -> nan GT -> m' h = hue c h' = hue c' m = average h h' m' = normalize (m + 180) d = distance h m
normalize h | h < 360 = h | otherwise = normalize (h - 360)
m'm
hd
h'ghci>
m'
ghci> quickCheck prop_hue_bounds
m'
ghci> quickCheck prop_hue_bounds+++ OK, passed 100 tests.ghci>
prop_hue_mix_nothing c c' = distance (hue c) (hue c') == 180 ==> isNaN (hue (Mix c c'))
QuickCheck
QuickCheck
prop_hue_mix_nothing c c' = distance (hue c) (hue c') == 180 ==> isNaN (hue (Mix c c'))
ghci> quickCheck prop_hue_mix_nothing
QuickCheck
prop_hue_mix_nothing c c' = distance (hue c) (hue c') == 180 ==> isNaN (hue (Mix c c'))
ghci> quickCheck prop_hue_mix_nothing*** Gave up! Passed only 23 tests.ghci>
QuickCheck
prop_hue_mix_nothing c c' = distance (hue c) (hue c') == 180 ==> isNaN (hue (Mix c c'))
QuickCheck
prop_hue_mix_nothing c c' = distance (hue c) (hue c') == 180 ==> isNaN (hue (Mix c c'))
-- Can we easily find the complement of a color?
complement Red = ???complement Green = ???complement Blue = ???complement (Mix c c') = ???
complement Red = cyancomplement Green = ???complement Blue = ???complement (Mix c c') = ???
complement Red = cyancomplement Green = magentacomplement Blue = ???complement (Mix c c') = ???
complement Red = cyancomplement Green = magentacomplement Blue = yellowcomplement (Mix c c') = ???
complement Red = cyancomplement Green = magentacomplement Blue = yellowcomplement (Mix c c') = Mix ??? ???
complement Red = cyancomplement Green = magentacomplement Blue = yellowcomplement (Mix c c') = Mix (complement c ) (complement c' )
QuickCheck
prop_complement c = let h = hue c in not (isNaN h) ==> distance h (hue (complement c)) == 180
QuickCheck
prop_complement c = let h = hue c in not (isNaN h) ==> distance h (hue (complement c)) == 180
prop_hue_mix_complement c = isNaN (hue (Mix c (complement c)))
QuickCheck
prop_complement c = let h = hue c in not (isNaN h) ==> distance h (hue (complement c)) == 180
prop_hue_mix_complement c = isNaN (hue (Mix c (complement c)))
ghci> quickCheck prop_complement
ghci> quickCheck prop_complement+++ OK, passed 100 tests.ghci>
ghci> quickCheck prop_complement+++ OK, passed 100 tests.ghci> quickCheck prop_hue_mix_complement
ghci> quickCheck prop_complement+++ OK, passed 100 tests.ghci> quickCheck prop_hue_mix_complement+++ OK, passed 100 tests.ghci>
To be continued...
Summary
Haskell is functional.
Haskell has types.
QuickCheck is cool.
Preview: Infinite Lists
primes = sieve [2..] where sieve (p:xs) = p : sieve [x | x <- xs, rem x p /= 0]
Preview: IO (echo.hs)
import System.Environment (getArgs)import Data.List (intercalate)
main = do args <- getArgs putStrLn (intercalate " " args)
Preview: Parsers
Preview: Parse JSON
data Value = String String | Number Double | Object [(String, Value)] | Array [Value] | Bool Bool | Null
Preview: Parse JSON
value = String <$> jsstring <|> Number <$> number <|> Object <$> commaGroup '{' pair '}' <|> Array <$> commaGroup '[' value ']' <|> Bool True <$ string "true" <|> Bool False <$ string "false" <|> Null <$ string "null"
Preview: Parse JSON
pair :: Parser (String, Value)pair = do s <- jsstring sp_char_sp ':' v <- value spaces return (s, v)
To be continued...