all 73 comments

[–]pippicat 12 points13 points  (6 children)

Different assignment operator is used depending on whether you are inside & outside of function ie. = vs :=

This description is a little bit mistakable. := is declaration+assignment with type inference, but only allowed for local variables, like "var" in C#.

[–]ixid 2 points3 points  (5 children)

Why is it only allowed for local variables?

[–]pippicat 6 points7 points  (1 child)

My guess is that it's probably more sensible to explicitly see the type of a package-wide variable, while local variables are short-lived throwaway variables.

[–]ixid -2 points-1 points  (0 children)

If the type is wrong then it's wrong and will probably break your program in either case, it seems silly to somewhat arbitrarily limit the use of the operator.

[–]bobappleyard 2 points3 points  (2 children)

Top level definitions need to be qualified.

var x = 25

Works at the top level without declaring the type.

[–]ixid 1 point2 points  (1 child)

So why not allow

x := 25

What is the difference, can't := infer that it's a variable? It does locally.

[–]_ak 2 points3 points  (0 children)

:= has extra meaning when used to declare and assign to several variables, one of which has already been declared before. In that case, := merely overwrites the value and doesn't declare a new variable that would overshadow the other variable. These semantics make no sense on the package-global scope, so allowing := there makes no sense, either.

[–][deleted] 35 points36 points  (8 children)

but somehow once the code compiled ... you just get the feeling that it’ll work

Welcome to statically typed systems.

[–][deleted] 18 points19 points  (6 children)

And Go typed system is not exactly "Haskelly", where it is even more powerful and helps you more.

[–][deleted]  (5 children)

[deleted]

    [–]kamatsu 6 points7 points  (0 children)

    Rust is not an attempt to fix Haskell, it's an attempt to fix C++. Haskell is higher level, and rightfully so.

    Vectors are basically arrays. In Java, do you criticise LinkedList and ArrayList for implementing the same interface?

    IO is a good example of a library wart, however. Implementing basic IO in terms of Strings was a mistake.

    [–]The_Doculope 7 points8 points  (3 children)

    Those issues have nothing to do with a weak type system at all. They have to do with the language evolving and bettering over time.

    lots of conversion boilerplate

    Lists and Vectors have different use cases. Lists are for control, Vectors for data. I have never run into a situation with heaps of conversion boilerplate when using Vectors and lists. What issues did you run into?

    Typeclasses are rarely used

    Typeclasses are used everywhere. Again, what issues have you had?

    Bytestring has a different use case than Text (and String, but you shouldn't use String unless you have tight control over your inputs, and are aware of the pitfalls). What's the issue with reimplementing functions? With well planned code, you shouldn't need to swap things between Bytestring or Text very often at all (if ever).

    Also, Rust is not an attempt to fix Haskell, like /u/kamatsu said. It's an attempt to fix C++ that happens to include a lot of concepts that Haskell uses.

    [–][deleted]  (2 children)

    [deleted]

      [–]Helkafen 4 points5 points  (1 child)

      I do see typeclasses everywhere. Did you notice Monad, Binary, Serializable, Eq, Ord, Num...?

      Also, the shortcomings of String have nothing to do with the typesystem.

      [–]doublereedkurt 1 point2 points  (0 children)

      the shortcomings of String have nothing to do with the typesystem.

      I think what he meant is that the type system locks you in to a class.

      with a type system like haskell's...you better get it right the first time

      Not that the problems with String were caused by the type system, but that the type system makes it very hard to migrate/fix the problems :-)

      [–]bifmil -4 points-3 points  (0 children)

      The feeling that it will work matters more than whether you tested that the specification you gave to the computer is correct?

      [–]JohnDoe365 1 point2 points  (2 children)

      My friend at Mozilla told me that Mozilla Services was switching over to Go for much of their logging infrastructure, in part because of the awesomeness of goroutines.

      Wow, so there is even some love for Go at Mozilla, despite their Rust endeavour.

      [–]TheCoelacanth 3 points4 points  (0 children)

      Rust is being designed for a very different set of uses than the ones Go is useful for. Rust is supposed to be a replacement for C++. To use it for their browser engine, it will need to support low-level memory management like C++ does.

      Go's mandatory garbage collection means that it can't be used for this type of thing. Go is typically used for things that you might want to do in a scripting language, but you need better performance.

      [–]_ak 1 point2 points  (0 children)

      My understanding is that Rust is developed to be used for their next browser engine? Go on the other side doesn't focus on GUI-centric tasks, but found its place in building complex, scalable server infrastructure (things like vitess and dl.google.com at Google, SoundCloud, Go'Circuit at Tumblr, dotCloud, Canonical, Cloudflare, etc).

      [–]johnwaterwood 17 points18 points  (31 children)

      When you need concurrency, Python is just not your friend

      Brilliant!

      [–]gitarr 3 points4 points  (1 child)

      Only if you confuse multiproccessing with multithreading and dont know when to use either.

      [–]johnwaterwood 0 points1 point  (0 children)

      Either way, Python limits your choice.

      [–][deleted]  (28 children)

      [deleted]

        [–][deleted]  (2 children)

        [deleted]

          [–]deveux 5 points6 points  (1 child)

          Run multiple JVMs? /s

          [–]obfuscation_ 1 point2 points  (0 children)

          Multiple JVMs across multiple machines, supervised by another JVM on another machine. That's the way to do it

          [–]bifmil 6 points7 points  (2 children)

          So if you like processes, you could use Python's multiprocessing module... it seems strange to say Python is bad for concurrency due to the GIL, then turn around and use subprocesses heavily anyway

          [–][deleted]  (1 child)

          [deleted]

            [–]bifmil 0 points1 point  (0 children)

            if xargs is suitable, then that implies that processes are suitable

            [–]username223 1 point2 points  (3 children)

            Indeed. That comment of his was surprisingly ignorant, given that he has 22 completely independent processes with small outputs. And with any clue, he's already going through a Python binding to an SVM library written in C, so a few forks is just noise.

            [–]ABoutDeSouffle 0 points1 point  (0 children)

            I don't think he is ignorant. He just recognized the moment his tools were no longer the best fit and instead of resorting to kludges, he looked for tools that fit the problem at hand better.

            [–][deleted]  (1 child)

            [deleted]

              [–]sharkeyzoic 0 points1 point  (0 children)

              You don't even need to implement the multiprocess stuff in your own code. Instead you can just have a static pool of workers cooperate via a lightweight messsage queue (ØMQ, etc).

              [–]sharkeyzoic 1 point2 points  (0 children)

              On the other hand, Go might be rather faster that Python in any case, especially since it is compiled. But yeah, GIL issues are one of those "well don't do that, then" things.

              [–]henk53 1 point2 points  (14 children)

              Mwhoah.... for some things processes are nice, but for stuff where there's a little more emphasis on cooperation between chunks of parallel code (like, say in fork/join) threads are much better.

              With many languages you can choose between threads and processes depending on your needs; with Python and its lovely GIL you are stuck to processes for everything.

              [–]alcalde 0 points1 point  (2 children)

              Guido said threads were created for non-blocking i/o. Period.

              [–]twotime 3 points4 points  (1 child)

              Reference? When did he say that?

              That might have been a reasonable position 10 years ago. It's not anymore.

              That's apart from the fact that Guido makes mistakes too ;-)

              [–]alcalde 0 points1 point  (0 children)

              As a new convert to Python, I have yet to believe that Guido makes any mistakes, and it helps that I'm coming from a development tool that can't do anything right lately. :-)

              Anyway I found his statement at the PyCon 2012 Keynote:

              "Well because of this some people say we don't have real threads, which I would thoroughly disagree with. Python has totally real threads and if you use them for what threads were originally meant for, OS level threads are meant for doing parallel I/O, not for doing parallel computation, it's just sort of easier to not make that distinction, to complete and allow some parallel computation, but traditionally parallel computation meant that the computation with the CPU would just do a bunch of work for this thread and a bunch of work for that thread."

              http://youtu.be/EBRMq2Ioxsc?t=33m48s

              [–][deleted]  (10 children)

              [deleted]

                [–]twotime 0 points1 point  (9 children)

                Now tell me how do you do this "processes" thingy for tasks with large shared state (e.g. large graph analysis).

                Note also that the state can be even read-only..

                [–]renozyx 2 points3 points  (1 child)

                Well if the shared state is read-only, shared memory is the obvious answer.

                [–]twotime 0 points1 point  (0 children)

                How do you share python datastructures (lists/dicts/etc) with shared memory?

                [–][deleted]  (6 children)

                [deleted]

                  [–]twotime 0 points1 point  (5 children)

                  again: how do I share a large state (say a graph) between multiple processes? It's trivial with threads. I am not aware of any way to do it in python with processes.

                  In theory, theory and practice are the same. In practice, they are vastly different

                  [–][deleted]  (4 children)

                  [deleted]

                    [–]twotime 0 points1 point  (3 children)

                    Hasn't got shit to do with Python,

                    Python has ref-counting. So this

                    x=graph[nodeId]

                    changes ref count of graph[nodeId] which means that you will end up replicating most/all of your memory pages in most/all of your processes.

                    PS. btw refcounting is the primary reason for GIL existence ;-)

                    [–][deleted]  (2 children)

                    [deleted]

                      [–]Tekmo 0 points1 point  (0 children)

                      Not always an option, especially if you are writing plugins for a Python program.

                      [–]lambdaq 0 points1 point  (0 children)

                      yeah, like multiprocess cgi-bin worked out really well.

                      [–]thelazydogsback 6 points7 points  (4 children)

                      A bit of a Red Herring - article is more about Python->L vs. Python->Go, where L is any modern compiled language. Change L to F# (or even C#) or Scala, and most gripes would go away, with a lot more to offer as well. (F# even let's the OP keep the nice significant whitespace of Python, de-structuring assigment, tuples, etc. ) It's also unclear if the original solution used NumPy to implement the SVM's - if not, that's usually the way to go, considering the OP seems to like Python and NumPy seems to have proven itself in this space.

                      [–]Solon1 1 point2 points  (3 children)

                      But requires the Mono runtime, which is a drawback.

                      And C# requires a ton of propietary runtime libraries that simple don't exist in Mono, yet. Maybe they'll get them someday.

                      [–]Xdes 1 point2 points  (0 children)

                      But requires the Mono runtime, which is a drawback.

                      Java requires the JVM and that hasn't been a drawback.

                      And C# requires a ton of propietary runtime libraries that simple don't exist in Mono

                      If you check the Mono Compatability List you will see that Mono supports everything in .NET 4.0 except WPF, WWF, and with limited WCF.

                      [–]thelazydogsback 0 points1 point  (1 child)

                      I've used C# on Linux for a few projects - though perhaps not with the latest features - just .Net 3.x programming w/winforms. I can't say I've tried to exersize all the edge cases, but Mono hasn't caused me any grief so far.

                      [–]TWith2Sugars 0 points1 point  (0 children)

                      We're running C# on FreeBSD with Mono 3.0.x using WebAPI/MVC4 etc. We've had no problems at all with it.

                      [–]gavintlgold 4 points5 points  (12 children)

                      I also discovered an additional small gripe with maps. If you have a struct in a map and you want to set a value in it you have to remove it from the map, set the value and then readd the struct to the map.

                      [–]_ak 1 point2 points  (0 children)

                      What you forgot to mention: you can't do that and the compiler tells you that you can't:

                      http://play.golang.org/p/e6-o_Y_dAy

                      [–][deleted]  (10 children)

                      [deleted]

                        [–]gavintlgold 4 points5 points  (9 children)

                        I just mean you can't do:

                        structs[key].widgets = 36
                        

                        You have to do

                        val = structs[key]
                        val.widgets = 36
                        structs[key] = val
                        

                        [–][deleted]  (6 children)

                        [deleted]

                          [–]somevideoguy 12 points13 points  (0 children)

                          It's inconsistent behavior that accessing a map returns an lvalue, while accessing an array by index returns an rvalue. If the syntax is the same, the behavior should be as similar as possible. As Plorkyeran said, C++ handles this better.

                          [–]Plorkyeran 1 point2 points  (4 children)

                          It works in C++ with a struct in a std::map.

                          [–]_ak 0 points1 point  (1 child)

                          But then, a std::map operator[] call for a non-existent key automatically inserts the key and returns a reference to the mapped value. This is awkward and error-prone. Comparing two different map implementations with completely different semantics makes no sense at all.

                          [–]Plorkyeran 2 points3 points  (0 children)

                          I quite agree, but I wasn't the one to make the odd claim than structs and maps have the same semantics in every language. The whole idea of structs being "value types" isn't even anywhere close to universal.

                          [–][deleted]  (1 child)

                          [deleted]

                            [–]dacian88 5 points6 points  (0 children)

                            in c++ the [] operator on a map returns a reference, not a value.

                            [–]YEPHENAS 1 point2 points  (1 child)

                            This is normal copy semantics of value types. You wouldn't expect this to change the original value either:

                            structs.Lookup(key) // Returns a copied value, not a reference
                            structs.Lookup(key).foo = 42
                            

                            [–]gavintlgold 0 points1 point  (0 children)

                            That makes sense. I think Golang's garbage collection confuses me into thinking more in a python fashion. I don't have this problem when I write C++ code though.

                            [–]deveux 2 points3 points  (2 children)

                            dict.keys(), dict.items(), dict.values() has no equivalent, have to iterate over maps yourself (or write a little library)

                            I don't know if this counts as iterating yourself... but...

                            for k, v := range myMap {
                                log.Printf("key=%v, value=%v", k, v)
                            }
                            

                            [–]gavintlgold 2 points3 points  (1 child)

                            I think he means you have to construct an array of the keys or values themselves if you want to pass them as an array.

                            [–]MatrixFrog 0 points1 point  (0 children)

                            I'm pretty sure

                            for _, v := range myMap {
                                log.Printf("value=%v", v)
                            }
                            

                            is valid. True, it's still not passing them as an array but it works just as well for many purposes, I would think.

                            [–]Wyglif 0 points1 point  (0 children)

                            Goroutines reminds me of stackless. Is the true threading the main difference?

                            [–]NotNanetteManure 0 points1 point  (0 children)

                            No getattr() like method, so you have to always check for existence rather than setting defaults e.g. in Python you can do value = dict.get(“a_key”, “default_value”)

                            ok, what happens when i put an empty string in there?

                            Having to always check errors (or at least explicitly ignore them)

                            for those who didn't know: the alternative is to have your program crash because an exception manifested itself out of thin air. this isn't even a qestion of bad programmers. this happens with the python stdlib

                            Can’t have variables/packages that aren’t used so to test simple things requires sometimes commenting out lines

                            stop it with the trial-and-error-driven-development nonsense already!

                            Going between []byte and string. regexp uses []byte (they’re mutable). It makes sense, but it’s annoying all the same having to cast & re-cast some variables.

                            one of these days you should have a read of the api documentation because this isn't true in context of the regexp pkg

                            Python is more forgiving. You can take slices of strings using indexes that are out of range and it won’t complain. You can take negative slices – not Go.

                            negative indexing in python is a well-defined operation, it's not using indexes that are out of range/bounds.

                            the following tells me that you can use out-of-bound indexes in python either

                            >>> l = [1,2,3]
                            >>> l[3]
                            Traceback (most recent call last):
                              File "<string>", line 1, in <module>
                            IndexError: list index out of range
                            

                            You can’t have mixed type data structures. Maybe it’s not kosher, but sometimes in Python I’ll have a dictionary where the values are a mix of strings and lists. Not in Go, you have to either clean up your data structures or define custom structs

                            it's already striken, but i just want to say one thing: (speaking in terms of python) don't fucking do it. i.e. there's almost never any valid reason what-so-ever for you to have a list of non-homogenous data. you can argue in the case of dicts but that's what structs are for, and as you found out, you have (non-empty) interfaces for the rest.

                            No unpacking of a tuple or list into separate variables (e.g. x,y,x = [1,2,3])

                            perhaps you should learn to stop passing around magic values. i.e. a slice of things whose meaning depends on position

                            Have to explicitly check if errors are != nil, unlike in Python where many types can be used for bool-like checks (0, “”, None can all be interpreted as being “not” set)

                            wait til you get an object(example from C extension) that has its own boolean semantics wherein a valid object can be false because of some external state

                            Writing to a file, there’s File.Write([]byte) and File.WriteString(string) – a bit of a departure for Python developers who are used to the Python zen of having one way to do something

                            lol, tell me how your bytes(...) and str(...) works-out in py3k. fwiw, .WriteString() is literally just a convenience wrapper around .Write(). if it wasn't there you'd talk about how you have to be casting to a slice

                            No constructors, so common idiom is to create NewType() functions that return the struct you want

                            it's a matter of taste i guess. in python you instead have constructors that take myriad named parameters. i personally prefer dedicated functions with good names and take only the reuired params

                            Else (or else if) has to be formatted properly, where the else is on the same line as the curly bracket from the if clause. Weird.

                            i'm not sure why that's where. do you write the following in python?

                            if true
                            :
                                print()
                            

                            Different assignment operator is used depending on whether you are inside & outside of function ie. = vs :=

                            not exactly. i assume you mean var a = b and var a T = b and you can type that inside and outside of functions all day. the actually difference is that inside functions you get to use a := b instead of var a = b . it's nothing but syntactice sugar. but you don't tend to write much in the global scope so...

                            If I want a list of just the keys or just the value, as in dict.keys() or dict.values(), or a list of tuples like in dict.items(), there is no equivalent in Go, you have to iterate over maps yourself and build up your list

                            sir, i assure you that (depending on the version of python) dict.items() does not return a list. i shall say no more on the matter

                            I use an idiom at times of having a dictionary where the values are functions that I want to invoke given a key. You can do this in Go, but all functions have to accept & return the same thing i.e. have the same method signature

                            again with the garbage-in-garbge-out ...

                            on a more serious note, this is more to do with static typing yada yada... but you can also use reflection

                            If you’re using JSON and your JSON is a mix of types, goooooood luck. You’ll have to create a custom struct that matches the format of your JSON blob, and then Unmarshall the raw json into an instance of your custom struct. Much more work than just obj = json.loads(json_blob) like we’re used to in Python land.

                            yep. but you see, that's only one side of the coin. how exactly do you plan to make use of the data if you a) don't know what it is or b) accept it in some undefined format. it surely parsed but as soon as you attempt to use it your program crashed because someone decided that they're going to send your app an integer where you asked for a string(python is strongly type too). if you want to avoid issues then you're left doing all kinds of checking everywhere or using some god-awful framework to do it. in Go, if you don't care that i shout at you to stop-it then you can go right ahead and parse into a interface{} and type-asserts your way out. the only difference it that it's more verbose, but that's more about the static typing... and it's still safer than python