Yet another Go thread


  • Discourse touched me in a no-no place

    @morbiuswilters said:

    @Ben L. said:
    Oh, please do tell me what features of the Linux kernel (other than syscalls) you have used in your programs.

    System calls are the primary services the kernel provides (albeit no the only ones). What I was referring to, obviously, was using some of the modern system calls that were invented in the last 2 decades. But, hey, thank God we have a "systems language" that is decades behind C.
    The pair of you are missing the point that system calls are the main interface between the kernel and user code. Some of the system calls are standardized by POSIX, others are not. (There are other parts to the interface, but when you get right down to it, operations on devices and files in /proc are still done via syscalls.) That's all well and good.

    The problem is that libc includes the standard wrapper functions for the system calls, and it is always supposed to be dynamically linked so as to enable the correct binding between the official API (which is defined in terms of C function calls) and the system calls used to implement it (where there is at least some freedom to change things). Static linking of libc defeats this, defeats efficient memory management (the OS usually manages to map one copy of libc into all processes in memory), and generally makes the whole system more sclerotic for no good reason as there will be a dynamic-linkable libc on the target platform if it is Linux.

    Other platforms might be different, but I doubt it. Nobody's done things like that since the days of Ultrix.

    What's worse is that it takes effort to make this mistake. Someone who really should have known better did this WTF on purpose.



  • @Bulb said:

    I would actually call channels a serious WTF. It really goes together with the lack of generics. Things like channels, or associative collections, the other thing go builds in, are sufficiently complex that they may need to be evolved, or special versions may be needed in some environment or something. If they were done in a library it would be much better, but it would require generics. I am all for special syntax for them, but think the implementation really shouldn't be tied to the language.
    That's actually one of the points where I can understand the reasoning behind Go.

    At least in Java, maps are (ab)used for all sorts of things that have little to do with their actual purpose - lists of pairs, poor-man "structs" when you don't want to define a new class for one special case deeply buried inside a function, extensible "properties", attaching private data to an object, etc... In many cases, it's well-known at compile time which keys could ever possibly end up in the map and how often (if at all) the map could change. But because maps are solely a concept of the standard library, the compiler can't do much about it and you always end up with the same bloaty default implementation.

    Compare that with JavaScript where maps are a core part of the language itself (complete with specialized syntax and everything). V8 can use that to make some heavy optimizations depending on what you do with the map (e.g. if your map is really a struct, it will actually implement it as a struct, etc...) If that's what Go was aiming for, that would be one of the few good ideas.



  • @joe.edwards said:

    Let's prepend 4 bytes of a very predictable value. It can't reduce the entropy, after all.
     

    And how do you propose that prepending a known value reduces the total entropy?



  • I totally forgot about these. http://golang.org/pkg/runtime/debug/#PrintStack http://golang.org/pkg/runtime/#Stack

    So how does this help my top-level catch-all error handler+logger? Please read the posts you're responding to.

    Okay, then when you found something that needs to be reported (i.e. erronneous state), ask for a stacktrace from the runtime using the function I linked. Then you have a byte array with a formatted stacktrace in it. Return it as an error, or call panic(bufWithStackTraceInIt). Eventually this will get to the top level logger/handler, and you have a stacktrace, which you can log. To demonstrate:

    Tell me if I'm still not answering your question properly.

    if you want to ignore some returned values, use an underscore:

    <p>a, _ = funcReturns2Things()  </p>
    

    You are literally not reading the posts you're responding to.

    (yes, I've read and understand your code examples, I'm just not going to quote them, if that's okay)

    Let me spell it out: Go is not supposed to force you to check for errors. Instead go is supposed to force you to receive the correct number (and type of) return values (or you can ignore all of them at once, or use underscores yadda yadda). The fact that a side-effect of this is that some of the time you are also forced not to ignore errors is just that: a side-effect. It wasn't intended to force you to check for errors, because that should be a part of how you code.

    You are encouraged to check your errors by the tutorials and how the language is designed, but you aren't forced. I don't know why was this decided as such. I am aware that the side-effect of forcing you to receive the error isn't there when there's only one return value (of type error), because you can simply ignore it by not recving any of the returned values.

    You state that go fails at forcing you to check error values. What you should be saying is that the go designers decided not to force you to check error values. And you are certainly entitled to the opinion to consider that decision a failure. Did I interpret your issue correctly?

    The zeroes are the invalid state. And having to strengthen all the methods on Foo to handle the case when Foo is uninitialized (even assuming that such a state is detectable) is pure retardation. I can't even believe you're suggesting that.

    Every variable is initialized to a zero value at declaration. In other words, it's not possible for variables to be filled with garbage (what originally sat in memory on that address).


    Therefore, if a Foo isn't initialized with NewFoo (and it's not set to any other value, say, because Foo has no exported fields, yadda yadda), then the only possible value it can take is Foo{} (this is how the zero value looks like for an arbitrary struct). Example:

    Zero values are detectable. Uninitialized variables (filled with memory garbage) aren't possible to happen at all. I don't see what's retarded about checking for a zero value. Does this help with your issue?

    Yes, God knows NewFoo(databaseRow); NewFoo(file); is too complicated. Obviously I want NewFoo(NewDatabaseRowReader(databaseRow)); NewFoo(NewFileReader(file)); Yep, much cleaner now.

    If the databaseRow and the File already has a Read method, then they can be passed as an io.Reader without hassle. You don't have to call those extra functions, and you can call NewFoo(databaseRow); NewFoo(file); if NewFoo takes an interface that both File and DatabaseRow implement.

    That is, assuming somehow that databaseRow.GetColumn("ID") and file.ReadLine().Substring("ID: ".Length) can be extracted into a reader interface in the first place.

    1. io.Reader was just an example; design an interface that suits them both.
    2. if that's not possible, then pass an empty interface (interface{}) into NewFoo(), then use type assertion to check whether it's a File or a DatabaseRow. Yes, I know it's basically runtime type checking, but I doubt that you can't abstract away the differences between File and DatabaseRow in order to create Foo. If they are so disparate, then how would you write the two constructors for Foo? How would the ability of having function overloading (and therefore same-named constructors) help with handling types with so much difference that you can't abstract the differences away?

    Go doesn't have inheritance.

    That's exactly what's wrong with it. Its entire class of problems and kludges/language features to work around said problems would be absent if it did.

    Okay.

    Your link has no relevance to why pointers are allowed to implement interfaces. The code in that link dereferences the pointer the first chance it gets.

    Pointers are allowed to implement interfaces because of the same reason you might want to pass a pointer to a function. A common reason is that you might want to modify the receiver. If you only pass a value to the method, then the caller won't see the modifications you did inside the method, just like with functions. Another reason: you have a big struct with lots of fields, and you don't want to have it copied all the time you call a method. And anyways, what else can do you want to do with a pointer than to dereference it?

    A code example, to demonstrate why pointer receivers are necessary when you need to modify the receiver:

    If what you meant to ask was "why do pointer receivers and value receivers have different method sets?" Then look at this, especially Rob's last reply:



  • @paperwing said:

    Every variable is initialized to a zero value at declaration. In other words, it's not possible for variables to be filled with garbage (what originally sat in memory on that address)

    Zero values are detectable. Uninitialized variables (filled with memory garbage) aren't possible to happen at all. I don't see what's retarded about checking for a zero value. Does this help with your issue?

    The zeroes are the garbage.



  • I also forgot about this possibility:

    test/foo/foo.go:

    package foo
    
    import "fmt"
    
    // Unexported type
    type foo struct {
        a, b int
    }
    
    // But the function that creates it is exported.
    func NewFoo() foo {
        return foo{1, 2}
    }
    
    // Method is also exported.
    func (f foo) Method() {
        fmt.Printf("Method called: %v\n", f)
    }
    

    test/main.go:

    package main
    
    import (
        "test/foo"
        "fmt"
    )
    
    func main() {
        f := foo.NewFoo()
        // g := new(foo.foo) // compile error
        // g := foo.foo{3, 4} // compile error
        f.Method()
        fmt.Println(f)
    }
    

    This way, you can force the usage of a "constructor".



  • And by not exporting the type, you can't enforce type safety when you pass it around to other functions.
    Well done.



  • @paperwing said:

    Your link has no relevance to why pointers are allowed to implement interfaces. The code in that link dereferences the pointer the first chance it gets.

    Pointers are allowed to implement interfaces because of the same reason you might want to pass a pointer to a function. A common reason is that you might want to modify the receiver. If you only pass a value to the method, then the caller won't see the modifications you did inside the method, just like with functions. Another reason: you have a big struct with lots of fields, and you don't want to have it copied all the time you call a method. And anyways, what else can do you want to do with a pointer than to dereference it?

    A code example, to demonstrate why pointer receivers are necessary when you need to modify the receiver:

    If what you meant to ask was "why do pointer receivers and value receivers have different method sets?" Then look at this, especially Rob's last reply:

    no, he meant to ask "why do pointers implement interfaces?" If you're saying that a pointer has to implement an interface so that you can change data on the object, that's yet another WTF


  • @MiffTheFox said:

    The main reason binaries aren't portable across distros is that one decides to use /lib, one uses /usr/lib, one uses /var/lib, one uses /usr/share/lib, etc.

    Any moron who decides to put a shared library (*.so) in /usr/share/lib or /var/lib should be buried under a million copies of the FHS.



  • @anonymous_guy said:

    Any moron who decides to put a shared library (*.so) in /usr/share/lib or /var/lib should be buried under a million copies of the FHS.

    Well, it's /usr/share/lib after all...


  • Considered Harmful

    @Faxmachinen said:

    @joe.edwards said:

    Let's prepend 4 bytes of a very predictable value. It can't reduce the entropy, after all.
     

    And how do you propose that prepending a known value reduces the total entropy?

    OK, step up and play Joe's Super Lottery. Match all 32 numbers and you win a photocopy of a cookie. Just 50 cents to play!

    Pretty unlikely you'll win. If I reveal to you that the first 4 numbers are the Unix epoch of the drawing, do your odds go up or down?


    Edit: Bah, just saw Morbs' reply. Still, the point stands.



  • @Sutherlands said:

    If you're saying that a pointer has to implement an interface so that you can change data on the object, that's yet another WTF

    Could you rephrase that statement? I read it right now as "why can't I modify this value that was passed by value?"



  • @paperwing said:

    Every variable is initialized to a zero value at declaration. In other words, it's not possible for variables to be filled with garbage (what originally sat in memory on that address).



    Therefore, if a Foo isn't initialized with NewFoo (and it's not set to any other value, say, because Foo has no exported fields, yadda yadda), then the only possible value it can take is Foo{} (this is how the zero value looks like for an arbitrary struct). Example:

    But what if you WANT to send a missile to latitude 0.0, longitude 0.0? How do you tell the difference between that missile's perfectly legit destination and a missile that hasn't been properly constructed?



  • @Ben L. said:

    @Sutherlands said:
    If you're saying that a pointer has to implement an interface so that you can change data on the object, that's yet another WTF

    Could you rephrase that statement? I read it right now as "why can't I modify this value that was passed by value?"

    Wow.  You're really trying to take the cake for "worst programmer on this board" aren't you?  Tell me how C++ or C# manages to modify values passed to a function without allowing/making pointers implement interfaces.


  • Considered Harmful

    @blakeyrat said:

    @paperwing said:
    Every variable is initialized to a zero value at declaration. In other words, it's not possible for variables to be filled with garbage (what originally sat in memory on that address).



    Therefore, if a Foo isn't initialized with NewFoo (and it's not set to any other value, say, because Foo has no exported fields, yadda yadda), then the only possible value it can take is Foo{} (this is how the zero value looks like for an arbitrary struct). Example:

    But what if you WANT to send a missile to latitude 0.0, longitude 0.0? How do you tell the difference between that missile's perfectly legit destination and a missile that hasn't been properly constructed?


    Obviously you have a non-exported variable initialized and set it to 1 in your constr... Er. Function.



  • @joe.edwards said:

    @blakeyrat said:
    @paperwing said:
    Every variable is initialized to a zero value at declaration. In other words, it's not possible for variables to be filled with garbage (what originally sat in memory on that address).



    Therefore, if a Foo isn't initialized with NewFoo (and it's not set to any other value, say, because Foo has no exported fields, yadda yadda), then the only possible value it can take is Foo{} (this is how the zero value looks like for an arbitrary struct). Example:

    But what if you WANT to send a missile to latitude 0.0, longitude 0.0? How do you tell the difference between that missile's perfectly legit destination and a missile that hasn't been properly constructed?


    Obviously you have a non-exported variable initialized and set it to 1 in your constr... Er. Function.



  • @joe.edwards said:

    Obviously you have a non-exported variable initialized and set it to 1 in your constr... Er. Function.

    I was thinking you create a bool, "wasInitialized", make all member variables private (lowercase, what-the-fuck-ever), and then have every goddamned member check whether the bool is set to true or not before doing anything. The "fake constructor" is the only function that sets it to true.

    But... yeah. This is a problem that doesn't exist in any other OO language I am aware of. Even fucking JavaScript.


  • Considered Harmful

    @blakeyrat said:

    @joe.edwards said:
    Obviously you have a non-exported variable initialized and set it to 1 in your constr... Er. Function.

    I was thinking you create a bool, "wasInitialized", make all member variables private (lowercase, what-the-fuck-ever), and then have every goddamned member check whether the bool is set to true or not before doing anything. The "fake constructor" is the only function that sets it to true.

    But... yeah. This is a problem that doesn't exist in any other OO language I am aware of. Even fucking JavaScript.

    In C#, you can't touch the nullary constructor of a struct ("struct cannot contain explicit parameterless constructor"), though you can add other constructors (that must call the default constructor). It's rare, though, in C# when you need to create a struct instead of a class.



  • @Ronald said:

    Anyways this code is so buggy, it won't even run in qbasic.exe.
    Have you tried QB64?



  • But what if you WANT to send a missile to latitude 0.0, longitude 0.0? How do you tell the difference between that missile's perfectly legit destination and a missile that hasn't been properly constructed?

    Then the zero value isn't an invalid value, i.e. it's immediately usable. Therefore you don't need explicit initialization. This is similar to how the sync.Mutex type's zero value represents an unlocked mutex.



  • @blakeyrat said:

    @joe.edwards said:
    Obviously you have a non-exported variable initialized and set it to 1 in your constr... Er. Function.

    I was thinking you create a bool, "wasInitialized"

    You know how I know paperwing isn't reading my posts? Because I already anticipate everything he says and answer it but he still says it anyway.

    @Arnavion said:

    The zeroes are the invalid state. And [b]having to strengthen all the methods on Foo to handle the case when Foo is uninitialized[/b] (even assuming that such a state is detectable) is pure retardation.



  • @Arnavion said:

    @blakeyrat said:
    @joe.edwards said:
    Obviously you have a non-exported variable initialized and set it to 1 in your constr... Er. Function.

    I was thinking you create a bool, "wasInitialized"

    You know how I know paperwing isn't reading my posts? Because I already anticipate everything he says and answer it but he still says it anyway.

    @Arnavion said:

    The zeroes are the invalid state. And having to strengthen all the methods on Foo to handle the case when Foo is uninitialized (even assuming that such a state is detectable) is pure retardation.



  • @paperwing said:

    Okay, then when you found something that needs to be reported (i.e. erronneous state), ask for a stacktrace from the runtime using the function I linked.

    Brilliant, let's put "Get the stack and append it to the message of this error." code everywhere. Of course, this doesn't work for errors that don't have a way to set their message (aka all of them), so I also have to make a wrapper error class with one. And I'll probably want to extract this into a method to avoid putting the same 3 lines in a million places, so

    • Every stack trace is going to have "AppendStackTraceToError()" at the top of the stack trace
    • Every place that checks for the error type is going to have to be changed to check error.InnerException instead.
    Brilliant, I say!


  • Sutherlands, I was agreeing with you and blakey...



  • @paperwing said:

    This way, you can force the usage of a "constructor".

    So your solution is to make all the types private? How do you propose I do type-checking on them, then? And before you say it (but you will, anyway, since you don't read the posts you're responding to), I really want to check the type. Interfaces aren't always enough.

    @paperwing said:

    If they are so disparate, then how would you write the two constructors for Foo? How would the ability of having function overloading (and therefore same-named constructors) help with handling types with so much difference that you can't abstract the differences away?

    Pseudocode: http://privatepaste.com/2120adeeda

    Link because CS is being a bitch about newlines.



  • @Arnavion said:

    @paperwing said:
    This way, you can force the usage of a "constructor".

    So your solution is to make all the types private? How do you propose I do type-checking on them, then? And before you say it (but you will, anyway, since you don't read the posts you're responding to), I really want to check the type. Interfaces aren't always enough.

    So your argument is that people who intentionally use your software incorrectly get incorrect results?

    This sure is a productive thread.



  • @Sutherlands said:

    @Ben L. said:
    @Sutherlands said:
    If you're saying that a pointer has to implement an interface so that you can change data on the object, that's yet another WTF

    Could you rephrase that statement? I read it right now as "why can't I modify this value that was passed by value?"

    Wow.  You're really trying to take the cake for "worst programmer on this board" aren't you?  Tell me how C++ or C# manages to modify values passed to a function without allowing/making pointers implement interfaces.

    If you copy a value, which is what passing by value does, you're still able to modify it, but it DOESN'T MODIFY THE ORIGINAL (pre-copy) VALUE.

    Are you saying that I should make a language where I can change the value of an int passed to a function and suddenly everywhere that used that int now has the new value?



  • @paperwing said:

    Then the zero value isn't an invalid value, i.e. it's immediately usable. Therefore you don't need explicit initialization. This is similar to how the sync.Mutex type's zero value represents an unlocked mutex.

    But how do you tell the difference between an improperly constructed missile with zero values and a properly constructed missile with zero values? And before you give an example of how it could be done, stop. Stop giving workarounds to a problem that shouldn't exist in the first place.

    In an language that has objects, it is valuable to know whether or not a given object has been properly created or not, that is, the object is in a state that the designer intended it to be in before being used. You can enforce this with the compiler like other sensible languages do, or you can be like Go. All your suggested workarounds have their own drawbacks, and the one that comes the closest - the suggestion of an 'initialized' boolean - is still dumb because obviously it's good design to make sure your objects are being used correctly so obviously you're going to do the 'initialized' variable things but if every single designer is doing the 'initialized' variable thing all the time why not just save everyone hours of work and enforce the convention with the fucking compiler?

    @Sutherlands said:

    @paperwing said:

    A code example, to demonstrate why pointer receivers are necessary when you need to modify the receiver:

    If what you meant to ask was "why do pointer receivers and value receivers have different method sets?" Then look at this, especially Rob's last reply:

    no, he meant to ask "why do pointers implement interfaces?" If you're saying that a pointer has to implement an interface so that you can change data on the object, that's yet another WTF

    I removed the interface declaration from that code example and it ran with the same exact output, so not sure what the point of the interface is.

    I've been trying to figure this out. Maybe 'interface' in Go simply means 'a public method'? In C++ and C# if you want to modify an object's private values in a function, then you pass the object with a pointer and call a public method on it. So is the confusion arising because Go programmers need paragraphs to explain extremely simple concepts? From the Go docs it seems like interfaces are implemented implicitly, but what effect does that have on the implement-er? Do pointers to an object automatically implement the same interfaces that the object does? If an object implements an interface in a given package, can that object now act as a 'receiver' (not one Go enthusiast has bothered to define this term, I'm gonna assume it means 'the object you call the method on') for all the other functions in the package? Fucking hell why does it delete two characters when I press backspace once? So many questions...



  • Personally, I think this discussion needs more Pass-Reference-By-Reference-versus-Pass-Reference-By-Value rage.
    Nothing gets C# developers frothing at the mouth more, as far as I can tell.



  • @Arnavion said:


    NewFoo(databaseRow DatabaseRow) {
    id = databaseRow.GetInt("ID")
    name = databaseRow.GetString("Name")
    bar = databaseRow.GetString("Bar")
    }

    NewFoo(file FileReader) {
    for each line in file {
    if line.BeginsWith("ID: ") {
    id = Integer.parse(line.Substring("ID: ".Length))
    }
    else if line.BeginsWith("Name: ") {
    name = line.Substring("Name: ".Length)
    }
    else if line.BeginsWith("Bar: ") {
    bar = line.Substring("Bar: .Length)
    }
    }
    }

    So you can parse both a database row and a file in 19 lines. With no error checking. I can do the same thing with encoding/json or database/sql in Go with no parsing code written for the specific type. I'll even throw in a non-pointer interface method, free of charge!



  • @Ben L. said:

    @Arnavion said:
    @paperwing said:
    This way, you can force the usage of a "constructor".

    So your solution is to make all the types private? How do you propose I do type-checking on them, then? And before you say it (but you will, anyway, since you don't read the posts you're responding to), I really want to check the type. Interfaces aren't always enough.

    So your argument is that people who intentionally use your software incorrectly get incorrect results?

    This sure is a productive thread.

    It shouldn't be possible to use it incorrectly in the first place!

    Can you provide an example where it would be a useful, good thing if someone could create and use invalid states of an object of a class you designed?



  • @lettucemode said:

    @paperwing said:

    Then the zero value isn't an invalid value, i.e. it's immediately usable. Therefore you don't need explicit initialization. This is similar to how the sync.Mutex type's zero value represents an unlocked mutex.

    But how do you tell the difference between an improperly constructed missile with zero values and a properly constructed missile with zero values? And before you give an example of how it could be done, stop. Stop giving workarounds to a problem that shouldn't exist in the first place.

    Simple: You use a *MissileLaunchDestination. nil means don't fire ze missiles.



  • @lettucemode said:

    Do pointers to an object automatically implement the same interfaces that the object does?

    No, but Go has a syntactical shorthand to allow you to write foo.Method when it's in fact (*foo) that has Method, which is why it [b]looks[/b] like pointers support the same methods as what they're pointing to.

    @lettucemode said:

    (not one Go enthusiast has bothered to define this term, I'm gonna assume it means 'the object you call the method on')

    Yes, I imagine that's what it means. Maybe they're trying to sound like SmallTalk about method calls being implemented as passing messages to objects, except Go doesn't allow "messages" that can't be handled so thinking of it like that is pointless.



  • @Ben L. said:

    @lettucemode said:
    @paperwing said:

    Then the zero value isn't an invalid value, i.e. it's immediately usable. Therefore you don't need explicit initialization. This is similar to how the sync.Mutex type's zero value represents an unlocked mutex.

    But how do you tell the difference between an improperly constructed missile with zero values and a properly constructed missile with zero values? And before you give an example of how it could be done, stop. Stop giving workarounds to a problem that shouldn't exist in the first place.

    Simple: You use a *MissileLaunchDestination. nil means don't fire ze missiles.

    So now all my methods need to check that the object they're called on isn't nil before doing anything. Again, creating extra work for a problem that shouldn't exist.



  • @lettucemode said:

    It shouldn't be possible to use it incorrectly in the first place!

    It shouldn't be possible for me to fire this gun at a human! I want a refund! This is obviously a faulty product.



  • @Ben L. said:

    I'm going to ignore the problem you have and solve an unrelated problem because my problem's solution is in-built into the standard library.

    Excellent reasoning.



  • @lettucemode said:

    So now all my methods need to check that the object they're called on isn't nil before doing anything. Again, creating extra work for a problem that shouldn't exist...

    No you don't. Dereferencing a nil pointer will crash your program with a nice stack trace.



  • @Arnavion said:

    I'm going to interpret your exact solution to my problem as something completely unrelated and complain on an online forum.

    Excellent reasoning.



  • @Ben L. said:

    No you don't. Dereferencing a nil pointer will crash your program with a nice stack trace.

    What's this obsession with crashing programs on what are recoverable errors in every other language, I wonder... Both you and paperwing have it.



  • @Arnavion said:

    @Ben L. said:
    No you don't. Dereferencing a nil pointer will crash your program with a nice stack trace.

    What's this obsession with crashing programs on what are recoverable errors in every other language, I wonder... Both you and paperwing have it.

    You're saying it's good that your program passes invalid data around? My, aren't you a wonderful programmer.



  • @Ben L. said:

    It shouldn't be possible for me to fire this gun at a human! I want a refund! This is obviously a faulty product.

    Okay, so then it should be possible to have a correct way to use the tool, right? So answer my question above: what's an example of a good reason you would want someone to use your class design incorrectly?

    @Ben L. said:

    No you don't. Dereferencing a nil pointer will crash your program with a nice stack trace.

    What if I don't want my program to crash because it dereferences nil pointers? I have to check that the pointer's not nil before I use it. Why am I checking that the pointer's not nil before I use it? Because I don't know whether nor not it's nil before I use it. And why don't I know whether or not the pointer's nil? Because Go doesn't have properly enforced constructors.



  • @Ben L. said:

    You're saying it's good that your program passes invalid data around? My, aren't you a wonderful programmer.

    Oh, so you're just trolling then. That makes it easier.



  • @lettucemode said:

    @Ben L. said:
    It shouldn't be possible for me to fire this gun at a human! I want a refund! This is obviously a faulty product.

    Okay, so then it should be possible to have a correct way to use the tool, right? So answer my question above: what's an example of a good reason you would want someone to use your class design incorrectly?

    What's a good reason I should answer your non-sequitors? Because the bridge fell down on the bacon truck.

    @lettucemode said:

    @Ben L. said:
    No you don't. Dereferencing a nil pointer will crash your program with a nice stack trace.

    What if I don't want my program to crash because it dereferences nil pointers? I have to check that the pointer's not nil before I use it. Why am I checking that the pointer's not nil before I use it? Because I don't know whether nor not it's nil before I use it. And why don't I know whether or not the pointer's nil? Because Go doesn't have properly enforced constructors.

    Every language ever invented that has pointers and/or classes would be a "bad language" by this standard.



  • Just to recap:

    • Go is a bad language because it allows you to use programs incorrectly
    • C# and C++ have no legal programs that can be used incorrectly
    • I walk on the ceiling and open doors diagonally.

  • ♿ (Parody)

    @lettucemode said:

    @Ben L. said:
    No you don't. Dereferencing a nil pointer will crash your program with a nice stack trace.

    What if I don't want my program to crash because it dereferences nil pointers? I have to check that the pointer's not nil before I use it. Why am I checking that the pointer's not nil before I use it? Because I don't know whether nor not it's nil before I use it. And why don't I know whether or not the pointer's nil? Because Go doesn't have properly enforced constructors.

    What the hell? I don't have a brief for Go, but this just sounds like you don't understand pointers. In any language.



  • @Ben L. said:

    @lettucemode said:
    @Ben L. said:
    It shouldn't be possible for me to fire this gun at a human! I want a refund! This is obviously a faulty product.

    Okay, so then it should be possible to have a correct way to use the tool, right? So answer my question above: what's an example of a good reason you would want someone to use your class design incorrectly?

    What's a good reason I should answer your non-sequitors? Because the bridge fell down on the bacon truck.

    Okay, I'll explain my thinking more clearly.

    Tools can be misused. However, we keep them around because they have positive uses, too. A hammer can be used to bash someone's skull in, however you can also use it to build things. A gun can be used to create harm, or it can be used to protect against harm. Code injection can be used to create viruses, however you can also use it to create some pretty neat stuff. So tools are useful because they have positive uses despite their negative ones. Given your example of the misuse of a gun, I assumed you knew this. Note to self.

    So tell me, what are the positive uses of the Go object/class creation and design that are not shared by enforced constructors? And if there aren't any, then why not just enforce them and prevent an entire class of errors from happening?

    @Ben L. said:

    @lettucemode said:
    @Ben L. said:
    No you don't. Dereferencing a nil pointer will crash your program with a nice stack trace.

    What if I don't want my program to crash because it dereferences nil pointers? I have to check that the pointer's not nil before I use it. Why am I checking that the pointer's not nil before I use it? Because I don't know whether nor not it's nil before I use it. And why don't I know whether or not the pointer's nil? Because Go doesn't have properly enforced constructors.

    Every language ever invented that has pointers and/or classes would be a "bad language" by this standard.

    Obviously if I have a class Foo with method MakeFooFromBar(Bar bar), I would check the incoming pointer for nil before using it. But if I have a class Foo with private variable bar and method IncrementBar() { ++bar; }, it would be supremely retarded if I had to preface the increment instruction with if (!this) return; That's the difference.



  • Boomzilla, see my second paragraph of last post. I guess I should have been more clear.



  • All this talk about using MissileLaunchDestination* and checking it for nil is pointless anyway, since new(MissileLaunchDestination) can be used to give you a MissileLaunchDestination*, not a MissileLaunchDestination. And this points to an uninitialized MissileLaunchDestination instance.

    So whether your method accepts a MissileLaunchDestination or a MissileLaunchDestination*, it can still get an uninitialized MissileLaunchDestination in the end, even if the actual MissileLaunchDestination* that's passed in is not nil.



  • @Arnavion said:

    All this talk about using MissileLaunchDestination* and checking it for nil is pointless anyway, since new(MissileLaunchDestination) can be used to give you a MissileLaunchDestination*, not a MissileLaunchDestination. And this points to an uninitialized MissileLaunchDestination instance.

    So whether your method accepts a MissileLaunchDestination or a MissileLaunchDestination*, it can still get an uninitialized MissileLaunchDestination in the end, even if the actual MissileLaunchDestination* that's passed in is not nil.

    Why the fuck are you using new(T)? This isn't Java. Use the constructor function.


  • @dkf said:

    The pair of you are missing the point that system calls are the main interface between the kernel and user code.

    What? I don't think anybody missed this.

    @dkf said:

    Static linking of libc defeats this, defeats efficient memory management (the OS usually manages to map one copy of libc into all processes in memory), and generally makes the whole system more sclerotic for no good reason as there will be a dynamic-linkable libc on the target platform if it is Linux.

    It creates all sorts of other problems, too, which should be obvious. After all, static linking was abandoned on Unix 20 years ago. For example, when SYSENTER / SYSEXIT came out, you could take advantage of it just by installing a new kernel and libc--no reason to re-compile every single application on your machine.

    Then there are the security implications. Let's say somebody discovers a vulnerability deep in the bowels of libc. With dynamic linking, so long as the ABI doesn't change the library can be patched and updates pushed out. There's no need to patch a single application on the machines, just libc itself. Hundreds of thousands of existing applications instantly benefit from the patch. It allows for decoupling of binary dependencies, which is a major goal of software engineering.

    With Go, if a bug were found in the core libraries, you'd have to recompile every single application on your machine. Thank God nobody actually uses Go, or else they'd be setting themselves up for a major butt-fucking there.

    Then there's the fact that they didn't just statically link libc, against all logic and reason they re-implemented it. So not only is it a nightmare to patch, but instead of using mature libraries which have gone through decades of bug fixes and enhancements, you now have to deal with the fact that the core Go libs are less mature and reliable than libc. Instead of one place where a particular function is implemented (and then linked into every C application, including other languages like Java, PHP, Perl, Python..) you now have two.

    This is why Go is a toy language. Anybody who looks at these major flaws and says "Oh, that's okay" clearly doesn't know the first fucking thing about software engineering or maintenance and support of code.


Log in to reply