@Michael%20Toy, I disagree! You're entirely correct to like what TypeScript 
does! It's a really cool example of a "modern language" strutting its 
stuff -- it's called "Narrowing": 
https://microsoft.github.io/TypeScript-New-Handbook/chapters/narrowing/ As 
a transpiler targeting Javascript, I think it's entirely appropriate. In 
the AST construction phase, each logical constriction of the possible space 
of types that could describe a value is propagated onwards. If you accept a 
parameter, "pet", as iCat or iDog iPlant, if you want that reference fields 
specific to animals, you must first cause an event which would cull the 
search space for any value that is not a mammal. If you want to try 
spray(hose(pet)), you need to either check that the pet isn't a cat, or 
else handle the possible error result.

The github.com/jackc/pgtypes repo has a really ergonomic approach to curing 
the nillness: package the value with a boolean, and receive a guaranteed 
value when you unwrap it. If you cared about whether it had a value, check 
that bool.

Otherwise, resolve that maybe-value to a value ASAP, and go from there.

@Axel, I really did mean what I said. TypeScript and Rust are both very 
effective at making you know that you're sure you know that the state is 
valid. There are programs that cannot be written into that paradigm, and 
there are also plenty of times when that's inconvenient and takes a while 
to spell out. Of course, that safety comes with tedious requirements of 
proofs, and different needs yield different tradeoffs, and you typically 
get an "unsafe" escape hatch. TypeScript is no different, except that the 
"unsafe" package is called "Javescript." The problem in Go, if you read 
through the proposals @Lance%20Taylor%20Armstrong shared, isn't that it 
wouldn't help, rather that Go is already in a good spot safety-speed-wise, 
and you can nudge it over it you want it somewhere else; just don't expect 
to make everyone else nudge it over simultaneously.

@Brian%20Candler, If Go allowed operator overloading or custom allocators, 
that'd be fine. Go doesn't support such, and if you opened a proposal for 
either, I'd bet you $5 it gets closed immediately. The more convenient 
approach is to implement a type like below. If you disagree? So help 
me....I'll.... I'll disagree with you?

```Go
type Box[T any] *T

func (ptr Box[T]) Raw() *T {
  return (*T)(ptr)
}

func (ptr Box[T]) IsNil() bool {
  return ptr.Raw() == nil
}

func (ptr Box[T]) Value() (checked T) {
  if blind := ptr.Raw(),  ok := !IsNil() bool; ok {
    checked = *blind
  }
  return checked
}

I recently saw a talk called "It's all about Tradeoffs". This is an 
excellent example of that. Maybe the above could be improved by static 
checking and optimization, but it's never as cheap as just trusting there's 
something there, so long as there actually is something there.
On Friday, March 25, 2022 at 1:41:07 PM UTC-5 Michael Toy wrote:

> The discussion is quite informative for me to read, thanks for responding. 
> Go uses nil in a way which I don't quite yet grok, and so I had no idea if 
> it was even a reasonable thing to wish for. Today I am writing in 
> Typescript, and the way null is integrated into the type system now (after 
> a while) feels natural and helpful to me.
>
> Sam is correct, there is bug in my Go snippet in the post. For humor value 
> only, I would like to point out that the imaginary Go compiler I was 
> wishing for would have found that bug!
>
> I think Brian gets to the heart of my question, which is "If I really 
> understood Go, would I want something like this". I am hearing, "No, you 
> would not"
>
> I think if I were to have a long conversation with Axel about "what is it 
> that makes programs robust and maintainable" we'd go round in circles a 
> bit, as should happen any time you talk about something complex and 
> important. I think I disagree with some statements, but even the 
> disagreement is super helpful.
>
> Thanks for the discussion!
>
> -Michael Toy
>
> On Thursday, March 24, 2022 at 12:22:44 AM UTC-10 Brian Candler wrote:
>
>> The OP hasn't said specifically which language or feature they're 
>> comparing with, but I wonder if they're asking for a pointer type which is 
>> never allowed to be nil, enforced at compile time.  If so, a normal 
>> pointer-which-may-be-nil would have to be represented as a Maybe[*T] or 
>> union { *T | nil }. To use such a pointer value at runtime you'd have to 
>> deconstruct it via a case statement or similar, with separate branches for 
>> where the value is nil or not-nil. I am sure there have been proposals 
>> along those lines floated here before.
>>
>> I don't think this would negatively affect code readability, because a 
>> function which takes *T as an argument can be sure that the value passed in 
>> can never be nil (the compiler would not allow a value of type Maybe[*T] to 
>> be passed).  Conversely, a function which accepts Maybe[*T] as an argument 
>> is explicitly saying that the value passed may legitimately be nil, and 
>> hence it needs to check for this.
>>
>> I like this idea in principle, but in the context of Go it would mean 
>> that *T does not have any valid zero value, so you would not be able to use 
>> it in any variable or struct which is not explicitly initialized.  This 
>> would definitely not be Go any more.
>>
>> type T ....
>> var t T  // ok
>>
>> var p1 Maybe[*T]  // ok
>> var p2 *T = &t  // ok
>> var p3 *T  // fails to compile (no zero value is available)
>>
>> type A struct {
>>     a Maybe[*T]
>> }
>> var q1 A // ok
>>
>> type B struct {
>>     b *T
>> }
>> var q2 B = B{b: &t} // ok
>> var q3 B  // fails to compile (cannot create zero-valued instance of this 
>> struct, because it includes a member which cannot have a zero value)
>>  
>> On Thursday, 24 March 2022 at 09:41:23 UTC axel.wa...@googlemail.com 
>> wrote:
>>
>>> One thing to be clear: It is very different if we are talking about 
>>> "emit a warning if a value is known to be nil" and "emit a warning unless a 
>>> warning is known not to be nil". The former seems fine to me - it is IMO 
>>> fine for this code to cause a vet-failure:
>>>
>>> var x *int
>>> y := *x
>>>
>>> What I'm opposing is the original idea, for this code to cause a 
>>> vet-failure:
>>>
>>> func (x *int) { *x }
>>>
>>> Importantly, whether or not a value is `nil` is *always* going to be a 
>>> heuristic <https://en.wikipedia.org/wiki/Halting_problem>.
>>> If we complain about "known to be nil", every finding will always be a 
>>> bug. I don't think it's objectionable to find them statically.
>>> If we complain about "not known not to be nil", a significant number of 
>>> findings will be non-bugs, leading to changes as OP suggested. So, I'm 
>>> assuming that's the situation we are talking about.
>>>
>>> On Thu, Mar 24, 2022 at 9:40 AM Sam Hughes <sam.a....@gmail.com> wrote:
>>>
>>>> @axel, it my feel counter-intuitive, but a possible runtime panic 
>>>> converted to a compiler error is an increase in how robust the code is.
>>>>
>>>
>>> Of course. But that's not what we are talking about. We are converting 
>>> *some* runtime bugs into compiler errors (really, vet checks, we can't fail 
>>> to compile because of backwards compatibility).
>>> But most of them, where it's not clear from the code that a particular 
>>> pointer is going to be nil, will get the treatment suggested by OP. Which 
>>> ends up exploding the state-space of possible behaviors of a program, 
>>> making it exponentially harder to know what it's doing.
>>>
>>> That's IMO the less intuitive thing. People tend to think "crashing code 
>>> is unreliable". But really, crashing is quite a safe and easy to reason 
>>> about behavior. But having to constantly program against any possible bug 
>>> leads to unmaintainable, brittle, impossible to reason about code. If every 
>>> index-expression, every pointer-dereference and every method call needs to 
>>> be wrapped in a conditional, it becomes impossible to really understand 
>>> where a failure is coming from and how a program behaves in different 
>>> failure modes.
>>>
>>> The weird part, at least, why it might feel weird, is that you might 
>>>> never encounter the issue as a runtime panic, but as a compiler error, it 
>>>> will hit you every time. This will make exploring and experimenting with 
>>>> unfamiliar imports, features, and patterns more complicated, but it is the 
>>>> entire point, to my mind, of having a type-system.
>>>>
>>>> P.S. Bug in the snippet given by OP? I expected to see "x != nil" 
>>>> instead of "x == nil", or else change comments around.
>>>>
>>>>
>>>> On Wednesday, March 23, 2022 at 4:15:13 PM UTC-5 
>>>> axel.wa...@googlemail.com wrote:
>>>>
>>>>> Personally, I think this leads to very unreadable code, for no real 
>>>>> benefit.
>>>>> If a nil-pointer dereference happens unexpectedly, that's always a 
>>>>> bug. A panic is the correct signal to tell me about that bug, so I can go 
>>>>> ahead and fix it. So, making my code less readable to get less robust 
>>>>> code 
>>>>> seems like a lose-lose proposition to me.
>>>>>
>>>>> Of course, people can always choose to write/use whatever static 
>>>>> checker they want. I'm not opposed to this existing. I just don't think 
>>>>> it 
>>>>> should be in the compiler or in `go vet`.
>>>>>
>>>>> On Wed, Mar 23, 2022 at 10:01 PM 'Michael Toy' via golang-nuts <
>>>>> golan...@googlegroups.com> wrote:
>>>>>
>>>>>> I barely understand Go, so this is likely a stupid idea. Since Go 
>>>>>> uses nil for a lot of things, lots of things can be nil.
>>>>>>
>>>>>> I am not a huge fan of the null safe accessor. ( 
>>>>>> https://github.com/golang/go/issues/42847 )
>>>>>>
>>>>>> I am a huge fan of the compiler telling me the places where I have 
>>>>>> not checked for nil ... It took me a while to get used to languages 
>>>>>> which 
>>>>>> do this, but now I can't imagine living without it.
>>>>>>
>>>>>> Is it crazy to wish for ...
>>>>>>
>>>>>> if x == nil {
>>>>>>   // this does not produce an error because x is known not to be nil
>>>>>>   x.interfaceFunc()
>>>>>> }
>>>>>> // this DOES produce an error/warning if y is possibly nil
>>>>>> y.interfaceFunc()
>>>>>>
>>>>>> -- 
>>>>>> You received this message because you are subscribed to the Google 
>>>>>> Groups "golang-nuts" group.
>>>>>> To unsubscribe from this group and stop receiving emails from it, 
>>>>>> send an email to golang-nuts...@googlegroups.com.
>>>>>> To view this discussion on the web visit 
>>>>>> https://groups.google.com/d/msgid/golang-nuts/5a700cd9-9274-4756-80a6-9d322232afebn%40googlegroups.com
>>>>>>  
>>>>>> <https://groups.google.com/d/msgid/golang-nuts/5a700cd9-9274-4756-80a6-9d322232afebn%40googlegroups.com?utm_medium=email&utm_source=footer>
>>>>>> .
>>>>>>
>>>>> -- 
>>>> You received this message because you are subscribed to the Google 
>>>> Groups "golang-nuts" group.
>>>> To unsubscribe from this group and stop receiving emails from it, send 
>>>> an email to golang-nuts...@googlegroups.com.
>>>>
>>> To view this discussion on the web visit 
>>>> https://groups.google.com/d/msgid/golang-nuts/14f6ade8-fe5b-4876-b692-ed98764eaa55n%40googlegroups.com
>>>>  
>>>> <https://groups.google.com/d/msgid/golang-nuts/14f6ade8-fe5b-4876-b692-ed98764eaa55n%40googlegroups.com?utm_medium=email&utm_source=footer>
>>>> .
>>>>
>>>

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/adb3260f-d6a8-4672-9a42-52d39a6a3bd9n%40googlegroups.com.

Reply via email to