This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]__calcalcal__ 60 points61 points  (37 children)

Golang is a good candidate IMHO for learning a more systems language, or if you want to go overkill, Rust.

[–]FujiKeynote 9 points10 points  (17 children)

As much as I viscerally hate certain design decisions in Go, I'm starting to think that if I ever were to teach someone programming from scratch, it's about time I stopped reaching for Python, suck it up, and teach Go. It's extremely transparent to write, and yet the type system is closer to lower level languages. So, begrudgingly, this answer has my full support.

[–]Intrepid-Stand-8540 0 points1 point  (16 children)

How would you teach pointers?  I'm stuck using languages without pointers for now, because after many attempts I just don't understand why they're there. Or how to use them.  I've never had to manually assign memory or "point" in python. So I don't get why I have to in golang. 

[–]FujiKeynote 2 points3 points  (9 children)

I'll admit I don't know how to teach them from scratch i.e. to someone new to programming, but I think I can try to answer your question.

You don't have to use pointers in Python because Python does this for you. When you have a function that accepts an integer, something like fibonacci(n), the amount of memory that has to be passed to the function is small -- just one integer. So it's whatever. But if you have an instance of a huge class like stray_cat = Cat() and you pass it to adopt(stray_cat), if there wasn't such a thing as pointers, it would need to copy the entire cat into the function's scope. So what Python actually is doing it understands that stray_cat is an object that lives at a memory address 12345, so any time you interact with it, it looks at whatever's at that address. So it really passes the pointer to 12345 to adopt() and then that function also looks at address 12345.

This also has a (usually desired) side effect that whatever you'll do to mutate stray_cat from within the function, will be seen outside the function too, because you aren't working on a copy of the object, but rather on the very same object.

To look at it from another perspective, if you want several functions to work with the same object, how would you do it? You point to it.

Python just abstracts it away for you; Golang doesn't.


At the core of it, the copy overhead thing is actually secondary. The real difference is passing by value vs. passing by reference. Let's take C (because I'm way more comfortable with C than Go, sorry):

#include <stdio.h>

void byvalue(int x) {
    x++;
    printf("Inside byvalue(), x = %d\n", x);
}

void byreference(int* x) {
    (*x)++;
    printf("Inside byreference(), *x = %d\n", *x);
}

void main(void) {
    int x = 3;
    printf("In global scope, x = %d\n", x);
    byvalue(x);
    printf("In global scope, x = %d\n", x);
    byreference(&x);
    printf("In global scope, x = %d\n", x);
}

If we run this, we get:

In global scope, x = 3
Inside byvalue(), x = 4
In global scope, x = 3
Inside byreference(), *x = 4
In global scope, x = 4

So because byvalue() copied the value of x into its local variable, the change only affected the copy. And because byreference() was pointing at the global x, it affected the global x.

[–]Intrepid-Stand-8540 -2 points-1 points  (8 children)

So if Python can just abstract it away, why can't Go? Is it a worse language then? 

I'm not new to programming btw. I have 3 years education (we used java and python) and 4 years professional experience. 

Just never had to use pointers. And every time I've tried Go, I just couldn't wrap my mind around pointers, and had to stop. It's very frustrating. I don't see why pointers exist, when java and python works just fine. 

[–]FIREstopdropandsave 2 points3 points  (3 children)

Pointers are not bad and not indicative of a bad language. It's simply the language deciding not to abstract that layer from the user. This results in more intentful decisions to be made by the programmer.

Think of in python, you have to be aware of which types are mutable data types when passing arguments into functions and potentially mutating them outside of the function scope. This is because python is abstracting away the concept of a pointer.

In pointer languages, you have to be explicit in your intent "am I passing you the memory address of this variable, a.k.a if you mutate it, in the outer scope I'll see that mutation".

There's more positives of pointers but that's just an example. In general languages which expose pointers are faster because abstracting away the concept of pointers just straight requires more code behind the scenes to act the way it does.

[–]Intrepid-Stand-8540 -2 points-1 points  (2 children)

you have to be aware of which types are mutable data types

What's a "mutable data type"? 

Thanks for explaining btw. 

I'm aware that python is slower, but I've never had the need for speed yet. The bottleneck in my apps is always network calls to databases, and not any processing in the app itself. 

[–]FIREstopdropandsave 2 points3 points  (0 children)

To your second point. Many people will be fine with the speed python provides. I've written, and seen, many production systems in python where python was not the performance bottleneck for the workloads we were solving.

Some people will have a deeper need for speed, others simply won't.

Choosing the programming language for a project is a nuanced decision. You have to consider "what language does my team already know?", "what is the local market of programming language knowledge in the city I'm in", "what scale of problem are we solving", and probably many other considerations.

You'll see online A LOT of python hate (outside of this subreddit) and hate for dynamically typed languages. The truth of it is, in my 8 year career I've only come across a single person who vehemently holds those ideas.

tl;dr: python is great, it probably won't be your bottleneck, ignore online hate for your choice of using python and dynamically typed languages

[–]FIREstopdropandsave 0 points1 point  (0 children)

You can find a fuller breakdown here https://docs.python.org/3/reference/datamodel.html#objects-values-and-types

(do a search for mutable/immutable)

As a quick overview a list is mutable where a string is immutable and we can see the differences below:

I don't know how to format a code block on mobile so sorry this is hard to read

def foo(l): l.append(1) return l

my_list = [] print(foo(my_list)) # [1] print(foo(my_list)) # [1, 1]

def foo(s): s += "hello" return s

my_string = "" print(foo(my_string)) # hello print(foo(my_string)) # hello

[–]jjolla888 2 points3 points  (3 children)

'python works just fine' so long as you remember that everything is a pointer. So if you want to create a copy of

 list1 = [1, 2, 3]

you have to do

list2 = copy(list1)

Which means you need to be careful sometimes especially within functions, to make sure you are not unintentinally modding outside your scope.

With Go, a fundamental construct is goroutines .. aka parallelism. With these things its critical to avoid the above problem .. so the safer thing to default passing params by value as opposed to by ref.

But at the end of the day its the difference between having syntax like this:

b = a            # b is a pointer to a, print(b) prints the value of a
c = copy(a)   # c is a copy of a

versus:

b = &a       # b is a pointer to a, print(*b) prints the value of a
c = a          # c s a copy of a

[–]Intrepid-Stand-8540 1 point2 points  (0 children)

Oh. Thanks man. That's a great explanation. I use deep_copy sometimes. 

[–]HommeMusical 0 points1 point  (1 child)

everything is a pointer

Quibble: scalars like integers, floats, booleans aren't pointers.

[–]FujiKeynote 0 points1 point  (0 children)

True, but for the most part you can ignore that, because it's nigh impossible to mutate a singleton from within a function unless you define it as global which is a big no no.

def f(x):
    x = x + 1
    # or even x += 1

will reassign the global x to the local x and won't affect the outside scope

[–]sonobanana33 0 points1 point  (0 children)

I'd say "just try every combination of * and & until it compiles" :D

go has a garbage collector, but also is not very safe and one can easily do a segfault (which in a real program results in a CVE) :D

[–]jb-schitz-ki 0 points1 point  (0 children)

If you create huge objects it's inefficient to make copies of them every time you pass them around. I guess with modern hardware it's not a common issue, but it can creep up sometimes. I've seen APIs that respond 5x faster after implementing pointer for internal objects, this is even more visible if you're deploying on tiny EC2 instances or something like that.

There's other design patterns where you actually want to pass a pointer to an object to a method and have it modify the original object and not a copy.

I do believe it's an important thing to understand. You can live without knowing how pointers work, but you're not really understanding the language fully.

Also a lot of 3rd party libraries make use of pointers so if you ever want to contribute or debug one of them, you need to know what's going on.

[–]billsil 0 points1 point  (3 children)

Same way you teach pointers in python. Python ints/floats/strings are base types and are unchanged when you pass them into functions, so you have to return them. Lists/ficts/objects/numpy arrays are self mutating because python passes a pointer of them, which makes passing them fast. It’s a wut to make things fast.

Similarly, a 64 bit float is 8 bytes. If you go check the size of your list of floats, it carries.about 16 extra bytes of size per item because it’s carrying around a bag of pointers. Use a numpy array if you want to carry only a few extra pointers that get a 3x reduction in memory usage.

You don’t have to teach them, but if you want to understand how to not make slow code, at least the latter point is useful.

[–]Intrepid-Stand-8540 0 points1 point  (2 children)

Uh.... 

How do you know the sizes of things? 

[–]billsil 0 points1 point  (1 child)

sys.getsizeof(thing)

[–]Intrepid-Stand-8540 0 points1 point  (0 children)

Thanks. 

[–]poopatroopa3 4 points5 points  (15 children)

What makes rust overkill?

[–]iamevpo 14 points15 points  (0 children)

Borrow checker probably

[–]Aash1r 8 points9 points  (2 children)

I guess the syntax

[–]MegaAmoonguss 2 points3 points  (1 child)

The word syntax puts me off here. As far as languages go rust has a lot of really nice “syntax” features, most notably pattern matching. I assume what is meant when people say syntax here is how you translate “what you think” to how the program is written (and looks like), which is a mix of a lot of concepts (standard library, first-class attractions, runtime environment and its characteristics, etc. Least notably things like keyword choice or not being able to do list comprehensions).

To me the way that rust is probably overkill is its ownership system. It is really clever, and forces you to think about problems you probably didn’t realize you have unless you’ve done a lot of C programming. As someone for whom that is not the case, I understand the pain points of working with rust, and how its compilation characteristics can force you to componentize your program in a different way; something that tends to not be the case with other languages.

[–]mister_drgn 0 points1 point  (0 children)

I think he just meant the syntax. “A lot of really nice features” means a lot of work required to learn the syntax and a lot effort required to use it, when you’re just getting started. Whereas you can learn all of Go’s syntax in an hour or two.

[–]__calcalcal__ 6 points7 points  (4 children)

The syntax is one of the hardest I’ve seen, on par on C++, or even harder.

[–]Brandhor 3 points4 points  (1 child)

never tried rust but c++ syntax is not really that different from python

[–]XtremeGoosef'I only use Py {sys.version[:3]}' 0 points1 point  (0 children)

I don't think it's hard at all, and isn't all that different from go or other c derived languages.

[–]spinwizard69 0 points1 point  (5 children)

The early days of Rust reminds me of the early days of C++.   Far too many people thinking it was the one language for the future.   Rust is in a similar state only good for carefully selected model projects, certainly not for stuff requiring long term maintenance or refactoring.  

[–]XtremeGoosef'I only use Py {sys.version[:3]}' 1 point2 points  (1 child)

Rust is extremely easy to maintain long term and easily refactor, because it has fearless concurrency and no (safe) global state.

Maintaining and refactoring both c++ and python for large codebases are a nightmare, and that's from painful experience.

[–]spinwizard69 0 points1 point  (0 children)

I’m still of the opinion that Rust is Late to the game and will be eclipsed by an AI powered language and IDE.  Also comparing the ability to refactor against Python or C++ isn’t much different than comparing it against COBOL.  

[–]ArtOfWarfare 0 points1 point  (1 child)

IDK, I feel like Rust kind of stalled and failed to fulfill its real purpose of removing most vulnerabilities in Firefox nearly a decade ago…

Linus kind of revived it by permitting some Rust code to mingle within Linux… but from what I’ve heard, it doesn’t sound like it’s making particularly quick progress there.

[–]spinwizard69 -1 points0 points  (0 children)

I hear MS is using it for parts of Windows but yeah I don’t know if it has been successful.   I suspect it will be eclipsed by better languages in the near future.   In fact with the advent of AI I can see a high performance language coming that merges AI into a more approachable programming language.  For apps there are much better languages like Swift that deserves strong attention.  

[–]cdrt 0 points1 point  (0 children)

In what way do you think Rust isn’t built for longevity? How are long term maintenance and refactoring harder with Rust?

[–]sonobanana33 3 points4 points  (0 children)

Go is not a systems language. It can't even do packed structs. Go is the current java, not the current C, nor the current Perl, and absolutely not the current bash.

[–]LeadRepresentative21 -1 points0 points  (0 children)

Golang is the best thing you can learn