Skip to content
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

feat: Document arrays and loops #341

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 3 additions & 1 deletion docs_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ docs_groups:
The Guide:
- guide/hello_world
- guide/basics
- guide/mutation
- guide/functions
- guide/lists
- guide/mutation
- guide/arrays
- guide/loops
- guide/data_types
- guide/pattern_matching
- guide/imports_exports
Expand Down
103 changes: 103 additions & 0 deletions src/guide/arrays.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
---
title: Arrays
---

An alternative to lists, arrays are fixed-length containers for ordered data. Individual elements can be swapped out for other elements, and individual elements can be retrieved quickly.

## Creating and Using Arrays

Like lists, arrays always contain elements of the same type, and attempting to mix element types will result in a compilation error.

```grain
let empty = [>]
let numbers = [> 1, 2, 3]
let strings = [> "foo", "bar", "baz"]
```

Unlike lists, we can easily access elements at any (zero-based) index:

```grain
let strings = [> "foo", "bar", "baz"]

print(strings[0]) // "foo"
print(strings[1]) // "bar"
print(strings[2]) // "baz"
```

We can also use negative indexes to access elements from the end of the array:

```grain
let strings = [> "foo", "bar", "baz"]

print(strings[-1]) // "baz"
print(strings[-2]) // "bar"
print(strings[-3]) // "foo"
```

If we try to access an element beyond the length of the array, we'll get an `IndexOutOfBounds` error.

## Updaing Arrays
ospencer marked this conversation as resolved.
Show resolved Hide resolved

One of the major benefits arrays have is the ability to change the values it contains. We can update an array's values like so:
ospencer marked this conversation as resolved.
Show resolved Hide resolved

```grain
let strings = [> "foo", "bar", "baz"]

print(strings) // [> "foo", "bar", "baz"]

strings[1] = "qux"

print(strings) // [> "foo", "qux", "baz"]
```

In some cases, this could allow us to write programs that are more efficient than if we used a list.

However, the size of an array is fixed. To add additonal items to an array, we must append them together, which would create a brand new, third array:

```grain
import Array from "array"

let one = [> 1]
let twoThree = [> 2, 3]
let oneTwoThree = Array.append(one, twoThree)

print(oneTwoThree) // [> 1, 2, 3]
```

Since `oneTwoThree` is a brand new array, updating values in `one` or `twoThree` doesn't affect the values in `oneTwoThree`.

For long arrays, even when adding just one element, this could be a fairly expensive operation. For programs that need to do this kind of operation, lists may be a better choice. We discuss this in more detail in the section below.

To learn more about what's available in the Array standard library, check out the [array standard library documentation](https://grain-lang.org/docs/stdlib/array).

## Lists vs. Arrays

Lists and arrays are similar constructs, but each has its own pros and cons. It's up to you to choose which is right for your use case!

Lists are excellent because they're
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Lists are excellent because they're
Lists are excellent at

Copy link
Member

@spotandjake spotandjake Dec 24, 2022

Choose a reason for hiding this comment

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

I thinkthere was a better transition here because, it goes on to list the properties rather than use cases. at indicates listing specific examples. It might make sense to say something like

Lists are useful over arrays because they are immutable, 
and their data structure makes resizing them more efficient,
they are easier to work with in grain and less susceptible to bugs 
because it is a lot harder to get runtime errors. The downfalls of lists compared 
to arrays are that they are unable to modified in place, and their data structure 
makes accessing specific elements and determining the length less efficient. 
Lists are best used when the size does not matter and you are constantly 
appending or removing elements.

The downfall of that sentence structure is you lose the nice list though a better option over a list might be having the two paragraphs with their differences and then using a table to compare the features.


- immutable
- efficient at adding additional elements
- efficient at removing elements
- easy to work with
- less succeptible to bugs
Copy link
Member

Choose a reason for hiding this comment

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

In what way?

Copy link
Member Author

Choose a reason for hiding this comment

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

No possible IndexOutOfBounds, typically pattern matching on them which forces you to handle empty cases, etc. I was fine letting the guide have this as "take our word for it" but we could expand on that if we wanted. Wouldn't do it inline though.

Copy link
Member

Choose a reason for hiding this comment

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

Hm, expanding might be valuable because "take our word for it" seems like a bad position for our "guide" 😛


Lists might not be the right choice because they're

- unable to be modified
ospencer marked this conversation as resolved.
Show resolved Hide resolved
- inefficient at accessing random elements
- inefficient at determining the number of elements

Arrays are excellent because they're
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Arrays are excellent because they're
Arrays are excellent at

Copy link
Member

Choose a reason for hiding this comment

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

I guess the list would have to be reworded if these are both changed to "at" but I think it's best to say what they are "excellent at"


- able to be modified after creation
- efficient at accessing random elements
- efficient at determining the number of elements

Arrays might not be the right choice because they're

- inefficient at adding additonal elements
- inefficient at removing elements
- more succeptible to bugs
Copy link
Member

Choose a reason for hiding this comment

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

How?

Choose a reason for hiding this comment

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

Suggested change
- more succeptible to bugs
- more susceptible to bugs


As a rule of thumb, lists are the idiomatic choice in Grain! Lean towards using lists whenever possible and use arrays when it makes your code easier to understand.
Copy link
Member

Choose a reason for hiding this comment

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

You might want to state that Lists are more idiomatic because they are generally easier to work with and more feature rich supporting destructuring in pattern matches and the fact that they are less likely to throw runtime errors. you might also want to note that as grain is functional lists are the better choice because you they are immutable.

Choose a reason for hiding this comment

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

Suggested change
As a rule of thumb, lists are the idiomatic choice in Grain! Lean towards using lists whenever possible and use arrays when it makes your code easier to understand.
As a rule of thumb, if you have to keep track of the amount of elements or want to index to retrieve an element often, arrays are the best choice. However, you should use lists in (tail) recursive applications! Lean towards using idiomatic lists whenever possible and use arrays if you need length or random access.

This rule of thumb more accurately paints the picture of determining the most efficient data structure for the use case.

69 changes: 69 additions & 0 deletions src/guide/loops.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
---
title: Loops
---

Loops allow you to repeat an action over and over (and over).

## While Loops
phated marked this conversation as resolved.
Show resolved Hide resolved

The most basic loop is the `while` loop. The code within the loop repeats until the condition is `false`.

```grain
let mut i = 0

while (i < 10) {
print("looping!")
i += 1
}
```

Loops can skip to the next iteration using the `continue` keyword. For example, to print only the odd numbers from zero to ten:

```grain
let mut i = 0

while (i < 10) {
if (i % 2 == 0) continue
print(i)
i += 1
}
```

Use the `break` keyword to exit from a loop entirely:

```grain
let mut i = 0

while (true) {
if (i < 10) {
print("forever?")
i += 1
} else {
break
}
}
```

## For Loops

`for` loops are just like `while` loops, just with more structure. `for` loops accept an optional initializer statement which runs before the loop, an optional condition statement which runs before each iteration, and an optional increment statement which runs after each iteration.

`for` loops are commonly used to iterate arrays:

```grain
import Array from "array"

let strings = [> "foo", "bar", "baz"]

for (let mut i = 0; i < Array.length(strings); i += 1) {
print(strings[i])
}
```

As all of the loop parameters are optional, they can all be omitted (though the semicolons are still required). Omitting the condition results in an infinite loop:

```grain
for (;;) {
print("forever!")
}
```
phated marked this conversation as resolved.
Show resolved Hide resolved