summaryrefslogtreecommitdiffhomepage
path: root/haskell.html.markdown
diff options
context:
space:
mode:
Diffstat (limited to 'haskell.html.markdown')
-rw-r--r--haskell.html.markdown211
1 files changed, 184 insertions, 27 deletions
diff --git a/haskell.html.markdown b/haskell.html.markdown
index 4ce1a839..328da5c9 100644
--- a/haskell.html.markdown
+++ b/haskell.html.markdown
@@ -1,5 +1,6 @@
---
language: Haskell
+filename: learnhaskell.hs
contributors:
- ["Adit Bhargava", "http://adit.io"]
---
@@ -60,15 +61,17 @@ not False -- True
-- A string is a list of characters
['H', 'e', 'l', 'l', 'o'] -- "Hello"
+
+-- Lists can be indexed with the `!!` operator followed by an index
"This is a string" !! 0 -- 'T'
----------------------------------------------------
--- Lists and Tuples
+-- 2. Lists and Tuples
----------------------------------------------------
-- Every element in a list must have the same type.
--- These two lists are the same:
+-- These two lists are equal:
[1, 2, 3, 4, 5]
[1..5]
@@ -77,11 +80,11 @@ not False -- True
-- You can create a step in a range.
[0,2..10] -- [0, 2, 4, 6, 8, 10]
-[5..1] -- This doesn't work because Haskell defaults to incrementing.
+[5..1] -- [] (Haskell defaults to incrementing)
[5,4..1] -- [5, 4, 3, 2, 1]
-- indexing into a list
-[1..10] !! 3 -- 4
+[1..10] !! 3 -- 4 (zero-based indexing)
-- You can also have infinite lists in Haskell!
[1..] -- a list of all the natural numbers
@@ -123,6 +126,9 @@ last [1..5] -- 5
fst ("haskell", 1) -- "haskell"
snd ("haskell", 1) -- 1
+-- pair element accessing does not work on n-tuples (i.e. triple, quadruple, etc)
+snd ("snd", "can't touch this", "da na na na") -- error! see function below
+
----------------------------------------------------
-- 3. Functions
----------------------------------------------------
@@ -152,14 +158,14 @@ fib x
| otherwise = fib (x - 1) + fib (x - 2)
-- Pattern matching is similar. Here we have given three different
--- definitions for fib. Haskell will automatically call the first
--- function that matches the pattern of the value.
+-- equations that define fib. Haskell will automatically use the first
+-- equation whose left hand side pattern matches the value.
fib 1 = 1
fib 2 = 2
fib x = fib (x - 1) + fib (x - 2)
--- Pattern matching on tuples:
-foo (x, y) = (x + 1, y + 2)
+-- Pattern matching on tuples
+sndOfTriple (_, y, _) = y -- use a wild card (_) to bypass naming unused value
-- Pattern matching on lists. Here `x` is the first element
-- in the list, and `xs` is the rest of the list. We can write
@@ -198,15 +204,15 @@ foo 5 -- 15
-- multiplies the result of that by 4, and then returns the final value.
foo = (4*) . (10+)
--- 4*(10 + 5) = 60
+-- 4*(10+5) = 60
foo 5 -- 60
-- fixing precedence
--- Haskell has another operator called `$`. This operator applies a function
--- to a given parameter. In contrast to standard function application, which
--- has highest possible priority of 10 and is left-associative, the `$` operator
+-- Haskell has an operator called `$`. This operator applies a function
+-- to a given parameter. In contrast to standard function application, which
+-- has highest possible priority of 10 and is left-associative, the `$` operator
-- has priority of 0 and is right-associative. Such a low priority means that
--- the expression on its right is applied as the parameter to the function on its left.
+-- the expression on its right is applied as a parameter to the function on its left.
-- before
even (fib 7) -- false
@@ -222,7 +228,7 @@ even . fib $ 7 -- false
-- 5. Type signatures
----------------------------------------------------
--- Haskell has a very strong type system, and every valid expression has a type.
+-- Haskell has a very strong type system, and every valid expression has a type.
-- Some basic types:
5 :: Integer
@@ -244,10 +250,10 @@ double x = x * 2
-- 6. Control Flow and If Expressions
----------------------------------------------------
--- if expressions
+-- if-expressions
haskell = if 1 == 1 then "awesome" else "awful" -- haskell = "awesome"
--- if expressions can be on multiple lines too, indentation is important
+-- if-expressions can be on multiple lines too, indentation is important
haskell = if 1 == 1
then "awesome"
else "awful"
@@ -289,19 +295,79 @@ foldr (\x y -> 2*x + y) 4 [1,2,3] -- 16
-- 7. Data Types
----------------------------------------------------
--- Here's how you make your own data type in Haskell
+-- A data type is declared with a 'type constructor' on the left
+-- and one or more 'data constructors' on the right, separated by
+-- the pipe | symbol. This is a sum/union type. Each data constructor
+-- is a (possibly nullary) function that creates an object of the type
+-- named by the type constructor.
+
+-- This is essentially an enum
data Color = Red | Blue | Green
-- Now you can use it in a function:
-
say :: Color -> String
-say Red = "You are Red!"
-say Blue = "You are Blue!"
-say Green = "You are Green!"
+say Red = "You are Red!"
+say Blue = "You are Blue!"
+say Green = "You are Green!"
+
+-- Note that the type constructor is used in the type signature
+-- and the data constructors are used in the body of the function
+-- Data constructors are primarily pattern-matched against
+
+-- This next one is a traditional container type holding two fields
+-- In a type declaration, data constructors take types as parameters
+-- Data constructors can have the same name as type constructors
+-- This is common where the type only has a single data constructor
+
+data Point = Point Float Float
+
+-- This can be used in a function like:
+
+distance :: Point -> Point -> Float
+distance (Point x y) (Point x' y') = sqrt $ dx + dy
+ where dx = (x - x') ** 2
+ dy = (y - y') ** 2
+
+-- Types can have multiple data constructors with arguments, too
+
+data Name = Mononym String
+ | FirstLastName String String
+ | FullName String String String
+
+-- To make things clearer we can use record syntax
+
+data Point2D = CartesianPoint2D { x :: Float, y :: Float }
+ | PolarPoint2D { r :: Float, theta :: Float }
+
+myPoint = CartesianPoint2D { x = 7.0, y = 10.0 }
+
+-- Using record syntax automatically creates accessor functions
+-- (the name of the field)
+
+xOfMyPoint = x myPoint
+
+-- xOfMyPoint is equal to 7.0
+
+-- Record syntax also allows a simple form of update
--- Your data types can have parameters too:
+myPoint' = myPoint { x = 9.0 }
+
+-- myPoint' is CartesianPoint2D { x = 9.0, y = 10.0 }
+
+-- Even if a type is defined with record syntax, it can be declared like
+-- a simple data constructor. This is fine:
+
+myPoint'2 = CartesianPoint2D 3.3 4.0
+
+-- It's also useful to pattern match data constructors in `case` expressions
+
+distanceFromOrigin x =
+ case x of (CartesianPoint2D x y) -> sqrt $ x ** 2 + y ** 2
+ (PolarPoint2D r _) -> r
+
+-- Your data types can have type parameters too:
data Maybe a = Nothing | Just a
@@ -310,8 +376,98 @@ Just "hello" -- of type `Maybe String`
Just 1 -- of type `Maybe Int`
Nothing -- of type `Maybe a` for any `a`
+-- For convenience we can also create type synonyms with the 'type' keyword
+
+type String = [Char]
+
+-- Unlike `data` types, type synonyms need no constructor, and can be used
+-- anywhere a synonymous data type could be used. Say we have the
+-- following type synonyms and items with the following type signatures
+
+type Weight = Float
+type Height = Float
+type Point = (Float, Float)
+getMyHeightAndWeight :: Person -> (Height, Weight)
+findCenter :: Circle -> Point
+somePerson :: Person
+someCircle :: Circle
+distance :: Point -> Point -> Float
+
+-- The following would compile and run without issue,
+-- even though it does not make sense semantically,
+-- because the type synonyms reduce to the same base types
+
+distance (getMyHeightAndWeight somePerson) (findCenter someCircle)
+
+----------------------------------------------------
+-- 8. Typeclasses
+----------------------------------------------------
+
+-- Typeclasses are one way Haskell does polymorphism
+-- They are similar to interfaces in other languages
+-- A typeclass defines a set of functions that must
+-- work on any type that is in that typeclass.
+
+-- The Eq typeclass is for types whose instances can
+-- be tested for equality with one another.
+
+class Eq a where
+ (==) :: a -> a -> Bool
+ (/=) :: a -> a -> Bool
+ x == y = not (x /= y)
+ x /= y = not (x == y)
+
+-- This defines a typeclass that requires two functions, (==) and (/=)
+-- It also declares that one function can be declared in terms of another
+-- So it is enough that *either* the (==) function or the (/=) is defined
+-- And the other will be 'filled in' based on the typeclass definition
+
+-- To make a type a member of a type class, the instance keyword is used
+
+instance Eq TrafficLight where
+ Red == Red = True
+ Green == Green = True
+ Yellow == Yellow = True
+ _ == _ = False
+
+-- Now we can use (==) and (/=) with TrafficLight objects
+
+canProceedThrough :: TrafficLight -> Bool
+canProceedThrough t = t /= Red
+
+-- You can NOT create an instance definition for a type synonym
+
+-- Functions can be written to take typeclasses with type parameters,
+-- rather than types, assuming that the function only relies on
+-- features of the typeclass
+
+isEqual (Eq a) => a -> a -> Bool
+isEqual x y = x == y
+
+-- Note that x and y MUST be the same type, as they are both defined
+-- as being of type parameter 'a'.
+-- A typeclass does not state that different types in the typeclass can
+-- be mixed together.
+-- So `isEqual Red 2` is invalid, even though 2 is an Int which is an
+-- instance of Eq, and Red is a TrafficLight which is also an instance of Eq
+
+-- Other common typeclasses are:
+-- Ord for types that can be ordered, allowing you to use >, <=, etc.
+-- Read for types that can be created from a string representation
+-- Show for types that can be converted to a string for display
+-- Num, Real, Integral, Fractional for types that can do math
+-- Enum for types that can be stepped through
+-- Bounded for types with a maximum and minimum
+
+-- Haskell can automatically make types part of Eq, Ord, Read, Show, Enum,
+-- and Bounded with the `deriving` keyword at the end of the type declaration
+
+data Point = Point Float Float deriving (Eq, Read, Show)
+
+-- In this case it is NOT necessary to create an 'instance' definition
+
----------------------------------------------------
--- 8. Haskell IO
+-- 9. Haskell IO
----------------------------------------------------
-- While IO can't be explained fully without explaining monads,
@@ -384,15 +540,15 @@ main'' = do
-- The type `IO` is an example of a "monad". The way Haskell uses a monad to
-- do IO allows it to be a purely functional language. Any function that
-- interacts with the outside world (i.e. does IO) gets marked as `IO` in its
--- type signature. This lets us reason about what functions are "pure" (don't
--- interact with the outside world or modify state) and what functions aren't.
+-- type signature. This lets us reason about which functions are "pure" (don't
+-- interact with the outside world or modify state) and which functions aren't.
-- This is a powerful feature, because it's easy to run pure functions
-- concurrently; so, concurrency in Haskell is very easy.
----------------------------------------------------
--- 9. The Haskell REPL
+-- 10. The Haskell REPL
----------------------------------------------------
-- Start the repl by typing `ghci`.
@@ -444,5 +600,6 @@ qsort (p:xs) = qsort lesser ++ [p] ++ qsort greater
There are two popular ways to install Haskell: The traditional [Cabal-based installation](http://www.haskell.org/platform/), and the newer [Stack-based process](https://www.stackage.org/install).
You can find a much gentler introduction from the excellent
-[Learn you a Haskell](http://learnyouahaskell.com/) or
+[Learn you a Haskell](http://learnyouahaskell.com/),
+[Happy Learn Haskell Tutorial](http://www.happylearnhaskelltutorial.com/) or
[Real World Haskell](http://book.realworldhaskell.org/).