Topic: Introduction and Administrivia Date: Sep. 23, 2009 Number: 1 Examples: introExamples.hs Handouts: UnixGuide.pdf Reading: SOE Preface, Chap. 1 (pg. xiii-xviii and 1-20) -- Announcement: Short assignment 1 is posted, but is not due until Monday. You should be able to solve the first two parts after today's lecture and the rest after tomorrow's lecture. (We ARE using tomorrow's x-hour.) -- Sections: We will be holding section meetings this term on Thursdays. Please sent email to Andrei Furtuna giving times that you would be available to meet, starting on the hour between 9 and 6, for 50 minutes. --- What is this course about? Now that you've got some programming experience, let's use it to do some fun stuff! We'll construct animations, analyze biological sequences, search social networks, play games, parse and manipulate HTML, identify clusters in data sets, solve puzzles like Sudoku, play the Kevin Bacon game on a movie database.... The final program is to write a Tetris programs. While we're doing all this, we'll develop expertise in core programming techniques useful throughout computer science. We'll use a language called Haskell, which makes it easy to quickly and incrementally develop solutions to these problems, and also really makes clear (and enforces) the underlying core concepts. You know how to program in one paradigm, which is an object-oriented procedural paradigm. Assignment statements, loops, and the ideas of sequential processing are basic building blocks. In the functional paradigm the emphasis is different. Recursion and functions come to the fore, especially higher-order functions (functions that take functions as arguments, and may return functions as values!). At first this approach may seem weird, but as you get into the course you should come to appreciate its power to allow you to abstract and to think about and express your programs at a higher level. The programs that you write in Haskell will usually be much shorter than equivalent programs in Java. We will often be able to do in a line or two what would take half a page or so in a procedural language. There are also practical reasons for learning functional programming. An article in Dr. Dobb's linked to the course web site titled "It's Time to Get Good at Functional Programming" explains why functional programming is being used by companies like Ericsson for applications on multi-core processors. The higher level of abstraction and the lack of side effects make it much easier to parallelize a functional program than a procedural one. "Real World Haskell" is an O'Reilley book linked to the web site. As the title implies, it presents a number of real-world applications of Haskell programming. This is not, however, a course about the Haskell language! Haskell just happens to be the notation that we have chosen for writing programs. The major goals of the course are to develop your skills and vocabulary for reasoning clearly about programs and programming, and to provide a toolbox of modern programming techniques that will be applicable in almost any language. Another is to teach you about a number of important applications (e.g. relational databases), algorithms (e.g. k-means clustering, breath-first search), and abstract data types (e.g. graphs, priority queues). --- Meet your course staff: - Scot Drysdale, Instructor - Andrei Furtuna, Graduate TA - Katelin Bailey, Section Leader - Karn Seth, Section Leader - Ken Greim Smith, Section Leader Lectures: 10:00-11:05, MWF 006 Kemeny Hall X-hour: 12:00-12:50pm Thurs. I have plans to use several of this term's x-hours (exam review, I will be out of town one Friday, and I will do an x-hour on an optional topic). I may schedule more x-hours, so please keep your x-hours open. In particular, we will use tomorrow's x-hour! --- Important course structure information: 1. Homework: Seven larger problem sets: 50% total Short assignments (about a dozen): 10% total Two exams (midterm, final): 20% each (40% total) 2. Homework is due no later than class time on the due date. For problem sets, the code must be mailed by 2 am of the date due. For the complete policy on homework due dates and lateness, see the Course Information page: http://www.cs.dartmouth.edu/~cs8/W2009/info.shtml#submit Caveat lector: Homework comprises more than half the grade in this course, so please do NOT fall behind! It is very hard to catch up once you fall behind. -- Honor code - The work that you do should be your own. You must work ALONE on short assignments. - You may work in pairs on most of the problem sets. Each problem set will say whether is is to be done alone or if it can be done in pairs. You can always choose to work alone. - Whenever you work in a pair, you both hand in a SINGLE joint assignment: 1. You must work with the same person for the entire assignment. 2. You may not share code with anyone else besides your partner. - Otherwise, you may discuss ideas with others, but write your own code yourself. Never copy someone else's code. You can help debug someone else's code after you have written your code for that part of the problem. Cheating is a VERY serious issue, and we take it as such; please read over the Course Information and the Dartmouth Honor Code. See the course info page on the web. If you are ever unsure about anything, please ask first! DO NOT look at solutions from previous terms of CS 8 or CS 18, whether written by students, or course staff. Also, DO NOT look at previous exams; we will supply you with practice exams to study from. Last Spring in CS 5 two students got suspended for 3 and 4 terms for using a solution from a previous term. I don't want to have something like that happen in this course. --- The Web Site The course web page is at http://www.cs.dartmouth.edu/~cs8/W2009/ --- Textbook Our textbook is School of Expression (SOE) by Paul Hudak. I will assign readings and assignments from it. I like a lot of things about the book, but one thing that is unfortunate is that it has a number of typos. Fortunately, there is an Errata page: http://www.haskell.org/soe/Bug/erata.htm I strongly suggest going through your book making corrections, to avoid confusion later. --- The Software We are using the Haskell language for this course. We will be using the Glasgow Haskell Compiler (GHC) with a graphics package SOE used by the book. Unfortunately, we will be using different implementations of this package for Macs and Windows/Linux. See the course software page for instructions on how to set this up on your own computers. NOTE - it may take many hours to install the software, so START EARLY. (You don't need to do anything or be there for most of that; you start it and the computer chugs away.) Machines will also be available for you to use in Rooms 001, 003, and 005. The software runs on Macs, Windows, and Linux, so take your choice. Room 002 will have lots of keyboards and monitors for those of you who bring your laptops to Sudikoff to work. (It also has a few Macs with Haskell installed.) Help sessions will be in 002 and/or 003. Installing software is probably the single most frustrating part of the course. Come to us for help; bring your laptop to evening labs or office hours. We will make house calls if you don't have a laptop. The good news is that you only have to do it once! --- These Lecture Notes Lecture notes are there to help you; however, they are not intended to take the place of the lectures. They're my working notes, so they're simple, sometimes terse, and plain text. I have a tentative syllabus posted on the web site, based on what I did last time. It is a first approximation - I usually make some changes along the way. -- Key Idea: Abstraction One of the most important themes in this course is abstraction: - Identify a pattern (computational or otherwise). - Give it a name. - Define its interface (i.e., describe inputs/outputs and their relationships to each other) Proper abstraction allows us to treat complex ideas as "black boxes" so that we can reason about their behavior about without knowing their exact contents. For instance, we can abstract the notion of square root: +------+ x ---->| SQRT |---> sqrt(x) +------+ This black box takes x as input and gives sqrt(x) as output; its name is SQRT. Inside the box we don't really care what we do, as long as it preserves the exterior relationships: We could use Newton's method, or something different. Same analogy applies to standard interchangeable parts (a big revolution in military and industrial technology when this happened!) Same deal with stereo components, etc. Lots of different pieces, but with standard connections and cables, so you can hook them up as you please. Same with computers - USB ports, video output, headphone mini-jacks, etc. --- Describing Processes In order to talk about computational processes, we need to have a kind of notation for describing them. Ideally, we want a notation that can be read by the computer as well as by humans. Our particular choice is a language called Haskell. It is close to mathematical notation, and we will see the concept of "computation by calculation". In next lecture I will give a more complete introduction to the various types, etc. that are allowed in Hakell. You should know that Haskell is strongly typed. It tries very hard to make sure that the expressions that you write are properly formed. It sometimes feels like it tries TOO hard. But the good news: most errors get caught by the compiler, rather than at run time. Once you get the type errors fixed the program often runs correctly. For now we will look at 6 types: Double, Float, Integer, Int, Char, Bool. What is the difference between Int and Integer? Int has fixed number of digits, like int in Java. Integer doesn't. Arbitrary precision. So we can say (where ^ is raising to an integral power): Start ghci in terminal window. 2^20 2^100 2^500 2^10000 Characters are surrounded by single quotes: 'b' There are also built-in structures. We will look at two early in the course: List -- [1, 2, 3] Tuple -- (1, 2, 3) Difference? List can have any length (even infinite!), but all elements same Type. Tuples have FIXED length, but each position can be a different type. More in a later lecture. -- Lists: Expression type [1, 2, 3] [Integer] [1.0, 2.0, 3.0, 4.0] [Doubles] [True, False, True, True] [Bools] ['a', 'b', 'c','d'] [Char] Wait - why does this print as "abcd"? In Haskell, a string is not a built-in type, it is a list of characters! Double quotes are "syntactic sugar" for representing a list of characters. ["cat", "dog", "bear"] [[Char]] Operations on lists: head - return first item in a list tail - return the list without the first item : - Construct a list out of a first element and a rest (head and tail) 1:[2, 3] => [1,2,3] == 1:2:3:[] (: is right associative!) Note that head lst : tail lst => lst (if length lst >= 1) So what can we do with this stuff? Assign names. pi :: Double ( :: means "is of type") pi = 3.14159 (Note - pi is a builtin constant, so don't need do this.) lst :: [Double] lst = [1.0, 2.0, 3.0, 4.0] CAN ONLY ASSIGN ONCE in a given scope. So x = 0 x = x+1 not possible in normal circumstances. So how do we do loops, sums, etc? Functions. Note on interpreter - it is useful to be able to bind names to values at the top level of the interpreter. To do so, we use a special construct: put "let" before the assignment. let lst = [1,3,5] Will see that this is a special case of something more general by the end of the term. Note: if you want to specify the type of lst, you do it all on the same line: let lst = [1,3,5] :: [Double] tells it to interpret the numbers in the list as doubles. (Note that the expression 3 could be an Int, Integer, Float, or Double.) name functions: Demo :load introExamples.hs Mention :cd (change directory), :q (quit), :r (reload) also -- Computes the area of a circle of a given radius circleArea :: Double -> Double (Function from Double to Double) circleArea radius = pi * radius^2 (radius is a parameter, type Double) Demo it Note: the Java equivalent is: public double circleArea(double radius) { return Math.PI * radius * radius } -- Higher Order Functions Will see ways to build functions that make choices tomorrow, but for now we look at some built-in higher-order functions. They make it easy to abstract operations. This idea of higher-order functions appears in Chapter 5, but I wanted you to see it in the first lecture. It is one of the "new" ideas that give functional languages their power. I hope that I am not going too fast, but if you see the way that you are used to solving problems first it will be harder to get you thinking in this alternate way. In functional languages functions are first-class objects - we can create them, assign them names, pass them as parameters, and even return them as the value of a function! This lecture will just touch on them; we will return later to look at them in detail. Suppose I want to compute a whole bunch of circle areas. In Java, write a loop, use subscripts. In Hakell, function map: Apply a function to every item in a list: map function list -- Computes the areas of circles whose radii are in a list circleAreas :: [Double] -> [Double] circleAreas radii = map circleArea radii Demo it --- GOT TO HERE -- Adds 1 to each item in a list incrementList :: [Double] -> [Double] incrementList list = map (+ 1) list Demo (+1) 5 ==> 6, Idea of "section" - any of the builtin binary functions can be turned into a one-parameter function by supplying one parameter. (+1), (*3), (5-), (> 0), (== 7) Exception: (-2) is ambiguous, so taken to be negative 2. Could write: (+ (-2)). Will see later that this is a special case of a more general operation called currying. Demo incrementList -- Triples each item in a list tripleList :: [Double] -> [Double] tripleList list = map (* 3) list Demo -- Scales each item in a list by multiplying it by a scale factor scaleList :: Double -> [Double] -> [Double] scaleList sf list = map (* sf) list Demo Note - 2-parameter function. Would expect (Double, [Double]) -> [Double] But not what Haskell does. Reason - something to do with currying, which we mentioned before. Will see later. We can use this definition to give an alternate tripleList: tripleList2 :: [Double] -> [Double] tripleList2 list = scaleList 3 list Another higher-order function: combine a list using an operation: foldl operator init_value list -- Sums the items in a list listSum :: [Double] -> Double listSum list = foldl (+) 0.0 list Note - If wrote "foldl + 0.0 list" the + looks like a sum. To pass an operator as a function, enclose in (). Whenever an infix operator is passed as a parameter or defined, etc., we put it into (). -- Computes the total area of circles whose radii are in a list totalCircleArea :: [Double] -> Double totalCircleArea radii = listSum (circleAreas radii) -- Computes the total area of circles whose radii are given - -- this time without helper functions totalCircleArea2 :: [Double] -> Double totalCircleArea2 radii = foldl (+) 0.0 (map circleArea radii) This version is not better than the earlier one, and we could argue that it is less clear. But you should know that it is possible. A third higher-order List function: filter. filter predicate list Keeps only items in the list for which the predicate is true. Boolean comparisons: <, <=, >=, > -- Inequality tests ==, /= -- Equality tests (Note that if you superimpose '/' and '=' you get the standard not equal symbol.) -- Counts the number of times that a base appears in a genome countBases :: Char -> [Char] -> Int countBases base genome = length (filter (== base) genome) length returns the length of a list. -- Sum only the positive numbers in a list sumPositives :: [Double] -> Double sumPositives list = listSum (filter (> 0) list) Fourth higher-order function: zipWith zipWith operation list1 list2 Combines the two lists by pairing up like positions and combining them using the operator. (If unequal lengths, quit when shorter list finished.) -- Create a merged list where each position contains the larger -- of the numbers in the corresponding postitions in list1 and list2 mergeLargest :: [Double] -> [Double] -> [Double] mergeLargest list1 list2 = zipWith max list1 list2 max, min are builtin functions. -- Computes the dot product of two vectors represented as lists dotProd :: [Double] -> [Double] -> Double dotProd a b = foldl (+) 0.0 (zipWith (*) a b) Compare this one-line definition to what it would require to write this function in Java. But that is not the main advantage. The main advantage is that you are thinking at a higher level of abstraction. It makes it easier to do things like parallelize computations. It is obvious that the multiplications of pairs in zipWith can be done in any order, and in fact each could be simultaneously solved on its own processor. But what about a Java loop? There is an explicit order. Is it important? How is the compiler supposed to figure that out? Google's parallel processing system is based on "map reduce". Map we have seen. Reduce is the Lisp name for foldl. So functional ideas underlie Google's compuational engine.