Skip to content

Updates to Basics / Values and Types, up through the text section #11

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
215 changes: 175 additions & 40 deletions docs/docs/basics/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,89 +2,224 @@
comments: true
---

## The type of Booleans
Haskell's type system is such an important feature, and so useful for understanding the language, that it is a good place to begin. This section will explore different Haskell values and their types.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"This section will explore different Haskell values and their types." - on reflection, I could probably leave out this sentence, since it's evidence from the table of contents.


`True` is a value in Haskell. Its *type* is `Bool`. In Haskell, we can state this as:
## The type of Boolean values

Here are three words that are central to understanding Haskell.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I sympathise with the intent here, and now realize I was using "value" not very carefully - thanks for addressing that! My only objection is that I want to avoid didactic prose as much as possible. My suggestion would be to put lines 11-13 in a "!!! Note" box after the repl example.

That fits the intended style better, which is: example + comments + parenthetic commentary


* An *expression* is a piece of Haskell source code that describes a value.
* A *value* is the meaning of an expression.
* A *type* is a category of values or expressions that can be used in the same ways.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Newcomers may be primed to misinterpret the word "category" here if they've heard of the Haskell link to category theory.


For example, `True` is a Haskell *value*. Its *type* is `Bool`. There are several other Haskell *expressions* (like `True && True` or `False || True`) that have the same value. In Haskell, we can state this as:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My suggestion would be:

"For example, True is a Haskell value. Its type is Bool. Other Haskell expressions may have the same value:"

Then in code comments, we could state, in case still unclear, that (True && True) is an expression with the same value as True.


=== "In a repl"

```haskell
True :: Bool -- (1)!
True && True :: Bool -- (2)!
False || True :: Bool
```

1. Read "X :: Y" as: "the value X has the type Y"
1. Read "X :: Y" as: "X has the type Y"
2. `::` has a lower precedence than any other operator, so this is read as: "`True && True` has the type `Bool`"

=== "In a file"

```haskell
example :: Bool
example = True
example1 :: Bool -- (1)!
example1 = True

example2 :: Bool
example2 = True && True

example3 :: Bool
example3 = False || True
```

Similarly,
1. A source file contains *declarations* instead expressions, so we define new variables. It's also possible write `example1 = True :: Bool`, but it's unusual. More commonly, Haskell source files contain the type of a definition on a separate line from the value as shown here.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just:

"A source file contains declarations, like example1 = True. It's also possible write example1 = True :: Bool, but more commonly, Haskell source files contain the type of a definition on a separate line from the value as shown here."


```haskell
False :: Bool
```
Similarly, these are expressions for the value `False`, which also has the type `Bool`.

=== "In a repl"

```haskell
False :: Bool
False && True :: Bool
False || False :: Bool
```

=== "In a file"

```haskell
example4 :: Bool
example4 = False

example5 :: Bool
example5 = False && True

example6 :: Bool
example6 = False || False
```

These expressions makes an explicit statement about the type, and it's written that way above to demonstrate the relationship between values and types, and show the meaning of `::`, which can be read as "has the type". In general, though, you are not required to include the type every time you use a value in Haskell. Because it's optional but can be attached to a value for clarity, `:: Bool` is called a *type* *annotation*.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels like slightly too much prose for my liking. My preference would be to put it in a box, and/or condense it to one sentence like: ":: Bool is called a type annotation. [You are not required to include the type every time you use a value in Haskell](link to type inference section of guide)


In the REPL, you can also ask for the type of an expression using `:t`, like this:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a slight preference for this to go in the type inference section (under the "Thinking in Haskell" section). Then again, I see the value in it being earlier. Perhaps we could have an example of :t in one of the repl examples, with a comment that links to the type inference section.

(I realize I'm being a bit picky here, so feel free to ignore this one)


=== "In a repl"

```haskell
> :t True
True :: Bool

> :t False
False :: Bool

> :t True && False
True && False :: Bool
```

!!! Note
In Haskell, everything from simple values like `True` to complex programs have a unique type.
In Haskell, every value, from a simple value like `True` to a complex program, has a type.

!!! Tip
Haskell types can be quite complex. To understand a type, always ask: what do the values belonging to this type look like?
'Bool' is a simple type, but Haskell types can become quite complex, too. To understand a type, always start by asking: what do the values belonging to this type look like?

For example, the values belonging to `Bool` are `True` and `False`.

## The type of integers
## Types of integers

`Int` is a type for integers, as in:
`Int` is one type for integers. An `Int` is a signed integer with a fixed number of bits, but the exact number of bits depends on the Haskell implementation and architecture.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this additional info be in a footnote?


```haskell
5 :: Int
```
=== "In a repl"

```haskell
5 :: Int
```

=== "In a file"

```haskell
x :: Int
x = 5
```

Sometimes you might want to work with integers whose values can be arbitrarily large. For this, there's another type: `Integer`. The same expression, `5`, can be used to describe an `Integer`, as well!
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be nice in an optional "??? Tip" section.


=== "In a repl"

```haskell
5 :: Integer
5 * 1000000000000000000 :: Integer
```

=== "In a file"

```haskell
y1 :: Integer
y1 = 5

y2 :: Integer
y2 = 5 * 1000000000000000000
```

An expression like `5` that can more than one type is said to be *overloaded*. It can describe different values depending on the type it's used as.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the subsequent Gotcha section, and the part you added, I think this is redundant.


??? Gotcha
`5` can have a more general type in Haskell. See [here](/faq/numbers.md)
If you ask for the type of `5` in the REPL, you'll see neither the type `Int` nor `Integer`, but rather `Num a => a`. This will make more sense once you understand type variables and type classes, but it just means the expression `5` can represent a value of any number type. See [here](/faq/numbers.md)

## The type of real numbers
## Types of real numbers

There are several available options. A good general choice is `Double`:
When it comes to numbers with a fractional part, Haskell again provides several possible types.

```haskell
5.0 :: Double
```
A good general choice is `Double`, which represents an IEEE double-precision floating point number:

=== "In a repl"

## The type of text
```haskell
5 :: Double -- (1)!
3.14 :: Double
```

`Char` is the type of single characters:
1. The same expression, `5`, can also be a `Double`. To be more explicit, you can also write `5.0`.

```hs title="repl example"
> :t 'a'
'a' :: Char
=== "In a file"

> :t 'b'
'b' :: Char
```
```haskell
z1 :: Double
z1 = 5

`Text` is the type of sequences of characters:
z2 :: Double
z2 = 3.14
```

```haskell
{-# LANGUAGE OverloadedStrings #-} --(1)!
import Data.Text (Text)
Floating point numbers are fixed precision, so doing computations with them can result in rounding error. An exact option for numbers with a fractional part is `Rational`, which can represent any rational number (that is, any fraction) exactly.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Footnote or optional text box.


=== "In a repl"

```haskell
5 :: Rational
3.14 :: Rational
```

=== "In a file"

```haskell
z3 :: Rational
z3 = 5

exampleText :: Text
exampleText = "hello world!"
```
z4 :: Rational
z4 = 3.14
```

??? Gotcha
If you ask for the type of `3.14` in the REPL, you'll see neither `Double` nor `Rational`, but rather `Fractional a => a`. This will make more sense once you understand type variables and type classes, but it just means the expression `3.14` can represent a value of any fractional number type. See [here](/faq/numbers.md)

## Text types

`Char` is the type of single characters. These are written in single quotes.

=== "In a repl"

```haskell
'A' :: Char
'b' :: Char
```

=== "In a file"

1. See [here](/gotchas/strings) for why this extension is needed.
```haskell
c1 :: Char
c1 = 'A'

c2 :: Char
c2 = 'b'
```

`Text` is the best type for most sequences of characters. However, there's a bit of boilerplate needed to use it.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a slight preference for not saying there's boilerplate, but just having code comments for the import line and the extension line, in the service of a "show, don't tell" style. But I won't die on this hill.


=== "In a repl"

```haskell
:set -XOverloadedStrings
import Data.Text (Text)

"hello world!" :: Text
```

=== "In a file"

<!-- Haskell's type system is such an important feature, and so useful for understanding the language, that it is a good place to begin.
```haskell
{-# LANGUAGE OverloadedStrings #-}
import Data.Text (Text)

Every expression (that includes all programs) in the language has a unique type. -->
exampleText :: Text
exampleText = "hello world!"
```

??? Gotcha
See [here](/gotchas/strings) for why the `OverloadedStrings` language extension is needed.

## The type of functions

Expand Down