Raku / old-issue-tracker

Tickets from RT
https://github.com/Raku/old-issue-tracker/issues
2 stars 1 forks source link

Set.new confused by Nil #6143

Open p6rt opened 7 years ago

p6rt commented 7 years ago

Migrated from rt.perl.org#130970 (status was 'open')

Searchable as RT130970$

p6rt commented 7 years ago

From zefram@fysh.org

Set.new(Nil).perl set(Any) set(Nil).perl set(Any) set(Nil).WHICH Set|Any|U16962232

Attempting to put Nil into a Set instead puts Any into one. Happens with both Set.new() and set() constructors. The .WHICH result shows that it really is the set construction that mangled the value, not .perl. There's no reason for Nil not to be a distinguishable value in a set.

-zefram

p6rt commented 7 years ago

From @geekosaur

Erm. Isn't Nil a silent Failure? Insisting that it be propagated and retained in all circumstances basically asserts that it must be a distinct concrete value, and specifically *not* any form of Failure. Could someone clarify this?

(At present my understanding is that it is a silent Failure and most if not all of today's Nil tickets are at best missing the point.)

On Thu, Mar 9, 2017 at 3​:03 PM, Zefram \perl6\-bugs\-followup@​perl\.org wrote​:

# New Ticket Created by Zefram # Please include the string​: [perl #​130970] # in the subject line of all future correspondence about this issue. # \<URL​: https://rt-archive.perl.org/perl6/Ticket/Display.html?id=130970 >

Set.new(Nil).perl set(Any) set(Nil).perl set(Any) set(Nil).WHICH Set|Any|U16962232

Attempting to put Nil into a Set instead puts Any into one. Happens with both Set.new() and set() constructors. The .WHICH result shows that it really is the set construction that mangled the value, not .perl. There's no reason for Nil not to be a distinguishable value in a set.

-zefram

-- brandon s allbery kf8nh sine nomine associates allbery.b@​gmail.com ballbery@​sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

p6rt commented 7 years ago

The RT System itself - Status changed from 'new' to 'open'

p6rt commented 7 years ago

From zefram@fysh.org

Brandon Allbery via RT wrote​:

Erm. Isn't Nil a silent Failure?

It may well represent such a thing, but it is also a reified object. Putting objects into sets is an operation that's applicable to any kind of object, and which (for comparison) does in fact work on objects of the Failure class. Expecting an object to be propagated doesn't assert that it doesn't represent failure; rather, it's just embracing the reification. The great benefit of reification is that the object can be processed in all the familiar ways that are used on ordinary value objects.

If you want to impose a restriction that failure objects can't go into some of the places I've tried to put Nil, then in some cases that would be a sensible decision. It would make perfect sense to restrict Range endpoints, for example, but it doesn't make any sense to restrict the domain of Sets. But in any case, wherever such a restriction is desirable it should be enforced by signalling an exception​: silently substituting a different value sucks. And if your logic for rejecting Nil is that it represents failure, then presumably the same logic would lead you to reject objects of the Failure class too, and maybe also Exception.

-zefram

p6rt commented 7 years ago

From @skids

On Thu, 09 Mar 2017 12​:55​:58 -0800, zefram@​fysh.org wrote​:

Brandon Allbery via RT wrote​:

Erm. Isn't Nil a silent Failure?

It may well represent such a thing, but it is also a reified object. Putting objects into sets is an operation that's applicable to any kind of object, and which (for comparison) does in fact work on objects of the Failure class. Expecting an object to be propagated doesn't assert that it doesn't represent failure; rather, it's just embracing the reification. The great benefit of reification is that the object can be processed in all the familiar ways that are used on ordinary value objects.

If you want to impose a restriction that failure objects can't go into some of the places I've tried to put Nil, then in some cases that would be a sensible decision. It would make perfect sense to restrict Range endpoints, for example, but it doesn't make any sense to restrict the domain of Sets. But in any case, wherever such a restriction is desirable it should be enforced by signalling an exception​: silently substituting a different value sucks. And if your logic for rejecting Nil is that it represents failure, then presumably the same logic would lead you to reject objects of the Failure class too, and maybe also Exception.

-zefram

The reason this is happening is not that Nil is a pseudo-Failure. It is that Nil is also semantically special as a RHS of an assignment or as a parameter.

$ perl6 -e 'my @​a; @​a = Nil,Nil; @​a.perl.say' [Any, Any] $ perl6 -e 'my Int @​a; @​a = Nil,Nil; @​a.perl.say' Array[Int].new(Int, Int) $ perl6 -e 'my @​a = Array.new(Nil,Nil); @​a.perl.say' [Any, Any] $ perl6 -e 'my @​a = Array[Int].new(Nil,Nil); @​a.perl.say' [Int, Int]

This is mentioned in S02.

Set, as of right now, cannot be parameterized so you only get to see the Any case there. The usual "solution" to this in the Array case is to directly bind the element. This is actually discouraged as it is in violation of the GLR "gentleman's agreement" that Array elements are Scalars. Set being immutable, getting a Nil into a set element would require some internals incantation.

Nil is not supposed to be used as a generic kickaround Mu instantiation -- it is just not a normal value.

Or philosphically, Per S02, Nil 'means "there is no value here"', so having a set that contains it as an element is incongruous.

We only let it out into Lists/Seqs because it would be impossible to use otherwise.

If there is a compelling use-case for allowing Nil in a Set, it should probably require using set() rather than Set.new() as we can call that a literal... you'll note List.new wont transcribe Nil either.

p6rt commented 7 years ago

From zefram@fysh.org

Brian S. Julin via RT wrote​:

It is that Nil is also semantically special as a RHS of an assignment or as a parameter. ... This is mentioned in S02.

Some of the specialness mentioned in S02 doesn't happen, such as .ACCEPTS returning Nil, and getting the default value of an optional parameter. Those issues probably don't change the logic of this ticket, though.

Set, as of right now, cannot be parameterized so you only get to see the Any case there.

False analogy. The intentional behaviour of the Array is that (by default) it contains a bunch of Scalar containers, and it is those containers that (by default) have the funny treatment of Nil. Set, otoh, doesn't overtly involve any Scalar containers, and in fact immutability is an advertised feature. So there's no API reason to import Scalar's behaviour around Nil.

Or philosphically, Per S02, Nil 'means "there is no value here"',

And yet Nil is a reified object, and so very much *is* a value that is here. It may indicate the absence of a value in some higher-level protocol in which Nil is not a relevant value. But down here in the base language Nil is a visible object.

so having a set that contains it as an element is incongruous.

Again, that may be so in some higher-level situation in which Nil is not a value of interest. If you're expecting a set of Ints then getting a set containing Nil would be incongruous. But where Nil is a value, it would be incongruous to be unable to put it in a set. It's literally an axiom of set theory that for every object there exists a set containing that object. (Note that you need a set theory that includes urelements, to match the Perl 6 situation.)

If there is a compelling use-case for allowing Nil in a Set,

I find the intrinsic concept of the set to be pretty compelling.

probably require using set() rather than Set.new() as we can call that a literal.

I don't follow this argument. There doesn't seem to be any rule against .new() methods accepting a Nil argument as Nil​:

Pair.new("foo", Nil).value.WHICH Nil|U15269208

Trying things out now, I see that the behaviour of the test cases with which I started this ticket has changed, and that currently set() and Set.new() do actually behave differently. (In the original bug report I didn't distinguish between set() and Set.new(), not seeing any difference.) set() now accepts a Nil element, and Set.new() accepts Nil as a sole argument; the substitution of Any now happens only for Set.new() and only when there's at least one other argument​:

set(Nil) set(Nil) set(3,Nil) set(3 Nil) set(Nil,Nil) set(Nil) Set.new(Nil) set(Nil) Set.new(3,Nil) set((Any) 3) Set.new(Nil,Nil) set((Any))

That's pretty weird. The subject line of this ticket still seems applicable.

                  you'll note List\.new wont transcribe

Nil either.

That looks like a separate bug.

-zefram

p6rt commented 7 years ago

From @skids

On Tue, 05 Sep 2017 03​:21​:07 -0700, zefram@​fysh.org wrote​:

Brian S. Julin via RT wrote​:

It is that Nil is also semantically special as a RHS of an assignment or as a parameter. ... This is mentioned in S02.

Some of the specialness mentioned in S02 doesn't happen, such as .ACCEPTS returning Nil, and getting the default value of an optional parameter. Those issues probably don't change the logic of this ticket, though.

Set, as of right now, cannot be parameterized so you only get to see the Any case there.

False analogy. The intentional behaviour of the Array is that (by default) it contains a bunch of Scalar containers, and it is those containers that (by default) have the funny treatment of Nil. Set, otoh, doesn't overtly involve any Scalar containers, and in fact immutability is an advertised feature. So there's no API reason to import Scalar's behaviour around Nil.

Or philosphically, Per S02, Nil 'means "there is no value here"',

And yet Nil is a reified object, and so very much *is* a value that is here. It may indicate the absence of a value in some higher-level protocol in which Nil is not a relevant value. But down here in the base language Nil is a visible object.

so having a set that contains it as an element is incongruous.

Again, that may be so in some higher-level situation in which Nil is not a value of interest. If you're expecting a set of Ints then getting a set containing Nil would be incongruous. But where Nil is a value, it would be incongruous to be unable to put it in a set. It's literally an axiom of set theory that for every object there exists a set containing that object. (Note that you need a set theory that includes urelements, to match the Perl 6 situation.)

If there is a compelling use-case for allowing Nil in a Set,

I find the intrinsic concept of the set to be pretty compelling.

probably require using set() rather than Set.new() as we can call that a literal.

I don't follow this argument. There doesn't seem to be any rule against .new() methods accepting a Nil argument as Nil​:

Pair.new("foo", Nil).value.WHICH Nil|U15269208

Trying things out now, I see that the behaviour of the test cases with which I started this ticket has changed, and that currently set() and Set.new() do actually behave differently. (In the original bug report I didn't distinguish between set() and Set.new(), not seeing any difference.) set() now accepts a Nil element, and Set.new() accepts Nil as a sole argument; the substitution of Any now happens only for Set.new() and only when there's at least one other argument​:

set(Nil) set(Nil) set(3,Nil) set(3 Nil) set(Nil,Nil) set(Nil) Set.new(Nil) set(Nil) Set.new(3,Nil) set((Any) 3) Set.new(Nil,Nil) set((Any))

That's pretty weird. The subject line of this ticket still seems applicable.

                  you'll note List\.new wont transcribe

Nil either.

That looks like a separate bug.

-zefram

OK, I can see your argument for the difference between Set and Array here.

Also, having now had a look at the current implementation I cannot argue with confidence that it's "that way for a reason." I'd suggest some core people have a lively alcohol-fueled discussion about which of set(), Set.new(), and List.new() should have an "is raw" added to their candidates, or work directly on a Capture.

Syntactically it may not be possible to get some reified objects into a set's keys through normal means. What syntax would we use to get Empty into a Set, for example?

I'd just offer that some reified objects in Perl6 come with attached syntax-like behaviors, though perhaps in the specific case of Nil this ticket may be a reasonable ask... apparently it does seem to be comfortable in object hash keys.

But for things like Empty, we'd need some special constructor, and depending on what Nil is *supposed* to do when passed into slurpies and what flavors of slurpy let set() and Set.new() behave sanely (?), Nil might, too. Considering Nil's default-finding behavior seems to have never been implemented, I'm less sure the answer to that has been thoroughly pondered.

As to the set-theory argument, I don't get the feeling Perl6 is aiming for a pure-FP level of calculus, and I don't think it is necessarily true that there is a "base language" which prevents Perl6 from using reified objects as means to implement what would otherwise be done with syntax when that is an efficient way to do things, just to adhere to a theoretical principle. The closest we have to a base language is the types that have to BOOTSTRAP, and Set isn't one of them.