[Responding to comments on ch03 Bryan O'Sullivan **20080318074730] { addfile ./examples/ch03/usingtypes.ghci addfile ./examples/ch03/Assign.hs addfile ./examples/ch03/assign.ghci addfile ./examples/ch03/assign.py hunk ./en/ch02-starting.xml 695 - type [Char]. + type [Char]. (The name String + is often used instead of [Char]. It is simply + a synonym for [Char].) hunk ./en/ch03-funcs-types.xml 8 - - Haskell's type system - - There are three interesting aspects to types in - Haskell: they are strong, they are - static, and they can be automatically - inferred. Let's talk in more detail about - each of these ideas. When possible, we'll present similarities - between concepts from Haskell's type system and related ideas in - other languages. + + Why care about types? hunk ./en/ch03-funcs-types.xml 21 + + Before we launch into a deeper discussion of Haskell's type + system, let's talk about why we should care about types at all: + what are they even for? At the lowest + level, a computer is concerned with bytes, with barely any + additional structure. What a type system gives us is + abstraction. A type adds meaning to plain + bytes: it lets us say these bytes are text, + those bytes are an airline reservation, and so + on. Usually, a type system goes beyond this to prevent us from + accidentally mixing types up: for example, a type system usually + won't let us treat a hotel reservation as a car rental + receipt. hunk ./en/ch03-funcs-types.xml 35 - Here are a few more examples of values and - expressions, and their types: the value 'a' - has type Char; the expression "a" ++ - "b" has type String (the - (++) is the concatenation operator). + The benefit of introducing abstraction is that it lets us + forget or ignore low-level details. If I know that a value in + my program is a string, I don't have to know the intimate + details of how strings are implemented: I can just assume that + my string is going to behave like all the other strings I've + worked with. hunk ./en/ch03-funcs-types.xml 42 - We call an expression that obeys the language's type rules - well typed. An expression that disobeys - the type rules, such as 10 + "hello", is - ill typed, and will be rejected by a - Haskell implementation. Our example 10 + "hello" - is ill typed because the (+) operator is - only defined to work with numbers. + What makes type systems interesting is that they're not all + equal. In fact, different type systems are often not even + concerned with the same kinds of problems. A programming + language's type system deeply colours the way we think, and + write code, in that language. hunk ./en/ch03-funcs-types.xml 48 - When we say that Haskell has a - strong type system, we mean that every - expression has exactly one most general - type. This is a notion we can most easily explain - by example. The expression x - 1 doesn't - represent a specific type of number: it will work equally well - if x is an Integer or a - Double (a floating point number). As a result, the - most general type for which the expression is valid is any - numeric type. The expression 1 < 2 is a - comparison that can only evaluate to True or - False, so its most general type is - Bool. + Haskell's type system allows us to think at a very abstract + level: this is what permits us to write concise, powerful + programs. However, we don't give up the ability to get + close to the metal when low-level performance is + important. + hunk ./en/ch03-funcs-types.xml 55 - Strong typing means that any attempt to apply a - function to a value of a different type causes an error. Another - aspect of Haskell's view of strong typing is that it will not - automatically coerce values from one type to another. (Coercion - is also known as casting or conversion.) For example, a C - compiler will automatically and silently coerce a value of type - int into a float on our behalf if a - function expects a parameter of type float, but a - Haskell compiler will raise a compilation error in a similar - situation. We must explicitly coerce types ourselves. + + Haskell's type system hunk ./en/ch03-funcs-types.xml 58 - - Weaker and stronger types + There are three interesting aspects to types in + Haskell: they are strong, they are + static, and they can be automatically + inferred. Let's talk in more detail about + each of these ideas. When possible, we'll present similarities + between concepts from Haskell's type system and related ideas in + other languages. We'll also touch on the respective strengths + and weaknesses of each of these properties. hunk ./en/ch03-funcs-types.xml 67 - It's useful to be aware that many language - communities have their own definitions of a strong - type. We won't get into the details here, but it's - still useful to speak broadly about the notion of strength in - type systems. + + Strong types hunk ./en/ch03-funcs-types.xml 70 - A hypothetical weak type system will accept as valid an - expression like 10 + "foo". A stronger type - system will be less permissive in how it allows expressions of - different types to be mixed. Under some strong type systems, - this expression is not valid. + When we say that Haskell has a + strong type system, we mean that the type + system guarantees that a program cannot contain certain kinds + of errors. These errors come from trying to write expressions + that don't make sense, such as using an integer as a function. + Similarly, if a function expects to work with integers, and we + pass it a string, a Haskell compiler will reject this. hunk ./en/ch03-funcs-types.xml 78 - Our example expression is valid in C, but not in Haskell, - so we say that Haskell has a stronger type system. - + We call an expression that obeys a language's type + rules well typed. An expression that + disobeys the type rules is ill typed, and + will cause a type error. + + Another aspect of Haskell's view of strong + typing is that it will not automatically coerce values from + one type to another. (Coercion is also known as casting or + conversion.) For example, a C compiler will automatically and + silently coerce a value of type int into a + float on our behalf if a function expects a + parameter of type float, but a Haskell compiler + will raise a compilation error in a similar situation. We + must explicitly coerce types by calling functions . + + Strong typing does occasionally make it more difficult to + write certain kinds of code. For example, a classic way to + write low-level code in the C language is to be given a byte + array, and cast it to treat the bytes as if they're really a + complicated data structure. This is very efficient, since it + doesn't require us to copy the bytes around. Haskell's type + system does not allow this sort of coercion. In order to get + the same structured view of the data, we would need to do some + copying, which would cost a little in performance. + + The huge benefit of strong typing is that it catches real + bugs in our code before they can cause problems. For example, + in a strongly typed language, we can't accidentally use a + string where an integer is expected. + + + Weaker and stronger types + + It is useful to be aware that many language + communities have their own definitions of a strong + type. Nevertheless, we will speak briefly and in + broad terms about the notion of strength in type + systems. + + In academic computer science, the meanings of + strong and weak have a + narrowly technical meaning: strength refers to how + permissive a type system is. A weaker type + system treats more expressions as valid than a stronger type + system. + + For example, in Perl, the expression "foo" + + 2 evaluates to the number 2, but the expression + "13foo" + 2 evaluates to the number 15. Haskell + rejects both expressions as invalid, because the + (+) operator requires both of its + operands to be numeric. Because Perl's type system is more + permissive than Haskell's, we say that it is weaker under + this narrow technical interpretation. + + The fireworks around type systems have their roots in + ordinary English, where people attach notions of + value to the words weak + and strong: we usually think of strength as + better than weakness. Many more programmers speak plain + English than academic jargon, and quite often academics + really are throwing brickbats at + whatever type system doesn't suit their fancy. The result is + often that popular Internet pastime, a flame war. + + + + + Static types hunk ./en/ch03-funcs-types.xml 148 - Having a static type system - means that the compiler knows the type of every value and - expression at compile time, before any code is executed. A - Haskell compiler or interpreter will detect when we try to use - expressions whose types don't match, and reject our code with an - error message. + Having a static type system + means that the compiler knows the type of every value and + expression at compile time, before any code is executed. A + Haskell compiler or interpreter will detect when we try to use + expressions whose types don't match, and reject our code with + an error message before we run it. hunk ./en/ch03-funcs-types.xml 155 - &ch03.basics.ghci:error; + &ch03.basics.ghci:error; hunk ./en/ch03-funcs-types.xml 157 - This error message is of a kind we've seen before. - The compiler has inferred that the most general type of the - expression "false" is [Char], - which is a synonym for String. The - (&&) operator requires each of its - operands to be of type Bool, and its left operand - indeed has this type. Since the actual type of - "false" does not match the required type, the - compiler rejects this expression as ill typed. + This error message is of a kind we've seen + before. The compiler has inferred that the most general type + of the expression "false" is + [Char], which is a synonym for + String. The (&&) + operator requires each of its operands to be of type + Bool, and its left operand indeed has this type. + Since the actual type of "false" does not + match the required type, the compiler rejects this expression + as ill typed. + + Static typing does make writing some useful programs + difficult, or sometimes impossible. In languages like Python, + duck typing is common, where an object acts + enough like another to be used as a substitute for it. While + Haskell has some support for programming with dynamic types, + it's not nearly as easy or expressive as in a language that + wholeheartedly embraces the notion. hunk ./en/ch03-funcs-types.xml 176 - Haskell's combination of strong and static typing - makes it impossible for type errors to occur at runtime. While - this means that we need to do a little more thinking up - front, it also eliminates many simple errors that can - otherwise be devilishly hard to find. It's a truism within the - Haskell community that once code compiles, it's more likely to - work correctly than in other languages. (Perhaps a more - realistic way of putting this is that Haskell code often has - fewer trivial bugs.) + Haskell's combination of strong and static + typing makes it impossible for type errors to occur at + runtime. While this means that we need to do a little more + thinking up front, it also eliminates many + simple errors that can otherwise be devilishly hard to find. + It's a truism within the Haskell community that once code + compiles, it's more likely to work correctly than in other + languages. (Perhaps a more realistic way of putting this is + that Haskell code often has fewer trivial bugs.) hunk ./en/ch03-funcs-types.xml 186 - Finally, a Haskell compiler can automatically - deduce the types of (almost) all values in a program. This - process is known as type inference. Haskell allows us to - explicitly declare the type of any value, but the presence of - type inference means that this is almost always optional, not - something we must do. + Programs written in dynamically typed languages require + large suites of tests to give some assurance that simple type + errors cannot occur. Test suites cannot offer complete + coverage: some common tasks, such as refactoring a program to + make it more modular, can introduce new type errors that a + test suite may not expose. hunk ./en/ch03-funcs-types.xml 193 - While strong, static typing makes Haskell safe, type - inference makes it concise. The result is potent: we end up - with a language that's both safer than popular statically typed - languages, and often more expressive than dynamically typed - languages. + In Haskell, the compiler proves the absence of type errors + for us: a Haskell program that compiles will not suffer from + type errors when it runs. Refactoring is usually a matter of + moving code around, then recompiling and tidying up a few + times until the compiler gives us the all + clear. + + + + Type inference + + Finally, a Haskell compiler can automatically + deduce the types of almost + Occasionally, we need to give the compiler a little + information to help it to make a choice in understanding + our code. XXX Insert link to wherever we talk + about this. + all expressions in a program. This process is + known as type inference. Haskell allows + us to explicitly declare the type of any value, but the + presence of type inference means that this is almost always + optional, not something we must do. + + + + + What to expect from the type system + + Our exploration of the major capabilities and benefits of + Haskell's type system will span a number of chapters. Early on, + you may find Haskell's types to be a chore to deal with. + + For example, instead of simply writing some code and running + it to see if it works as you might in Python or Ruby, you'll + first need to make sure that your program passes the scrutiny of + the type checker. Why stick with the learning curve? hunk ./en/ch03-funcs-types.xml 230 - We've already briefly seen Haskell's notation for types in - . We write expression - :: MyType to say that - expression has the type MyType. + While strong, static typing makes Haskell safe, + type inference makes it concise. The result is potent: we end + up with a language that's both safer than popular statically + typed languages, and often more expressive than dynamically + typed languages. + + Fixing type errors may initially feel like more work than if + you were using a dynamic language. It might help to look at + this as moving much of your debugging up + front. The compiler shows you many of the logical + flaws in your code, instead of leaving you to stumble across + problems at runtime. + + Furthermore, because Haskell can infer the types of your + expressions and functions, you gain the benefits of static + typing without the added burden of + finger typing imposed by less powerful statically + typed languages. In other languages, the type system serves the + needs of the compiler. In Haskell, it serves + you. The tradeoff is that you have to + learn to work within the framework it provides. + + We will introduce new uses of Haskell's types throughout + this book, to help us to write and test practical code. As a + result, the complete picture of why the type system is + worthwhile will emerge gradually. While each step should + justify itself, the whole will end up greater than the sum of + its parts. hunk ./en/ch03-funcs-types.xml 265 - are some more of the most common base types, i.e. types that - aren't composed of other types. + are several more of the most common base types. hunk ./en/ch03-funcs-types.xml 269 - A Char value represents a - character. The values of Char are drawn from - the Unicode character set, which covers most of the world's - written languages. + A Char value represents a Unicode + character. hunk ./en/ch03-funcs-types.xml 284 - 64-bit machine, it is usually 64 bits wide. (There are also - numeric types that are exactly 8, 16, and so on bits wide; + 64-bit machine, it is usually 64 bits wide. The Haskell + standard only guarantees that an Int is wider + than 28 bits. (There exist numeric types that are exactly + 8, 16, and so on bits wide, in signed and unsigned flavours; hunk ./en/ch03-funcs-types.xml 292 - integer of arbitrary size. Integers are not + integer of unbounded size. Integers are not hunk ./en/ch03-funcs-types.xml 308 + + We have already briefly seen Haskell's notation + for types in . When we write a + type explicitly, we use the notation expression :: + MyType to say that expression + has the type MyType. If we omit the + :: and the type that follows, a Haskell compiler + will infer the type of the expression. + + &ch03.basics.ghci:types; + + + + + Function application + + Now that we've had our fill of data types for a while, let's + turn our attention to working with some of + the types we've seen, using functions. + + To apply a function in Haskell, we write the name + of the function followed by its arguments. + + &func.ghci:odd; + + We don't use parentheses or commas to group or + separate the arguments to a function; merely writing the name of + the function, followed by each argument in turn, is enough. As + an example, let's apply the compare + function, which takes two arguments. + + &func.ghci:compare; + + If you're used to function call syntax in other + languages, this notation can take a little getting used to, but + it's simple and uniform. + + Function application has higher precedence than using + operators, so the following two expressions have the same + meaning. + + &func.ghci:precedence; + + The parentheses don't do any harm, but they add some visual + noise. Sometimes parentheses are necessary, for example to + indicate how we want a complicated expression to be + parsed. + + &func.ghci:precedence2; + + This applies compare to the results of + applying odd 3 and odd 6, + respectively. hunk ./en/ch03-funcs-types.xml 374 - written [Char]. (The type String is - simply an alias for [Char]. We can use the two - interchangeably.) + written [Char]. + + The head function returns the first + element of a list. + + &func.ghci:head; + + Its counterpart, tail, returns all + but the head of a list. + + &func.ghci:tail; hunk ./en/ch03-funcs-types.xml 386 - We call the list type - polymorphic because it can contain any type - of value (see for - more details). We can write the type list of a for any type a by enclosing the type in square - brackets: [a]. The lowercase a is called a type - variable; it's a placeholder for some other type - whose details do not matter. + As you can see, we can apply + head and tail to lists + of different types. Applying head to a + [Char] value returns a Char value, + while applying it to a [Bool] value + returns a Bool value. The + head function doesn't care what type of + list it deals with. + + Because a list can contain any type of value, we call it + polymorphic + We'll talk more about polymorphism in . + . When we want to write a polymorphic type, we use a + type variable, which must begin with a + lowercase letter. A type variable is a placeholder, where + eventually we'll substitute a real type. + + If we have a type variable a, + we can write the type list of a by enclosing the type + variable in square brackets: [a]. This amounts to + saying I don't care what type I have; I can make a list + with it. + + + Distinguishing type names and type variables hunk ./en/ch03-funcs-types.xml 414 - So, for example, the type [Int] is a list of - values of type Int (and only - of type Int!), the type - [MyPersonalType] is a list of values of type - MyPersonalType, and [[Int]] is a list - of values of type [Int], i.e. a list of lists of + We can now see why a type name must start with an + uppercase letter: this makes it distinct from a type variable, + which must start with a lowercase letter. + + + When we want to talk about a list containing + values of a specific type, we substitute that type for our type + variable. So, for example, the type [Int] is a + list of values of type Int, because we substituted + Int for a. + Similarly, the type [MyPersonalType] is a list of + values of type MyPersonalType. We can perform this + substitution recursively, too: [[Int]] is a list of + values of type [Int], i.e. a list of lists of hunk ./en/ch03-funcs-types.xml 430 - Lists are the bread and butter of - Haskell collections. However, their utility goes beyond merely - gathering values together. + &usingtypes.ghci:listlist; + + + Lists are special + + Lists are the bread and butter of + Haskell collections. In an imperative language, we might + perform a task many items by iterating through a loop. This + is something that we often do in Haskell by traversing a list, + either by recursing or using a function that recurses for us. + Lists are the easiest stepping stone into the idea that we can + use data to structure our program and its control flow. We'll + be spending a lot more time discussing lists in + . + hunk ./en/ch03-funcs-types.xml 446 - In an imperative language, we might repeat a task over many - items by iterating through a loop. This is something that we - often do in Haskell with operations over a list, either by - recursing or using a function that recurses for us. Lists are - the easiest stepping stone into the idea that we can use data to - structure our program and its control flow. We'll be spending a - lot more time discussing lists in . + A tuple is a fixed-size collection of values, + where each value can have a different type. This distinguishes + them from a list, whose elements must all have the same + type. hunk ./en/ch03-funcs-types.xml 451 - A tuple is a fixed-size collection of values, each - of which can be of any type. Whereas the elements of a list - must all have the same type, there's no need for the elements of - a tuple to have related types. In fact, tuples are important - precisely because they're often the most convenient way to group - several values of different types. + To help to understand the difference, let's say we want to + track two pieces of information about a book. It has a year of + publication, which is a number, and a title, which is a string. + We can't keep both of these pieces of information in a list, + because they have different types. Instead, we use a + tuple. hunk ./en/ch03-funcs-types.xml 458 - A handy rule of thumb for remembering which is - which is that a list has varying size and uniform type, while a - tuple has a uniform size and varying type. + &tuple.ghci:book; hunk ./en/ch03-funcs-types.xml 471 - Haskell doesn't have a notion of a one-element tuple. Tuples - are often referred to using the number of elements as a prefix, - hence 3-tuple for a tuple of three elements, - 5-tuple for five, and so on. - - In practice, working with tuples that contain more - than a handful of elements quickly leads to unwieldy code, so - tuples of more than half a dozen elements are not often - seen. + Haskell doesn't have a notion of a one-element + tuple. Tuples are often referred to using the number of elements + as a prefix. A 2-tuple has two elements, and is usually called a + pair. A 3-tuple (sometimes called a triple) has + three elements; a 5-tuple has five; and so on. In practice, + working with tuples that contain more than a handful of elements + makes code unwieldy, so tuples of more than half a dozen + elements are rarely used. hunk ./en/ch03-funcs-types.xml 482 - numbers or types of elements have distinct types of their - own. + numbers or types of elements have distinct types. hunk ./en/ch03-funcs-types.xml 486 - In this example, the expression (False, 'a') - has the type (Bool, Char). If we swapped the two - elements, we'd get a tuple of type (Char, Bool). - Even though the number of elements and their types are the same, - these two types are distinct because the positions of the - element types are different. + In this example, the expression (False, + 'a') has the type (Bool, Char), which is + distinct from the type of ('a', False). Even though + the number of elements and their types are the same, these two + types are distinct because the positions of the element types + are different. hunk ./en/ch03-funcs-types.xml 495 - This type, (Bool, Char, Char), is distinct from - (Bool, Char) because it contains an added - element. - - - Jargon watch - - 2-tuples are often referred to as - pairs, and 3-tuples are sometimes (though - less frequently) called triples. - + This type, (Bool, Char, Char), is + distinct from (Bool, Char) because it contains + three elements, not two. hunk ./en/ch03-funcs-types.xml 531 - - Function application - - Now that we've had our fill of data types for a while, let's - turn our attention to working with some of - the types we've seen. We've already seen how to perform - arithmetic in , and it - looks quite similar to arithmetic in other languages. + + Functions over lists and tuples hunk ./en/ch03-funcs-types.xml 535 - we can construct them, but not how we do anything with them - afterwards. Haskell defines a large library of functions, the - Prelude, for working with lists and (to a lesser extent) tuples, - so let's find out how to use a few of those functions. - - To apply a function in Haskell, we write the name - of the function followed by its arguments. - - - Jargon watch - - When talking about functions, we'll use the terms - application and - calling interchangeably. We'll do the - same with parameter and - argument. - - - We don't use parentheses or commas to group or separate the - arguments to a function; merely writing the name of the - function, followed by each argument in turn, is enough. As an - example, let's apply the head function, - which returns the first element of a list. - - &func.ghci:head; - - Its counterpart, tail, returns all - but the head of a list. - - &func.ghci:tail; + we can construct them, but little about how we do anything with + them afterwards. We have only been introduced to + head and tail. hunk ./en/ch03-funcs-types.xml 551 - If you're used to function call syntax in other - languages, this notation can take a little getting used to, but - it's simple and uniform. - - Here's something to watch out for, until you gain a little - more familiarity with the language. Under Haskell's convention - for function application, the following expression is an - application of snd to a single argument, - which is a pair. - - &func.ghci:snd; - - In a popular imperative language, this would - probably instead mean call snd with - two arguments, 1 and - 2, which is quite different. + Here's something to watch out for, until you gain + a little more familiarity with the language. For tuples, the + fst and snd functions + return the first and second element of a pair, + respectively. hunk ./en/ch03-funcs-types.xml 557 - By the way, snd has a - companion function, fst, which returns the - first element of a pair. + &tuple.ghci:fst; hunk ./en/ch03-funcs-types.xml 559 - &func.ghci:fst; + If your background is in any of a number of other languages, + each of these may look like an application of a function to two + arguments. Under Haskell's convention for function application, + each one is an application of a single pair. hunk ./en/ch03-funcs-types.xml 567 - If your background is in Python, you'll probably be used - to lists and tuples being almost interchangeable. This isn't - the case in Haskell, so don't try to carry that idea with - you into unfamiliar linguistic territory. + If your background is in Python, you'll probably + be used to lists and tuples being almost interchangeable. + Although the elements of a Python tuple are immutable, it can + be indexed and iterated over using the same methods as a list. + This isn't the case in Haskell, so don't try to carry that + idea with you into unfamiliar linguistic territory. hunk ./en/ch03-funcs-types.xml 574 - As an illustration, take a look at the type signatures of - fst and snd: they're - defined only for pairs, and can't be used - with tuples of other sizes. Haskell's type system makes it - tricky to write a generalised get the second element - from any tuple, no matter how wide function. + As an illustration, take a look at the type + signatures of fst and + snd: they're defined + only for pairs, and can't be used with + tuples of other sizes. Haskell's type system makes it tricky + to write a generalised get the second element from any + tuple, no matter how wide function. hunk ./en/ch03-funcs-types.xml 586 - Haskell parses an expression from left to right. - If we want to use the one expression as an argument to - another, we have to keep this in mind and use parentheses to - tell the parser what we really mean. Here's an + In Haskell, function application is left + associative. This mouthful is easier to understand visually, + by adding parentheses: the expression a b c d is + equivalent to (((a b) c) d). If we want to use one + expression as an argument to another, we have to use explicit + parentheses to tell the parser what we really mean. Here's an hunk ./en/ch03-funcs-types.xml 599 - the parentheses, Haskell would instead interpret the - expression as pass drop as the - argument to head. The - offending expression would actually be parsed as (((head - drop) 4) "azerty"), where we've introduced lots of - extra parentheses to eliminate any ambiguity. Compilation - would fail with a type error, as head - requires its argument to be a list, and - drop is a function. + the parentheses, the offending expression would be similar to + passing three arguments to head. + Compilation would fail with a type error, as + head requires a single argument, a + list. hunk ./en/ch03-funcs-types.xml 660 - statement, as a function is a single expression, not a sequence - of statements. + keyword, as a function is a single expression, not a sequence of + statements. The value of the expression is the result of the + function. (Haskell does have a function called + return, but we won't discuss it for a + while.) hunk ./en/ch03-funcs-types.xml 667 - it represents equivalence: the name on the left - is equivalent to the expression on the right. It does - not represent assignment of - a value to a memory location. This distinction is very - important. + it represents meaning: the name on the left + is equivalent to the expression on the right. hunk ./en/ch03-funcs-types.xml 673 - In Haskell, a variable is a way of giving a name - to an expression. Once a variable is bound - to (i.e. associated with) a particular - expression, its value does not change: we can always use the - name of the variable instead of writing out the expression, - and get the same result either way. + In Haskell, a variable provides a way to + give a name to an expression. Once a variable is + bound to (i.e. associated with) a + particular expression, its value does not change: we can + always use the name of the variable instead of writing out the + expression, and get the same result either way. hunk ./en/ch03-funcs-types.xml 684 - In an imperative language can change a variable's value at any - time, so that examining the memory location repeatedly can + In an imperative language we can change a variable's value at + any time, so that examining the memory location repeatedly can hunk ./en/ch03-funcs-types.xml 695 + For example, if we run the following tiny Python script, + it will print the number 11. + + &assign.py:assign; + + In contrast, trying the equivalent in Haskell results in + an error. + + &Assign.hs:assign; + + We cannot assign a value to x + twice. + + &assign.ghci:load; + hunk ./en/ch03-funcs-types.xml 724 - introduce a new variable, also named + introduce a completely new variable, which happens to be named hunk ./en/ch03-funcs-types.xml 733 - until . We'll see another - case of shadowing later in this chapter. + until . Our function + f still has access to the shadowed + definition of x. + + &shadowing.ghci:f; + + We'll see another case of shadowing later in this + chapter. hunk ./en/ch03-funcs-types.xml 747 - Like other languages, Haskell has an + Like many other languages, Haskell has an hunk ./en/ch03-funcs-types.xml 750 - own version of the standard take + own version of the standard drop hunk ./en/ch03-funcs-types.xml 767 + In Haskell, indentation is important: it + continues an existing definition, instead + of starting a new one. Don't omit the indentation! + hunk ./en/ch03-funcs-types.xml 817 - While it can make sense in an imperative - language to omit the else branch from an - if, this would be nonsensical in Haskell. An - if expression that was missing an - else wouldn't have a type if the predicate - evaluated to False, so it would be ill - typed. + + + Recall that Haskell is an expression-oriented + language. In an imperative language, it can make sense to + omit the else branch from an if, + because we're working with statements, + not expressions. However, when we're working with + expressions, an if that was missing an + else wouldn't have a result or type if the + predicate evaluated to False, so it would + be nonsensical. hunk ./en/ch03-funcs-types.xml 829 - The predicate contains a few more novelties. The + Our predicate contains a few more novelties. The hunk ./examples/ch03/Assign.hs 1 +{-- snippet assign --} +x = 10 +x = 11 +{-- /snippet assign --} hunk ./examples/ch03/assign.ghci 1 +--# load + +:load Assign hunk ./examples/ch03/assign.py 1 +## snippet assign + +x = 10 +x = 11 +# value of x is now 11 +print x + +## /snippet assign hunk ./examples/ch03/ch03.basics.ghci 5 +--# types + +:type 'a' +'a' :: Char +[1,2,3] :: Int + hunk ./examples/ch03/func.ghci 4 +head ['a','b','c'] hunk ./examples/ch03/func.ghci 10 -tail [3,4] +tail [True,False] +tail "list" +tail [] hunk ./examples/ch03/func.ghci 19 ---# snd - -snd (1,2) - ---# fst - -fst ("you", True) - hunk ./examples/ch03/func.ghci 52 +--# odd + +odd 3 +odd 6 + +--# compare + +compare 2 3 +compare 3 3 +compare 3 2 + +--# precedence + +(compare 2 3) == LT +compare 2 3 == LT + +--# precedence2 + +compare (odd 3) (odd 6) + hunk ./examples/ch03/shadowing.ghci 3 +let f n = x + n hunk ./examples/ch03/shadowing.ghci 8 +--# f +f 2 + hunk ./examples/ch03/usingtypes.ghci 1 +--# listlist + +:type [[True],[False]] + +--# badlist + hunk ./examples/ch04/tuple.ghci 1 +--# book +(1964, "Labyrinths") + hunk ./examples/ch04/tuple.ghci 10 +:type ('a', False) hunk ./examples/ch04/tuple.ghci 22 +--# fst +fst (1,'a') +snd (1,'a') + }