Hacker News new | ask | show | jobs
by littlestymaar 2228 days ago
Your can't really do anything real without pointers in Go though because you won't be able to mutate states out of a function.
3 comments

That's definitely true, but I've often seen beginners just say:

    type Foo struct { ... }
    func (f *Foo) Bar() { ... }
And go "Aha, that's an object and a method, I get it!" but when asked whether they should be using a pointer there or not, have no idea what that even entails.

In other words, it's possible to neglect the details of pointers easily and have things generally work

William Kennedy has an excellent post on this https://www.ardanlabs.com/blog/2017/06/design-philosophy-on-... As someone who has written (and read) a decent amount of Go the value vs pointer semantics of the function informs me of how the creator intends for the type to be used. Seeing pointer receivers informs me that there is an expectation of modifying the state while value pointers (which are thankfully more common) return a copy of the data.
> Tom has also mentioned that a box of copy paper can hold 100k lines of code.

that comes out to 20 loc per 8.5x11 piece of printer paper.

I feel like that's nearly an order or magnitude too low.

Remember ByVal and ByRef from VB? (I hope you don't, it was the dark ages) but those to terms I've found are very useful for getting folk introduced to pointer - and also why.
That is really interesting to me! I never used VB, is the only difference here that it is "named" ByVal and ByRef that you think made it easier for people to understand than "pointers"?
Well, I use those terms (ByVal/Ref) and then say, one is a copy (byVal) and the other is a reference (pointer to you and me) to the thing.

So, pass small things ByVal (int, float), it's on the Stack.

Pass big things (Object) ByRef cause they're in the Heap.

Then I start saying pointer more than ByRef and the link is made.

Then on to ByRef/Pointer to how that then manipulate the shared data.

Once that basic is done, we refine/clarify around what Pointer really is, and also it's syntax.

I'm not sure I've seen this explained so well in so few words. Thanks!
I learned Rust & Go pretty much at the same time, and it actually works quite well because most of the paradigms are actually the same (structs & function implementation on top of them, error as return value). Rust is clearer about the point you mention[1], while Go introduction spend more time explaining things like threads and channels (which I wasn't familiar with), and finally Rust help you to use them properly, thanks to the Send & Sync trait, which make you realize when you're doing racy things.

[1]: https://doc.rust-lang.org/stable/book/ch05-03-method-syntax....

Yeah, Go focusing on threads and channels I agree is generally a painful new concept for beginners to learn. I've also seen beginners often "over-correct" and start using channels and goroutines for things they really shouldn't.

How was your learning experience with Rust's borrow checking and pointers? Was it introduced to you early on, or later on?

I learned Rust through the book (first edition), which at this time was the only available material, and it was explained right at the beginning (at least borrowing, ownership and mutable borrows).

I like when things are explained upfront, and actually my biggest issue when learning Rust came from not having the smart pointers (like RefCell and Rc, which relax the ownership constraints) explained at the same times as regular ones.

This is me right now. I get the basics of Go, but I don't understand when i'm suppose to have something be a pointer, and when i'm suppose to use * or & . Anyone who has a good explanation in a EL5 way, would help me a lot!!
Imagine we're working together and I have some text document I want you to work on and update. If I send you the text by email, I send a copy of it (the original text is still on my computer). If you modify that copy and keep it for you, I won't ever know what you did. I just used a function

    func (c Coworker) SendForUpdates(d Document) {
        ...
    }
That wouldn't make sense. You worked hard and I don't even know what you did. So, what I would expect you to do is, once you made updates on the copy, to send me back that copy by email. That would be akin to

    func (c Coworker) SendForUpdates(d Document) Document {
        ...
        return d
    }
I sent you a copy, and you returned another updated copy. That is "pass-by-value", the default, no-pointer style.

Now, let's say I think those emails back and forth and boring. Rather than sending you a copy of the text each time, I could rather use Google Docs, and send you the link to that document. Its URL, rather than a copy of its content. Now, you can just go to that URL and do the updates on the document. You don't have to send me back the document: you're working on it, not on a copy of it! Well, that URL is a reference to the document rather than the document itself, or, if you prefer, a pointer to it. So, now, the function would be

    func (c Coworker) SendForUpdates(d *Document) {
        ...
    }
And we're done, no more back-and-forth dance now! That is "pass-by-reference".

You don't only use "pass-by-reference" just to be able to check updates on the document sent, by the way. If I want to send you some text just for your information and I don't expect any kind of update, I'll use pass-by-value (the very first function). But what if I want to send you a 3 GB video? I can't send that through e-mail! Sending a copy would be totally inefficient. Once again, I'll send you a pointer, an URL to download the video:

    func (c Coworker) InformText(d Document) // d is small: pass-by-value

    func (c Coworker) InformBigVideo(v *Video) // videos are huge: pass-by-reference
Why not use pointers everywhere by default, they seem easier, right? That's basically what java and python do. Well, they can be tricky too. I gave you the URL to the link and you could work on it. Once you're done, I don't want you to modify the document anymore. I want to send it to our boss. But, how could I know you didn't keep the URL somewhere in your bookmarks? How do I know, of all the coworkers I sent the URL to, one of them doesn't keep on updating that document even when I don't want to anymore? With copies, I'm safe, do whatever the hell you want with your copy, I don't care anymore. But a reference to the original document? That can be dangerous.
>That's basically what java and python do. Well, they can be tricky too.

But,you can easily make the classes/collections immutable to avoid the issues you mentioned. I think in java records and many collections are immutable by default. Immutability is fundamental to functional style programming.

This is a phenomenal explanation. Thank you - I've saved this for next time I need to explain to a junior.
You want a function that accepts a pointer when you want to modify an outside value within a function without returning the value. In that case, the func accepts a pointer (like in `func(p int)`) and you pass it either a pointer (a var declared as an {some_type}) or the address of a variable (with &variable).
Challenge accepted https://play.golang.org/p/iTwreChPZH8

ᕕ(¬ ͜ ¬)ᕗ

Slices are (fat) pointers though ;)
Yes you can, lots of functional languages work without mutation. I'd go as far as to say mutating should be the exception, not the default, in more traditional languages.
Go misses almost all tools a functional programming language offers (no iterator or combinators of any kind) and even if you really wanted and decided the verbosity wasn't a problem for you, I'm pretty sure the compiler doesn't optimize it well (it does a lot less optimisations than LLVM for instance).
But those things don't make a language functional. It's like saying there are no lambda statements as there is no lambda syntax, but syntax != feature.

I mean I see your point, and if I really wanted to be fully functional I'd use a language _with_ those things like Haskell. But not being mutable is easy in Go too.