Jump to content

Haskell features

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Scravy~enwiki (talk | contribs) at 15:32, 9 February 2010 (created article, splitted from Haskell (programming language)). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
(diff) ← Previous revision | Latest revision (diff) | Newer revision → (diff)

Function calls

Applying a function f to a value x is expressed as simply f x.

Haskell distinguishes function calls from infix operators syntactically, but not semantically. Function names which are composed of punctuation characters can be used as operators, as can other function names if surrounded with backticks; and operators can be used in prefix notation if surrounded with parentheses.

This example shows the ways that functions can be called:

  add a b = a + b

  ten1 = 5 + 5
  ten2 = (+) 5 5
  ten3 = add 5 5
  ten4 = 5 `add` 5

Functions which are defined as taking several parameters can always be partially applied. Binary operators can be partially applied using section notation:

  ten5 = (+ 5) 5
  ten6 = (5 +) 5
  
  addfive = (5 +)
  ten7 = addfive 5

Layout

Haskell allows indentation to be used to indicate the beginning of a new declaration. For example, in a where clause:

product xs = prod xs 1
  where
    prod []     a = a
    prod (x:xs) a = prod xs (a*x)

The two equations for the nested function prod are aligned vertically, which allows the semi-colon separator to be omitted. In Haskell, indentation can be used in several syntactic constructs, including do, let, case, class, and instance.

The use of indentation to indicate program structure originates in Landin's ISWIM language, where it was called the off-side rule. This was later adopted by Miranda, and Haskell adopted a similar (but rather more complicated) version of Miranda's off-side rule, which it called "layout". Other languages to adopt whitespace-sensitive syntax include Python and F#.

The use of layout in Haskell is optional. For example, the function product above can also be written:

product xs = prod xs 1
  where { prod [] a = a; prod (x:xs) a = prod xs (a*x) }

The explicit open brace after the where keyword indicates that the programmer has opted to use explicit semi-colons to separate declarations, and that the declaration-list will be terminated by an explicit closing brace. One reason for wanting support for explicit delimiters is that it makes automatic generation of Haskell source code easier.

Haskell's layout rule has been criticised for its complexity. In particular, the definition states that if the parser encounters a parse error during processing of a layout section, then it should try inserting a close brace (the "parse error" rule). Implementing this rule in a traditional parsing/lexical-analysis combination requires two-way cooperation between the parser and lexical analyser, whereas in most languages these two phases can be considered independently.

List comprehensions

See List_comprehension#Overview for the Haskell example.

Namespaces

In the Haskell_(programming_language)#More_complex_examples section below, calc is used in two senses, showing that there is a Haskell type class namespace and also a namespace for values:

  1. a Haskell type class for calc. The domain and range can be explicitly denoted in a Haskell type class.
  2. a Haskell value, formula, or expression for calc.

Algebraic data types

Algebraic data types are used extensively in Haskell. Some examples of these are the built in list, Maybe and Either types:

-- A list of a's ([a]) is either an a consed (:) onto another list of a's, or an empty list ([])
data [a] = a : [a] | []
-- Something of type Maybe a is either Just something, or Nothing
data Maybe a = Just a | Nothing
-- Something of type Either atype btype is either a Left atype, or a Right btype
data Either a b = Left a | Right b

Users of the language can also define their own abstract data types. An example of an ADT used to represent a person's name, sex and age might look like:

data Sex = Male | Female
data Person = Person String Sex Int -- Notice that Person is both a constructor and a type

-- An example of creating something of type Person
tom :: Person
tom = Person "Tom" Male 27

Pattern matching

Pattern matching is used to match on the different constructors of algebraic data types. Here are some functions, each using pattern matching on each of the types above:

-- This type signature says that empty takes a list containing any type, and returns a Bool
empty :: [a] -> Bool
empty (x:xs) = False
empty [] = True

-- Will return a value from a Maybe a, given a default value in case a Nothing is encountered
fromMaybe :: a -> Maybe a -> a
fromMaybe x (Just y) = y
fromMaybe x Nothing  = x

isRight :: Either a b -> Bool
isRight (Right _) = True
isRight (Left _)  = False

getName :: Person -> String
getName (Person name _ _) = name

getSex :: Person -> Sex
getSex (Person _ sex _) = sex

getAge :: Person -> Int
getAge (Person _ _ age) = age

Using the above functions, along with the map function, we can apply them to each element of a list, to see their results:

map empty [[1,2,3],[],[2],[1..]]
-- returns [False,True,False,False]

map (fromMaybe 0) [Just 2,Nothing,Just 109238, Nothing]
-- returns [2,0,109238,0]

map isRight [Left "hello", Right 6, Right 23, Left "world"]
-- returns [False, True, True, False]

map getName [Person "Sarah" Female 20, Person "Alex" Male 20, tom]
-- returns ["Sarah", "Alex", "Tom"], using the definition for tom above
  • Abstract Types
  • Lists

Tuples

Tuples in haskell can be used to hold a fixed number of elements. They are used to group pieces of data of differing types:

account :: (String, Integer, Double) -- The type of a three-tuple, representing a name, balance, interest rate
account = ("John Smith",102894,5.25)

Tuples are commonly used in the zip* functions to place adjacent elements in separate lists together in tuples (zip4 to zip7 are provided in the Data.List module):

-- The definition of the zip function. Other zip* functions are defined similarly
zip :: [a] -> [b] -> [(a,b)]
zip (a:as) (b:bs) = (a,b) : zip as bs
zip _      _      = []

zip [1..5] "hello"
-- returns [(1,'h'),(2,'e'),(3,'l'),(4,'l'),(5,'o')]
-- and has type [(Integer, Char)]

zip3 [1..5] "hello" [False, True, False, False, True]
-- returns [(1,'h',False),(2,'e',True),(3,'l',False),(4,'l',False),(5,'o',True)]
-- and has type [(Integer,Char,Bool)]

In the GHC compiler, tuples are defined with sizes from 2 elements up to 62 elements.

  • Records

Type system

  • Type classes
  • Type defaulting
  • Overloaded Literals
  • Higher Kinded Polymorphism
  • Multi-Parameter Type Classes
  • Functional Dependencies


Monads and input/output

  • Overview
  • Applications
    • Monadic IO
    • Do-notation
    • References
    • Exceptions

ST monad

The ST monad allows programmers to write imperative algorithms in Haskell, using mutable variables (STRef's) and mutable arrays (STArrays and STUArrays). The advantage of the ST monad is that it allows programmers to write code that has internal side effects, such as destructively updating mutable variables and arrays, while containing these effects inside the monad. The result of this is that functions written using the ST monad appear completely pure to the rest of the program. This allows programmers to produce imperative code where it may be impractical to write functional code, while still keeping all the safety that pure code provides.

Here is an example program (taken from the Haskell wiki page on the ST monad) that takes a list of numbers, and sums them, using a mutable variable:

import Control.Monad.ST
import Data.STRef
import Control.Monad

sumST :: Num a => [a] -> a
sumST xs = runST $ do            -- runST takes stateful ST code and makes it pure.
    summed <- newSTRef 0         -- Create an STRef (a mutable variable)

    forM_ xs $ \x -> do          -- For each element of the argument list xs ..
        modifySTRef summed (+x)  -- add it to what we have in n.

    readSTRef summed             -- read the value of n, which will be returned by the runST above.

STM monad

The STM monad is an implementation of Software Transactional Memory in Haskell. It is implemented in the GHC compiler, and allows for mutable variables to be modified in transactions.


Arrows

  • Applicative Functors
  • Arrows

As Haskell is a pure functional language, functions cannot have side effects. Being non-strict, it also does not have a well-defined evaluation order. This is a challenge for real programs, which among other things need to interact with an environment. Haskell solves this with monadic types that leverage the type system to ensure the proper sequencing of imperative constructs. The typical example is I/O, but monads are useful for many other purposes, including mutable state, concurrency and transactional memory, exception handling, and error propagation.

Haskell provides a special syntax for monadic expressions, so that side-effecting programs can be written in a style similar to current imperative programming languages; no knowledge of the mathematics behind monadic I/O is required for this. The following program reads a name from the command line and outputs a greeting message:

main = do putStrLn "What's your name?"
          name <- getLine
          putStr ("Hello, " ++ name ++ "!\n")

The do-notation eases working with monads. This do-expression is equivalent to, but (arguably) easier to write and understand than, the de-sugared version employing the monadic operators directly:

main = putStrLn "What's your name?" >> getLine >>= \ name -> putStr ("Hello, " ++ name ++ "!\n")
See also wikibooks:Transwiki:List of hello world programs#Haskell for another example that prints text.

Concurrency and parallelism

The Haskell language definition itself does not include either concurrency or parallelism, although GHC supports both.

Concurrency

Concurrent Haskell is an extension to Haskell that provides support for threads and synchronization.[1] GHC's implementation of Concurrent Haskell is based on multiplexing lightweight Haskell threads onto a few heavyweight OS threads,[2] so that Concurrent Haskell programs run in parallel on a multiprocessor. The runtime can support millions of simultaneous threads.[3]

The GHC implementation employs a dynamic pool of OS threads, allowing a Haskell thread to make a blocking system call without blocking other running Haskell threads.[4] Hence the lightweight Haskell threads have the characteristics of heavyweight OS threads, and the programmer is unaware of the implementation details.

Recently, Concurrent Haskell has been extended with support for Software Transactional Memory (STM), which is a concurrency abstraction in which compound operations on shared data are performed atomically, as transactions.[5] GHC's STM implementation is the only STM implementation to date to provide a static compile-time guarantee preventing non-transactional operations from being performed within a transaction. The Haskell STM library also provides two operations not found in other STMs: retry and orElse, which together allow blocking operations to be defined in a modular and composable fashion.

Parallelism

Programming in the large

  • FFI
  • Modules
  • Packages

Semantics

  1. ^ Simon Peyton Jones, Andrew Gordon, and Sigbjorn Finne. Concurrent Haskell. ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages (PoPL). 1996. (Some sections are out of date with respect to the current implementation.)
  2. ^ Runtime Support for Multicore Haskell (Simon Marlow, Simon Peyton Jones, Satnam Singh) ICFP '09: Proceeding of the 14th ACM SIGPLAN international conference on Functional programming, Edinburgh, Scotland, August 2009
  3. ^ http://donsbot.wordpress.com/2009/09/05/defun-2009-multicore-programming-in-haskell-now/
  4. ^ Extending the Haskell Foreign Function Interface with Concurrency (Simon Marlow, Simon Peyton Jones, Wolfgang Thaller) Proceedings of the ACM SIGPLAN workshop on Haskell, pages 57--68, Snowbird, Utah, USA, September 2004
  5. ^ Tim Harris, Simon Marlow, Simon Peyton Jones, Maurice Herlihy "Composable memory transactions" Proceedings of the tenth ACM SIGPLAN symposium on Principles and practice of parallel programming, 2005