Skip to content

Add list concept #1273

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 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
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
6 changes: 6 additions & 0 deletions concepts/lists/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"blurb": "Lists are a basic data type in Haskell for holding a collection of values. A list can hold values of different types. Lists are immutable, meaning they cannot be modified. Lists in Haskell are implemented as linked lists. Therefore, accessing an element in a list takes linear time depending on the length of the list.",
"authors": [
"meatball133"
]
}
87 changes: 87 additions & 0 deletions concepts/lists/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# About

[Lists][list] are a basic data type in Haskell for holding a collection of values.
Lists are _immutable_, meaning they cannot be modified.
Any operation that changes a list returns a new list.
There are several methods in the prelude which allows you to work with Lists.

Lists in Haskell are implemented as [linked lists][linked-list-wiki], and not as arrays of contiguous memory location.
Therefore, accessing an element in a list takes linear time depending on the length of the list.

Lists can be written in literal form, head-tail notation, (which uses the `cons` operator `:`), or a combination of both:

```haskell
-- Literal Form
[]
[1]
[1, 2, 3]

-- Head-tail Notation
[]
-- same as [1]
1 : []
-- same as [1, 2, 3]
1 : (2 : (3 : []))

-- Mixed
-- same as [1, 2, 3]
1 : [2, 3]
```

Head-tail notation can be used to append items to a list.

```haskell
list = [2, 1]

[3, 2, 1] == 3 : list
-- -> True
```

Appending elements to a list during iteration is considered an anti-pattern.
Appending an element requires walking through the entire list and adding the element at the end, therefore, appending a new element in each iteration would require walking through the entire list in each iteration.

We can achieve the same result by prepending an element to the reversed list, and then reversing the result. Prepending is a fast operation and requires constant time.

```haskell
-- Appending to the end of a list (potentially slow)
[1, 2, 3] ++ [4] ++ [5] ++ [6]

-- Prepend to the start of a list (faster, due to the nature of linked lists)
6 : (5 : (4 : [3, 2, 1]))
-- then reverse!
```

There are several common Prelude functions for lists:

- [`head`][head] returns the _head_ of a list -- the _first_ item in a list.
- [`tail`][tail] returns the _tail_ of the list -- the list _minus_ the _first_ item.
- [`length`][length] returns the number items in the list.
- [`elem`][elem] returns a boolean value indicating whether the item is an element in the list.

There is also the [`Data.List` module][list].

## List types

In Haskell, lists are **homogenous**.
This means that all elements of a list have the same type.
When you try to put elements of different types into the same list, you will get a type error.

```haskell
list = [1, "string"]
-- Error: No instance for (Num String) arising from the literal ‘1’
```

The type annotation of a list is `[a]` where a is the type which the lists holds, for example `String` or `Int`.

``` haskell
a :: [Int]
a = [1, 2, 3]
```

[prelude]: https://hackage.haskell.org/package/base/docs/Prelude.html
[list]: https://hackage.haskell.org/package/base/docs/Data-List.html
[head]: https://hackage.haskell.org/package/base/docs/Prelude.html#v:head
[tail]: https://hackage.haskell.org/package/base/docs/Prelude.html#v:tail
[elem]: https://hackage.haskell.org/package/base/docs/Prelude.html#v:elem
[length]: https://hackage.haskell.org/package/base/docs/Prelude.html#v:length
[linked-list-wiki]: https://en.wikipedia.org/wiki/Linked_list
87 changes: 87 additions & 0 deletions concepts/lists/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# About

[Lists][list] are a basic data type in Haskell for holding a collection of values.
Lists are _immutable_, meaning they cannot be modified.
Any operation that changes a list returns a new list.
There are several methods in the prelude which allows you to work with Lists.

Lists in Haskell are implemented as [linked lists][linked-list-wiki], and not as arrays of contiguous memory location.
Therefore, accessing an element in a list takes linear time depending on the length of the list.

Lists can be written in literal form, head-tail notation, (which uses the `cons` operator `:`), or a combination of both:

```haskell
-- Literal Form
[]
[1]
[1, 2, 3]

-- Head-tail Notation
[]
-- same as [1]
1 : []
-- same as [1, 2, 3]
1 : (2 : (3 : []))

-- Mixed
-- same as [1, 2, 3]
1 : [2, 3]
```

Head-tail notation can be used to append items to a list.

```haskell
list = [2, 1]

[3, 2, 1] == 3 : list
-- -> True
```

Appending elements to a list during iteration is considered an anti-pattern.
Appending an element requires walking through the entire list and adding the element at the end, therefore, appending a new element in each iteration would require walking through the entire list in each iteration.

We can achieve the same result by prepending an element to the reversed list, and then reversing the result. Prepending is a fast operation and requires constant time.

```haskell
-- Appending to the end of a list (potentially slow)
[1, 2, 3] ++ [4] ++ [5] ++ [6]

-- Prepend to the start of a list (faster, due to the nature of linked lists)
6 : (5 : (4 : [3, 2, 1]))
-- then reverse!
```

There are several common Prelude functions for lists:

- [`head`][head] returns the _head_ of a list -- the _first_ item in a list.
- [`tail`][tail] returns the _tail_ of the list -- the list _minus_ the _first_ item.
- [`length`][length] returns the number items in the list.
- [`elem`][elem] returns a boolean value indicating whether the item is an element in the list.

There is also the [`Data.List` module][list].

## List types

In Haskell, lists are **homogenous**.
This means that all elements of a list have the same type.
When you try to put elements of different types into the same list, you will get a type error.

```haskell
list = [1, "string"]
-- Error: No instance for (Num String) arising from the literal ‘1’
```

The type annotation of a list is `[a]` where a is the type which the lists holds, for example `String` or `Int`.

``` haskell
a :: [Int]
a = [1, 2, 3]
```

[prelude]: https://hackage.haskell.org/package/base/docs/Prelude.html
[list]: https://hackage.haskell.org/package/base/docs/Data-List.html
[head]: https://hackage.haskell.org/package/base/docs/Prelude.html#v:head
[tail]: https://hackage.haskell.org/package/base/docs/Prelude.html#v:tail
[elem]: https://hackage.haskell.org/package/base/docs/Prelude.html#v:elem
[length]: https://hackage.haskell.org/package/base/docs/Prelude.html#v:length
[linked-list-wiki]: https://en.wikipedia.org/wiki/Linked_list
10 changes: 10 additions & 0 deletions concepts/lists/links.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[
{
"url": "https://hackage.haskell.org/package/base/docs/Data-List.html",
"description": "Haskell Prelude: Data.List"
},
{
"url": "https://www.haskelltutorials.com/guides/haskell-lists-ultimate-guide.html",
"description": "Haskell Lists: The Ultimate Guide"
}
]
17 changes: 17 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,18 @@
"basics"
]
},
{
"slug": "language-list",
"name": "Language List",
"uuid": "2770e2c0-883d-4336-be94-3b4ae7529ac3",
"prerequisites": [
"basics"
],
"status": "beta",
"concepts": [
"lists"
]
},
{
"slug": "temperature",
"name": "Temperature",
Expand Down Expand Up @@ -1399,6 +1411,11 @@
"slug": "booleans",
"name": "Booleans"
},
{
"uuid": "ef484ab5-d4d2-4d7c-b4b5-abb5920f916a",
"slug": "lists",
"name": "Lists"
},
{
"uuid": "ca21a553-6fc1-49be-8f47-730f97330862",
"slug": "pattern-matching-literals",
Expand Down
36 changes: 36 additions & 0 deletions exercises/concept/language-list/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Hints

## General

- Use the built-in [(linked) list type][list].

## 1. Define a function to return an empty language list

- The function needs to return `[]`.

## 2. Define a function to add a language to the list

- An element can be prepended to a list using `:`.

## 3. Define a function to remove a language from the list

- Haskell [provides a function][tail] to return a list with the first item removed.

## 4. Define a function to return the first item in the list

- HaskellHaskell [provides a function][head] to get the first item from a list.

## 5. Define a function to return how many languages are in the list

- Haskell [provides a function][length] to count the length of a list.

## 6. Define a function to determine if the list includes a functional language

- Your function should return a boolean value indicating whether `"Haskell"` is a member of the list.
Haskell [provides a function][elem] to test list membership.

[list]: https://hackage.haskell.org/package/base/docs/Data-List.html
[head]: https://hackage.haskell.org/package/base/docs/Prelude.html#v:head
[tail]: https://hackage.haskell.org/package/base/docs/Prelude.html#v:tail
[elem]: https://hackage.haskell.org/package/base/docs/Prelude.html#v:elem
[length]: https://hackage.haskell.org/package/base/docs/Prelude.html#v:length
66 changes: 66 additions & 0 deletions exercises/concept/language-list/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Instructions

In this exercise you need to implement some functions to manipulate a list of programming languages.

## 1. Define a function to return an empty language list

Define the `new` function that takes no arguments and returns an empty list.

```haskell
new
-- -> []
```

## 2. Define a function to add a language to the list

Define the `add/2` function that takes 2 arguments (a _language list_ and a string literal of a _language_).
It should return the resulting list with the new language prepended to the given list.

```haskell
add new "Clojure"
-- -> ["Clojure"]
add ["Clojure"] "Haskell"
-- -> ["Haskell", "Clojure"]
```

## 3. Define a function to remove a language from the list

Define the `remove` function that takes 1 argument (a _language list_).
It should return the list without the first item. Assume the list will always have at least one item.

```haskell
remove ["Haskell", "Clojure", "Erlang"]
-- -> ["Clojure", "Erlang"]
```

## 4. Define a function to return the first item in the list

Define the `first` function that takes 1 argument (a _language list_).
It should return the first language in the list.
Assume the list will always have at least one item.

```haskell
first ["Elixir", "Haskell", "Clojure", "Prolog"]
-- -> "Elixir"
```

## 5. Define a function to return how many languages are in the list

Define the `count` function that takes 1 argument (a _language list_).
It should return the number of languages in the list.

```haskell
count ["Prolog", "Elm"]
-- -> 2
```

## 6. Define a function to determine if the list includes a functional language

Define the `isFunctionalList` function which takes 1 argument (a _language list_).
It should return a boolean value.
It should return `True` if _"Haskell"_ is one of the languages in the list.

```haskell
isFunctionalList ["Haskell"]
-- -> True
```
Loading
Loading