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

Function argument alias #129

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
120 changes: 120 additions & 0 deletions text/0000-function-argument-alias.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
- Feature Name: Function argument alias
- Start Date: 2018-09-24
- RFC PR:
- Pony Issue:

# Summary

Pony's shadowing prohibition means that using an object field or
method name as a function argument is not allowed. This is a
proposal to add an optional clause to function arguments that gives
the argument a local alias.

# Motivation

Function arguments are part of the public API of a class. They appear
in the documentation, in error messages and are used explicitly when
calling a function using named arguments.

In a variety of situations, a function argument will naturally
correspond to a class field or method, for example when a constructor
provides a way to directly pass an initial state, or when a method
replaces a field with a new value. For ergonomics and ease of use,
it's ideal to be able to use the most simple and obvious argument name
here. That is, if a method updates the field `_name` then the most
obvious argument for that method is `name`.

However, if we want to allow public access to that field then we're
out of luck due to the shadowing prohibition. The only solution in
this case is to use a different name for the function argument such as
`new_name`, `_name` or `name'`, none of which are ideal.

This problem applies for any function, whether it's a constructor or a
method. It's been brought up occassionally on the mailing list with
Joe McIlvain suggesting a change such that an argument `name'` could
be filled with the named argument `name` (provided that no parameter
is using the "unprimed" name). This solution does not seem to solve
the problem of primed arguments appearing in error messages and
documentation. And it could be argued that it makes the language
harder to understand.

# Detailed design

The design has been borrowed from OCaml and is perhaps the most
straight-forward solution:
```pony
class Thing
let name: String
let cost: I64

new create(name: String = "" as name', cost: I64 = 0 as cost') =>
name = name'
cost = cost'
```
That is, after a function argument, we allow an optional "as" clause such that the argument is available locally under a different name.

Of course, the shadowing prohibition applies for the new name and the
name must not be used by other arguments.

# How We Teach This

The "Shadowing" section in the tutorial needs to be updated:

> If you need a variable with nearly the same name, you can use a prime '.

This would be changed to:

> If you need a variable with nearly the same name, you can use a
> prime ' or use a local alias for the variable using the `as`
> keyword.

An example should follow or a link to the "Classes" section.

The Wombat example in the "Classes" should be updated to reflect this
language change:
```pony
class Wombat
let name: String
var _hunger_level: U64

new create(name: String as name') =>
name = name'
_hunger_level = 0

new hungry(name: String as name', hunger_level: U64) =>
name = name'
_hunger_level = hunger_level
```

# How We Test This

The new syntax is fully backwards compatible so we only need to test
the new functionality.

# Drawbacks

This proposal uses the `as` keyword which is already use to convert a
value to another type.

# Alternatives

Two alternatives that have been brought up previously, the first being
the suggestion to allow the use of a non-primed named argument as
already presented, the other being using a special marker for
constructor arguments that should always be set directly on the
created object.

This latter approach is used by for example TypeScript where you can
put either `public` or `private` in front of an argument and have it
automatically defined and set on the class.

However, this does not solve the more general problem which applies to
all functions and in which we don't always simply want to assign the
argument as the value for the corresponding field. An example might be
a date object where we both want to be able to pass arguments for day,
month and year as part of the construction and be able to use the same
names for methods such as `day()`, `month()` and `year()`.

# Unresolved questions

None.