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

Make is alias imply is ro. #79

Open
schwern opened this issue Apr 27, 2013 · 6 comments
Open

Make is alias imply is ro. #79

schwern opened this issue Apr 27, 2013 · 6 comments
Labels

Comments

@schwern
Copy link
Contributor

schwern commented Apr 27, 2013

Read-only aliases are a great way to save memory and if you accidentally try to modify the variable Perl will spit at you.

Read-write aliases have far more limited use cases, either you can't be arsed to pass in a reference or you're doing something magical. If you accidentally use a read-write alias Perl won't tell you.

Read-write is the default for aliased variables. Whoops.

is alias should imply is ro, same way is copy implies is rw. If the user wants a modifiable alias they should have to explicitly declare it so, or consider using a copy or pass by reference.

This would break backwards compatibility, but I wonder how much code it would actually break, and how many accidental modifications of aliases it would reveal.

@thoughtstream
Copy link
Contributor

@schwern wrote:

is alias should imply is ro, same way is copy implies is rw.

I strenuously disagree. In fact, I think that first implication is
completely the wrong way round (more on that later...)

If the user wants a modifiable alias they should have to explicitly
declare it so, or consider using a copy or pass by reference.

I even more strenuously disagree.

"Alias" implies "all the original characteristics". So adding an implicit
"is ro" to "is alias" is utterly counterintuitive. By all means document
this issue, if it's a common mistake, but please don't mess up the
meaning of "alias" (which is utterly orthogonal to constness) in order
to "solve" the problem.

We spent a lot of time on this when we were designing Perl 6.
We concluded:

* Parameters should default to "is ro is alias" (for performance and safety)
* "is rw" then implies "is alias" (without having to write "is alias")
* "is ro" is just an explicit way of getting the default
* "is copy" gives you "isnt ro  isnt alias"
* There is no explicit "is alias" trait, since it isn't needed

So, if you're going to break backwards compatibility, why not break it in favour
of forwards compatibility instead? ;-)

Except, as you're considering exiling aliases in any case, you probably can't
do that. :-(

So maybe just reverse your original implication. That is:

"is ro" should imply "is alias"

Which produces:

* Parameters default to "is copy" (for safety)
* "is rw" then implies "is alias" (without having to write "is alias")
* "is ro" also implies "is alias" (again, without having to write

"is alias")
* Get rid of "is alias"

Damian

@schwern
Copy link
Contributor Author

schwern commented Apr 28, 2013

On 4/27/13 10:48 PM, thoughtstream wrote:

So maybe just reverse your original implication. That is:

"is ro" should imply "is alias"

I'd totally agree if aliasing in Perl 5 were reliable. I don't think we
can apply aliasing implicitly without making "is ro" buggy as well. :-/

Which produces:

  • Parameters default to "is copy" (for safety)
  • "is rw" then implies "is alias" (without having to write "is alias")

In a pass by reference language this would make sense, since every
function can potentially modify the caller's input. Though in a pass by
reference language aliasing probably isn't necessary.

In a pass by copy language like Perl 5, the assumption is if you pass in
a non-reference that data will not be altered.

foo($string);    # $string will not be altered

Read-write aliases break this assumption and leave the caller unable to
know if the function is going to modify their variable.

foo($string);    # Will $string be modified?  Who knows?!

While you can already do that with @_, I don't want to encourage that
behavior. It makes it difficult to know by skimming a lexical block how
data might be altered.

If a function wants to alter its caller's data I'd rather it said so.

foo(\$string);
  • "is ro" also implies "is alias" (again, without having to write
    "is alias")
  • Get rid of "is alias"

@thoughtstream
Copy link
Contributor

"is ro" should imply "is alias"

I'd totally agree if aliasing in Perl 5 were reliable. I don't think we
can apply aliasing implicitly without making "is ro" buggy as well. :-/

Fair enough.

In a pass by copy language like Perl 5, the assumption is if you pass in
a non-reference that data will not be altered.

foo($string); # $string will not be altered

Um. Is that a reasonable assumption. Given that $[0], $[1], etc. in
every subroutine are rw aliases?

And given that quite a few of Perl's own builtins (open, close, chomp,
tie, bless, undef, local, sysread, seek, delete, map, grep, pop, push,
splice, etc. etc.) already violate that assumption?

(Note: not saying it's good; just saying it's so).

Read-write aliases break this assumption and leave the caller unable to
know if the function is going to modify their variable.

Far be it from me to argue with my own prescriptions from PBP. ;-)

While you can already do that with @_, I don't want to encourage that
behavior. It makes it difficult to know by skimming a lexical block
how data might be altered.

True. In which case we should remove "is alias" entirely.

But, if we're not going to remove it, we shouldn't mess up its
semantics by making it default to "is ro" as well.

Is the real problem that you're trying to solve here that
there's currently no way of specifying a read-only alias parameter
(because "is alias" trumps "is ro" when both are specified)?

If so, perhaps we need to fix that instead?
Or maybe we need a new trait: "is ro_alias"?

Damian

@schwern
Copy link
Contributor Author

schwern commented Apr 29, 2013

On 4/28/13 9:25 PM, thoughtstream wrote:

In a pass by copy language like Perl 5, the assumption is if you pass in
a non-reference that data will not be altered.

foo($string); # $string will not be altered

Um. Is that a reasonable assumption. Given that $[0], $[1], etc. in
every subroutine are rw aliases?

Yes. It's mostly unknown that you can modify @_ and it looks gross.
It's also very easy to scan for with PPI.

In my experiences cleaning up and refactoring all sorts of code,
modifying @_ is something I almost never see. It's a thing you do more
when you're writing clever CPAN modules than large swaths of business logic.

And given that quite a few of Perl's own builtins (open, close, chomp,
tie, bless, undef, local, sysread, seek, delete, map, grep, pop, push,
splice, etc. etc.) already violate that assumption?

They're built ins. They're special in all sorts of ways. They break
all sorts of rules, are inconsistent with each other and often violate
good programming practices. I think people understand that.

Is the real problem that you're trying to solve here that
there's currently no way of specifying a read-only alias parameter
(because "is alias" trumps "is ro" when both are specified)?

Do they? Oh crap, I never even tried it. That's a bug.

No, the real problem for me was "is alias" is dangerous and easier to
type than the safer and more commonly useful "is alias is ro".

Not sure what I think about "is ro_alias" in that regard.

@thoughtstream
Copy link
Contributor

No, the real problem for me was "is alias" is dangerous and easier to
type than the safer and more commonly useful "is alias is ro".
Not sure what I think about "is ro_alias" in that regard.

Yeah, it's less than ideal. My objection to "is alias" changing
semantics is that the new semantics don't mirror what the term "alias"
means, which would make the behaviour surprising/annoying to the
competent.

To me the point is that a parameter with "is ro is alias" is identical
in behaviour to "is ro". The "is alias" bit is just being used as an
optimization.

Whereas, when I use "is alias", I do so because I want that specific
aliasing behaviour (i.e. semantics again), not merely because I want
better performance.

So that tells me we need another kind of "is ro" to indicate
optimization, but without mentioning aliases (because they might go
away, or not be applicable if the user hasn't installed Data::Alias, or
we might later find a better way of optimizing read-only params).

What we have now is:

$param                  -->  read-write copy (default)
$param is copy          -->  read-write copy (i.e. redundant)
$param is copy is rw    -->  read-write copy (i.e. redundant)
$param is rw            -->  read-write copy (i.e. redundant)
$param is copy is ro    -->  read-only copy
$param is ro            -->  read-only copy
$param is alias         -->  read-write alias
$param is alias is rw   -->  read-write alias (i.e. redundant)
$param is alias is ro   -->  read-write alias (i.e. broken)

That indicates a failure of interface to me: of the nine possibilities,
nearly half are redundant, and one more is so subtly broken that no-one
ever noticed until now. :-(

In contrast, the Perl 6 interface is:

$param                  -->  read-only alias (default)
$param is ro            -->  read-only alias (i.e. redundant)
$param is rw            -->  read-write alias
$param is copy          -->  read-write copy
$param is copy is ro    -->  read-only copy
$param is copy is rw    -->  compile-time error (can't be both

copy and alias)

with one fewer keyword, and only one redundant case.

But we can't do that. So here's another bold suggestion...

Given your disinclination to promote the use of aliases and your desire
to protect the inexperienced (both laudable goals), we could eliminate
one trait entirely (namely: "is copy") and still clean up the overall interface,
by rearranging the meanings like so:

$param                  -->  read-only copy (default)
$param is rw            -->  read-write copy
$param is ro            -->  read-only alias (or copy where

aliases not supported)
$param is alias --> read-write alias
$param is alias is ro --> read-only alias (i.e. redundant)
$param is alias is rw --> compile-time error (can't be both
copy and alias)

This is mostly consistent with current use usages[*], has only one
redundant case, has no bugs, makes all trait names semantic, means users
don't have to explicitly request (or even think about) optimizations, and also
ensures that the length of each trait name is directly proportional to
how dangerous it is!

Submitted for your consideration.

Damian

[*] Except, obviously that the default behaviour is no longer
read-write, which is safer!

@barefootcoder
Copy link
Contributor

I strenuously agree with @thoughtstream. :-D

A read-only alias is pretty useless. An read-only alias of a scalar is less efficient than a copy, and aliasing of non-scalars doesn't work (and you could, and probably should, use the \ syntax for that anyway).

So, what, exactly, is the use case for a read-only alias?

The only time I ever use is alias is when I want to modify the value. If I didn't want that, I'd just stick with the copy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants