Skip to content

More language guide edits #10

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

Merged
merged 2 commits into from
Jul 27, 2022
Merged
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
42 changes: 24 additions & 18 deletions pages/language-tour.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,6 @@ public fun greet(_ name: String) {

The statement `import Greetings` at the top of `Hello.val` tells the compiler it should import the module `Greetings` when it compiles that source file.
Implicitly, that makes `Greetings` a dependency of `Hello`.
{% comment %}
The point of this change is that some people “hear” words as they read them, and if you have two
distinct names with the same sound, especially whose spelling differs only by capitalization, it
gets confusing.
{% endcomment %}

Notice that `greet` had to be made public so it could be visible to other modules.
As such, it can be called from `Hello.val`.
Expand Down Expand Up @@ -242,9 +237,6 @@ public fun main() {
```

The type of an expression can be retrieved, without evaluating the expression, using `type(of:)`:
{% comment %}
I understand, precedent from Swift, but… what purpose does `of:` serve here?!
{% endcomment %}

```val
public fun main() {
Expand Down Expand Up @@ -414,7 +406,7 @@ public fun main() {
}
```

*Note: indexing a buffer outside of is either caught as a compile-time error, or causes the program to terminate at runtime.*
*Note: indexing a buffer outside of its bounds is either caught as a compile-time error, or causes the program to terminate at runtime.*

The type of a buffer is written either `T[n]` or `Buffer<T, n>`, where `T` is a type and `n` the number of elements in the buffer.
All elements of a buffer must be initialized at the same time as the buffer itself, either by the means of a buffer literal expression, as in the program above, or by calling a buffer *initializer*:
Expand Down Expand Up @@ -467,6 +459,13 @@ type Matrix3 {
}
```

{% comment %}
I don't know if we discussed this, but making the memberwise init public has API resilience
implications. In particular, it means you can't simply add/remove stored properties, even if they
were non-public, without breaking client code. I'd like to design some facilities for helping a
library author to ensure API stability eventually.
{% endcomment %}
Copy link
Contributor

@kyouko-taiga kyouko-taiga Jul 25, 2022

Choose a reason for hiding this comment

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

I thought about that.

The idea is that if you want to change the layout of the type later on, you'll have to write a custom initializer to keep your promise. That's what you have to do in Swift today anyway. The memberwise init feature is just a shortcut for a large class of situations where your type does not cross a resilience boundary but also doesn't want to expose all its parts publicly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes! But the problem in Swift today is that AFAIK there's nothing in place to discourage you from changing the layout of your type once you've made the promise. Actually in the case of API it's not necessarily about layout, just about which members are stored.


The type declaration above defines a type `Matrix3` with a single property of type `Double[3][3]`.
The second declaration exposes the default memberwise initializer of the type, allowing us to create matrices by calling `Matrix2.init(components:)`:

Expand All @@ -490,7 +489,7 @@ public fun main() {
```

In the program above, `m.components` can be modified because `m` is a mutable binding **and** the property `components` is mutable, as it is declared with `var`.
Would that property be declared with `let`, the components of the matrix would remain immutable once the matrix has finished initializing, notwistaning the mutability of the binding to which it is assigned.
Had that property been declared with `let`, the components of the matrix would remain immutable once the matrix has finished initializing, even though `m` is mutable.

Members that are not declared `public` cannot be accessed outside of the scope of a record type.
As we uncover more advanced constructs, we will show how to exploit that feature to design clean and safe APIs.
Expand Down Expand Up @@ -520,8 +519,8 @@ public fun main() {
### Unions

Two or more types can form a union type, also known as a [sum type](https://en.wikipedia.org/wiki/Tagged_union).
A sum type is a super type of all its elements.
Among other things, it means that if a type `T` is the union of the types `U` and `V`, then a binding of type `T` can be assigned to a value of type `U` *or* a value of type `V`.
A sum type is a supertype of all its elements.
Among other things, that means that if a type `T` is the union of the types `U` and `V`, then a binding of type `T` can be assigned a value of type `U` *or* a value of type `V`.

```val
public fun main() {
Expand Down Expand Up @@ -551,13 +550,17 @@ The type `Option<T>` is the union of any type `T` and `Nil`, which can be used t
## Functions and methods

Functions are blocks of organized and reusable code that performs a single action, or group of related actions.
As a program grows in complexity, they become mandatory.
They are an essential tool for managing the complexity of a program as it grows.

*Note: Though Val should not be considered a functional programming language, it does support this style of programming relatively well, as functions are first-class citizen.*
*Note: Though Val should not be considered a functional programming language, functions are
first-class citizens, and functional programming style is well-supported. In fact, Val's mutable
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice!

value semantics can be freely mixed with pure-functional code without eroding that code's local
reasoning properties*

### Free functions

Functions are declared with `fun`, followed by its name, its signature, and finally its body:
A function declaration is introduced with the `fun` keyword, followed by the function's name, its
signature, and finally its body:

```val
typealias Vector2 = (x: Double, y: Double)
Expand All @@ -573,8 +576,11 @@ public fun main() {
```

The program above declares a function named `norm` that accepts a 2-dimensional vector (represented as a pair of `Double`) and returns its norm.
The signature of a function fully specifies its API.
Most importantly, it describes the function's parameters and their type as well as a return type.
{% comment %}
The API of a function arguably includes the its semantics and preconditions.
{% endcomment %}

A function's signature describes its parameter and a return types.

*Note: The return type of a function that does not return any value may be omitted.*
*In that case, the declaration is interpreted as though the return type was `Unit`.*
Expand All @@ -597,7 +603,7 @@ fun scale(_ v: Vector2, by factor: Vector2) -> Vector2 {
```

Argument labels are also useful to distinguish between different variants of the same operation.
Further, note that Val does not suppoer type-based overloading, meaning that the only way for two functions to share the same name is to have different argument labels.
Further, note that Val does not support type-based overloading, meaning that the only way for two functions to share the same base name is to have different argument labels.

```val
typealias Vector2 = (x: Double, y: Double)
Expand Down