"break"? What is this you speak of?



  • @joe.edwards said:

    Objects have constructors (actually any function can be a constructor, which smells funny but isn't so bad), and you can assign initial values to your instance properties there. This should solve both your "same underlying object" problem and your "dummy websocket" problem.


    function MyFoo() {}

    function MyBar() {
    this.baz = new Baz(); // each instance gets its own freshly constructed Baz (defined elsewhere)
    }

    MyBar.prototype = new MyFoo();

    var myBar = new MyBar();

    myBar instanceof MyBar; // true
    myBar instanceof MyFoo; // true

    How to write a constructor is not the problem. But that approach only works as long as MyFoo has no internal state - or it's constructor is so trivial that you can easily initialize the state yourself, like you just did.

    But suppose MyFoo's constructor was like this:

    function MyFoo(a, b, c) {
        this.baz = new Baz(a, b, new Xyz(c));
    }

    ...which you can't change, because you have other places where you need standalone MyFoos which are no MyBars. What do you do?

  • Considered Harmful

    @PSWorx said:

    What do you do?

    Refactor. No, really.



  • @joe.edwards said:

    @PSWorx said:
    What do you do?

    Refactor. No, really.

    Ok, bad example :). But let me try again. Consider this:
    
     class A {
    

    int _x;

    public A(int x) {
    this._x = x;
    }

    public String sayHello() {
    return "Hi from A! (x=" + _x + ")";
    }

    }

    class B extends A {

    public B(x) {
    super(x);
    }

    public String sayHello() {
    return "Hi from B! (x=" + _x + ")";
    }

    }


    That's straight out of java-101, so no WTF architecture required. Now try to rewrite that as JS and you'll see what i mean.



  • @PSWorx said:

    Ok, bad example :). But let me try again. Consider this:

    
     class A {
    

    int _x;

    public A(int x) {
    this._x = x;
    }

    public String sayHello() {
    return "Hi from A! (x=" + _x + ")";
    }

    }

    class B extends A {

    public B(x) {
    super(x);
    }

    public String sayHello() {
    return "Hi from B! (x=" + _x + ")";
    }

    }


    That's straight out of java-101, so no WTF architecture required. Now try to rewrite that as JS and you'll see what i mean.

     

     Unless I've completely missed the point you're trying to make, this works as expected:

    function A(x){
    	this._x = x;
    }
    

    A.prototype.sayHello = function(){
    alert("Hi from A! x=" + this._x);
    }

    A.prototype.sayGoodbye = function(){
    alert("Goodbye: x=" + this._x);
    }

    function B(x){
    A.call(this, x);
    }

    B.prototype = new A();

    B.prototype.sayHello = function(){
    alert("Hi from B x=" + this._x);
    }

    function test(){
    var a = new A(5);
    var b = new B(10);

        //Both will report x=5
    a.sayHello();
    a.sayGoodbye();
    
        //Both will report x=10
    b.sayHello();
    b.sayGoodbye();
    

    }


  • Considered Harmful

    @PSWorx said:

    class A {

    int _x;

    public A(int x) {
    this._x = x;
    }

    public String sayHello() {
    return "Hi from A! (x=" + _x + ")";
    }

    }

    class B extends A {

    public B(x) {
    super(x);
    }

    public String sayHello() {
    return "Hi from B! (x=" + _x + ")";
    }

    }

    function A(x) {
        this._x = x;
    }
    
    A.prototype.sayHello = function() {
        return 'Hi from A! (x=' + this._x + ')';
    };
    
    function B(x) {
        A.call( this, x );
    }
    
    B.prototype = new A();
    
    B.prototype.sayHello = function() {
        return 'Hi from B! x=' + this._x +')';
    };
    
    var b = new B( 1 );
    

    I see your point, we do end up constructing an A before we ever construct a B. I think as a design principle you should avoid doing much in a constructor, and in JS prepare for the possibility that a constructor is called more than once (once for the prototype, once for the instance). This is fine as long as your constructor is just initializing your members; if it needs to construct other objects I suggest taking those as parameters (which makes dependency injection possible).

    Edit: Damn, he beat me to it.



  • Hmm, I've never actually thought about call()ing a constructor before - I guess that happens if you write too much classical OO. :)
    Thanks for the infos, guys and sorry for the derailment - you may now resume your regularly scheduled flamewar.



  • @PSWorx said:

    But suppose MyFoo's constructor was like this:

    function MyFoo(a, b, c) {
    this.baz = new Baz(a, b, new Xyz(c));
    }

    ...which you can't change, because you have other places where you need standalone MyFoos which are no MyBars. What do you do?

    You can do something like:

    <html><body style='color:#000000; background:#ffffff; '>
    var Baz = function(a, b, c) {
        this.a = a;
        this.b = b;
        this.c = c;
    };
    

    Baz.prototype = {
        woofwoof: function() {
    console.log(this.a + " " + this.b + " " + this.c);
    }
    };

    var Foo = function(a, b, c) {
    if (!(this instanceof Foo)) {
    return new Foo(a, b, c);
    }

    <span style='color:#800000; font-weight:bold; '>this</span><span style='color:#808030; '>.</span>baz <span style='color:#808030; '>=</span> new Baz<span style='color:#808030; '>(</span>a<span style='color:#808030; '>,</span> b <span style='color:#808030; '>,</span>c<span style='color:#808030; '>)</span><span style='color:#800080; '>;</span>
    

    };

    var Bar = function(a, b, c) {
    if (!(this instanceof Bar)) {
    Bar.prototype = new Foo(a, b, c);

        <span style='color:#800000; font-weight:bold; '>return</span> new Bar<span style='color:#808030; '>(</span><span style='color:#808030; '>)</span><span style='color:#800080; '>;</span>
    <span style='color:#800080; '>}</span>
    

    };

    var myFoo = Foo("dhromed", "loves", "dicks");
    console.log(myFoo);
    myFoo.baz.woofwoof();

    var myBar = Bar("so", "does", "Lorne");
    console.log(myBar);
    myBar.baz.woofwoof();

    myFoo.baz.woofwoof();

    var otherBar = Bar("morbs", "not", "gay");
    otherBar.baz.woofwoof();

    myBar.baz.woofwoof();

    What this does is instead of invoking the functions with the new keyword, you just call the function directly. Then the function will determine if it's being called directly or with new. If it's being called directly, it invokes itself with the new keyword, and returns that object. Tada!

    This gets around the limitation that you can't set the prototype on an existing object--the prototype chain is only set up when a function is invoked with new. So what this code does is in Bar() it sets the prototype of Bar to a new Foo (passing it the parameters you passed to Bar). Then it invokes Bar() with the new keyword, resulting in an object that has its prototype properly set to that new Foo we just created.

    Should you do this? Probably not. The performance is probably going to be iffy. And it looks like crap. And I'm not even sure there isn't some environment it won't blow up in, although it works for me ine node and Chrome.

    Really, just forget the word "constructor" even exists in JS. JS constructors are too dissimilar from Java constructors. If you want to do complex initialization of object properties like baz, do it in an initialization function, like so:

    <html><body style='color:#000000; background:#ffffff; '>
    var Baz = function(a, b, c) {
        this.a = a;
        this.b = b;
        this.c = c;
    };
    

    Baz.prototype = {
        woofwoof: function() {
    console.log(this.a + " " + this.b + " " + this.c);
    }
    };

    var Foo = function() {
    // i am now useless
    };

    Foo.prototype = {
        initFoo: function(a, b, c) {
    this.baz = new Baz(a, b, c);
    }
    };

    var Bar = function() {
    // i am also useless
    };

    Bar.prototype = new Foo();
    Bar.prototype.initBar = function(a, b, c) {
    this.initFoo(a, b, c);
    };

    var myFoo = new Foo();
    myFoo.initFoo("dhromed", "loves", "dicks");
    console.log(myFoo);
    myFoo.baz.woofwoof();

    var myBar = new Bar();
    myBar.initBar("so", "does", "Lorne");
    console.log(myBar);
    myBar.baz.woofwoof();

    myFoo.baz.woofwoof();

    var otherBar = new Bar();
    otherBar.initBar("morbs", "not", "gay");
    otherBar.baz.woofwoof();

    myBar.baz.woofwoof();


  • Considered Harmful

    @morbiuswilters said:

    var Bar = function(a, b, c) {
    if (!(this instanceof Bar)) {
    Bar.prototype = new Foo(a, b, c);

    Fancy syntax highlighting, but won't this blow away the state of all other Bars every time the function is invoked?



  • @joe.edwards said:

    Fancy syntax highlighting...

    http://tohtml.com/ Pretty good, although I did have to fix some HTML entities it stuck in there.

    @joe.edwards said:

    ...but won't this blow away the state of all other Bars every time the function is invoked?

    I can see why you'd think so, but this might clarify:

    <html><body style='color:#000000; background:#ffffff; '>
    var Foo = function() {
    };
    

    var firstFooPrototype = {
    someData: "dildo",
    someFunc: function() {
    console.log(this.someData);
    }
    };

    Foo.prototype = firstFooPrototype;

    /* Foo.someFunc(); // error! someFunc is not part of Foo's prototype chain */

    var myFoo = new Foo();

    myFoo.someFunc(); // outputs "dildo"

    myFoo.someData = "55 gallons of lube"; /* setting an "own" property on myFoo means it will
                                                no longer go up the prototype chain to resolve
                                                someData. however, it still will for someFunc */

    myFoo.someFunc(); // outputs "55 gallons of lube"

    var otherFoo = new Foo();

    otherFoo.someFunc(); // outputs "dildo". still uses prototype chain for someData

    Foo.prototype.someData = "bucket of fried chicken";

    myFoo.someFunc(); // outputs "55 gallons of lube". myFoo still has "own" property for someData.

    otherFoo.someFunc(); /* outputs "bucket of fried chicken". otherFoo has no "own" property,
                            so it still uses the prototype chain to resolve someData */

    Foo.prototype.someFunc = function() {
    console.log("donkey show!");
    };

    myFoo.someFunc(); // outputs "donkey show!". although someData was overriden, someFunc was not

    otherFoo.someFunc(); // same here

    var secondFooPrototype = {
    allNewFunc: function() {
    console.log("wine coolers");
    }
    };

    Foo.prototype = secondFooPrototype;

    /* myFoo.allNewFunc(); // error, allNewFunc is undefined on myFoo. but we just set it on Foo.prototype ??
                                let's try something else.. */

    myFoo.someFunc(); /* outputs "donkey show!" shouldn't this be blown away when we unset the prototype? No!
                            see, objects have an internal pointer to their prototype which they use when to
                            navigate up the prototype chain. this cannot be set explicitly on an object. it is
                            set by the runtime when a function is invoked with new. the internal prototype pointer then points to
                            whatever object was pointed to by the "prototype" property of the function when we invoked it with "new".

                            since Foo.prototype is also a pointer to this object, you can modify the object at
                            runtime by modifying the properties of Foo.prototype, automagically adding properties that will be
                            picked up by subtypes that have Foo in their prototype chain.

                            however, if we simply change what Foo.prototype points to, we aren't modifying the
                            object itself, just the pointer Foo.prototype contains. all of our subtypes created
                            from Foo still have their internal prototype pointer pointing to the original object */

    var anotherFoo = new Foo();

    anotherFoo.allNewFunc(); // outputs "wine coolers" since anotherFoo's internal prototype pointer points to secondFooPrototype

    // and finally..

    var Bar = function() {
    };

    Bar.prototype = {
    stuff: "huffing spray paint",
    printStuff: function() {
    console.log(this.stuff);
    }
    };

    var myBar = new Bar();

    myBar.printStuff(); // outputs "huffing spray paint"

    var oldBarPrototype = Bar.prototype; // retain a reference

    // overwrite Bar's prototype. myBar's internal prototype pointer still points to the old Bar prototype,
    // which I have retained a reference to for later use
    Bar.prototype = {
    moreStuff: "mom's not home until 11!",
    printMoreStuff: function() {
    console.log(this.moreStuff);
    }
    };

    var otherBar = new Bar();

    otherBar.printMoreStuff(); // outputs "mom's not home until 11!"

    // the finale
    oldBarPrototype.stuff = "dhromed's best birthday ever!"

    myBar.printStuff(); // outputs "dhromed's best birthday ever!"

    And as a further evil challenge, figure out what's going on here:

    <html><body style='color:#000000; background:#ffffff; '>
    var Evil = function() {
        var _protos = {};
    
    <span style='color:#800000; font-weight:bold; '>return</span> <span style='color:#800000; font-weight:bold; '>function</span><span style='color:#808030; '>(</span>evilName<span style='color:#808030; '>,</span> evilCause<span style='color:#808030; '>)</span> <span style='color:#800080; '>{</span>
        <span style='color:#800000; font-weight:bold; '>if</span> <span style='color:#808030; '>(</span><span style='color:#808030; '>!</span><span style='color:#808030; '>(</span><span style='color:#800000; font-weight:bold; '>this</span> <span style='color:#800000; font-weight:bold; '>instanceof</span> Evil<span style='color:#808030; '>)</span><span style='color:#808030; '>)</span> <span style='color:#800080; '>{</span> <span style='color:#696969; '>// invoked without "new"</span>
            <span style='color:#800000; font-weight:bold; '>if</span> <span style='color:#808030; '>(</span><span style='color:#800000; font-weight:bold; '>typeof</span> _protos<span style='color:#808030; '>[</span>evilName<span style='color:#808030; '>]</span> <span style='color:#808030; '>!=</span> <span style='color:#0000e6; '>'undefined'</span><span style='color:#808030; '>)</span> <span style='color:#800080; '>{</span>
                <span style='color:#800000; font-weight:bold; '>return</span> _protos<span style='color:#808030; '>[</span>evilName<span style='color:#808030; '>]</span><span style='color:#800080; '>;</span>
            <span style='color:#800080; '>}</span>
    
            Evil<span style='color:#808030; '>.</span><span style='color:#797997; '>prototype</span> <span style='color:#808030; '>=</span> _protos<span style='color:#808030; '>[</span>evilName<span style='color:#808030; '>]</span> <span style='color:#808030; '>=</span> <span style='color:#800080; '>{</span>
                antagonist<span style='color:#800080; '>:</span> <span style='color:#0000e6; '>'The Devil'</span><span style='color:#808030; '>,</span>
                blame<span style='color:#800080; '>:</span> <span style='color:#800000; font-weight:bold; '>function</span><span style='color:#808030; '>(</span><span style='color:#808030; '>)</span> <span style='color:#800080; '>{</span>
                    console<span style='color:#808030; '>.</span><span style='color:#800000; font-weight:bold; '>log</span><span style='color:#808030; '>(</span><span style='color:#800000; font-weight:bold; '>this</span><span style='color:#808030; '>.</span>antagonist <span style='color:#808030; '>+</span> <span style='color:#0000e6; '>' made me do it!'</span><span style='color:#808030; '>)</span><span style='color:#800080; '>;</span>
                <span style='color:#800080; '>}</span>
            <span style='color:#800080; '>}</span><span style='color:#800080; '>;</span>
    
            <span style='color:#800000; font-weight:bold; '>return</span> <span style='color:#800000; font-weight:bold; '>new</span> Evil<span style='color:#808030; '>(</span>evilName<span style='color:#808030; '>,</span> evilCause<span style='color:#808030; '>)</span><span style='color:#800080; '>;</span>
        <span style='color:#800080; '>}</span>
    
        <span style='color:#800000; font-weight:bold; '>if</span> <span style='color:#808030; '>(</span><span style='color:#800000; font-weight:bold; '>typeof</span> evilCause <span style='color:#808030; '>!=</span> <span style='color:#0000e6; '>'undefined'</span><span style='color:#808030; '>)</span> <span style='color:#800080; '>{</span>
            Evil<span style='color:#808030; '>(</span>evilName<span style='color:#808030; '>)</span><span style='color:#808030; '>.</span>antagonist <span style='color:#808030; '>=</span> evilCause<span style='color:#800080; '>;</span>
        <span style='color:#800080; '>}</span>
    <span style='color:#800080; '>}</span><span style='color:#800080; '>;</span>
    

    }();

    var plainOldEvil = Evil("plain");
    plainOldEvil.blame(); // The Devil made me do it!

    var seeNo = Evil("seeNo", "Rosie O'Donnell");
    seeNo.blame(); // Rosie O'Donnell made me do it!

    var hearNo = Evil("hearNo", "Fran Drescher");
    hearNo.blame(); // Fran Drescher made me do it!

    var speakNo = Evil("speakNo", "Ben L.");
    speakNo.blame(); // left as an exercise for the reader


  • Discourse touched me in a no-no place

    @PSWorx said:

    Hmm, I've never actually thought about call()ing a constructor before - I guess that happens if you write too much classical OO. :)
    Placement new. Not a new thought.



  • @morbiuswilters said:

    "dhromed's best birthday ever!"

     

    A stunning conclusion!

     

    The answer is, of course, to not use inheritance in javascript. If you need inheritance, your application has clearly outgrown its scripty nature.



  • @dhromed said:

    The answer is, of course, to not use inheritance in javascript. If you need inheritance, your application has clearly outgrown its scripty nature.

    I'd generally say that inheritance is a mistake in any language. It's a loophole in encapsulation, allowing subtypes access to the inner-workings of a class. If your language has private and protected members, you can at least somewhat work around this by making your "internal API" all protected and everything else private, but I really question whether it's worth the hassle.



  • @morbiuswilters said:

    @dhromed said:
    The answer is, of course, to not use inheritance in javascript. If you need inheritance, your application has clearly outgrown its scripty nature.

    I'd generally say that inheritance is a mistake in any language. It's a loophole in encapsulation, allowing subtypes access to the inner-workings of a class. If your language has private and protected members, you can at least somewhat work around this by making your "internal API" all protected and everything else private, but I really question whether it's worth the hassle.

    When I click save, Excel clears the undo stack.


  • Considered Harmful

    I broke it.

    Evil( 'toString' ).blame();
    TypeError: Evil(...).blame is not a function

    (Hint, typeof _protos[evilName] != 'undefined' matches inherited properties. Try _protos.hasOwnProperty( evilName ) or _protos[ evilName ] instanceof Evil)


  • @joe.edwards said:

    (Hint, typeof _protos[evilName] != 'undefined' matches inherited properties. Try _protos.hasOwnProperty( evilName ) or _protos[ evilName ] instanceof Evil)

    This is true. I blame the dildos.

    @joe.edwards said:

    Filed under: Never use != when !== will do.

    I see that around, but I'm not sure what difference it would make in this case, when comparing to a string literal.



  • @Ben L. said:

    When I click save, Excel clears the undo stack.

    While I give them credit for not using inheritance, Go's type system seems really, really fucked.

    So it's a statically-typed language with interfaces, but the interfaces are implicit--you implement an interface simply by implementing all of its methods. I guess this saves the very tiny amount of typing required to specify the interface in the class declaration. But what happens when you change the interface? Now all of your classes just suddenly stop being subtypes of that interface. How do you track down all of the subtypes of the interface and implement the changes necessary to bring it back into compliance with the API?

    Then they go on to say "Types can satisfy many interfaces at once, without the complexities of traditional multiple inheritance." which is true of practically any modern language that implements them. Interfaces were added to Java to allow for multiple subtypes without worrying about the problems of multiple inheritance.

    Plus, as far as I can tell, Go doesn't provide what I'd really like to see, which is some auto-magic method of delegating method calls to an internal type, which would make composition much more powerful. Your outer class would appear to be a subtype of the inner class, but wouldn't need to 1) inherit from anything, with all the problems that entails; or 2) need to implement every bloody method itself, with all the reduction in code-reuse that entails.



  • @morbiuswilters said:

    Then they go on to say "Types can satisfy many interfaces at once, without the complexities of traditional multiple inheritance." which is true of practically any modern language that implements them.

    Yeah wait... is this language being designed by people who have literally only used C++ in their lives???



  • @blakeyrat said:

    Yeah wait... is this language being designed by people who have literally only used C++ in their lives???

    It wouldn't surprise me if the Go team has been out-of-touch with mainstream technology for the last 40+ years. "We think electrical outlets should have a third prong which provides a safety ground. That way, you won't keep getting shocked when you try to use your lava lamp in the bathtub. Jive."


  • Considered Harmful

    @morbiuswilters said:

    @blakeyrat said:
    Yeah wait... is this language being designed by people who have literally only used C++ in their lives???

    It wouldn't surprise me if the Go team has been out-of-touch with mainstream technology for the last 40+ years. "We think electrical outlets should have a third prong which provides a safety ground. That way, you won't keep getting shocked when you try to use your lava lamp in the bathtub. Jive."

    We'll develop a GUI debugger once there's a stable preemptively multitasking OS on the market.



  • @joe.edwards said:

    @morbiuswilters said:
    @blakeyrat said:
    Yeah wait... is this language being designed by people who have literally only used C++ in their lives???

    It wouldn't surprise me if the Go team has been out-of-touch with mainstream technology for the last 40+ years. "We think electrical outlets should have a third prong which provides a safety ground. That way, you won't keep getting shocked when you try to use your lava lamp in the bathtub. Jive."

    We'll develop a GUI debugger once there's a stable preemptively multitasking OS on the market.

    Good luck doing that with polio ravaging the country.



  • @blakeyrat said:

    @morbiuswilters said:
    Then they go on to say "Types can satisfy many interfaces at once, without the complexities of traditional multiple inheritance." which is true of practically any modern language that implements them.

    Yeah wait... is this language being designed by people who have literally only used C++ in their lives???

    For people like you two who can't actually read and understand English, here's an explanation via some example code:

    type Writer interface {
        Write([]]byte) (int, error)
    }

    That's an interface. It's named Writer. It is implemented by ANY VALUE with a method with that signature. You don't need to declare that they implement it, like you do in TRADITIONAL MULTIPLE INHERITANCE. You recognize a pattern and you use that. You don't have to go back and edit all of your old code to implement the new interface.

    type Reader interface {
        Read([]byte) (int, error)
    }

    Now we have two interfaces, Reader and Writer, which let us Read and Write things, respectively.

    What if we want to make a function that needs to read AND write things to the same place?

    func Dildo(rw interface{ Reader; Writer })
    You can't do that in C++ or Java or C# or any other language with TRADITIONAL MULTIPLE INHERITANCE.


  • @Ben L. said:

    For people like you two who can't actually read and understand English, here's an explanation via some example code:

    type Writer interface {
        Write([]byte) (int, error)
    }

    Ok but say now you need to get the content length and so you need to change Writer to this:

    type Writer interface {
        Write([]byte) (int, error)
        GetLength() (int)
    }

    Now everything that used to be a writer is magically no longer a writer. Meaning that other code, expecting to see a Writer, will not suddenly not get one. What's the solution to that? Just manually find every class with a Write() function and manually retrofit it?

    With the C# implementation, if you change your interface the compiler will flat-out tell you, "hey buddy, these other classes need to change to to fit the new interface, HTH HAND." With Go it looks like you're just fucked.

    @Ben L. said:

    You can't do that in C++ or Java or C# or any other language with TRADITIONAL MULTIPLE INHERITANCE.

    Uh... why not?

    I guess because you need to create a third interface to encapsulate the other two? But that's just a couple lines of typing.

    interface IReaderAndWriter : IReader, IWriter {}

    Does that work? Honestly I've never had it come up, so I've never tried.

    EDIT: I just tried it. Replaced the second colon with a comma. Works fine.



  • @Ben L. said:

    What if we want to make a function that needs to read AND write things to the same place?

    func Dildo(rw interface{ Reader; Writer })
    You can't do that in C++ or Java or C# or any other language with TRADITIONAL MULTIPLE INHERITANCE.

    So what the fuck does that even do? You can make something that implements two interfaces:

    public interface IWriter {
    	int Write(byte[]);
    }
    public interface IReader {
    	int Read(byte);
    }
    public class Dildo : IWriter, IReader {
    	public int Write(byte[]){
    		// ...
    	}
    	public int Read(byte){
    		// ...
    	}
    }

    You can make a method that takes either a IReader or an IWriter using overloading:

    public void Dildo(IReader rw){
    	// ...
    }
    public void Dildo(IWriter rw){
    	// ...
    }

    And if you require a parameter that takes both interfaces, in C#, the official suggestion is to refactor by combining to a single interface:

    public interface IReadWriter : IReader, IWriter {}

    But you can do it with generics:

    public void Dildo<T>(T rw) where T : IReader, IWriter {
    	// ...
    }

    Which you can (IIRC) just call as Dildo(rw) because the type will be filled in by the compiler.

    So whatever you're trying to do, it works in C#.

    EDIT: Also, multiple return values are no substitution for exceptions.



  • Aha! I win the battle of the "explaining C# to an idiot!"



  • @Ben L. said:

    It is implemented by ANY VALUE with a method with that signature.

    Which is incredibly fragile. "Hey, the interface changed, now all our objects stopped being that subtype!" It also introduces some nice ambiguity "Did the programmer mean to implement this interface or did they just happen to create a method with an identical signature by accident?"

    By removing the intention of an explicit declaration, it further muddies the already-muddy waters of substitutability.

    @Ben L. said:

    You don't need to declare that they implement it, like you do in TRADITIONAL MULTIPLE INHERITANCE.

    No. Interfaces are not the same as multiple inheritance. Stop saying this.

    @Ben L. said:

    You don't have to go back and edit all of your old code to implement the new interface.

    Of course you do. Or are you saying you'll notice that several classes implement the same exact method meaning precisely the same thing and that you will then create an interface around that? That's ass-backwards, dude.

    @Ben L. said:

    What if we want to make a function that needs to read AND write things to the same place?

    func Dildo(rw interface{ Reader; Writer })

    How in the fuck does that work? Writing should take bytes and reading should return them, no? How does your method know whether it's supposed to be reading or writing?

    And Go doesn't support overloading, so what if you have a method named Read that exists in multiple interfaces and then you implement it in your class*? Which interface does it use? Only one would be correct, right? So did Go copy the diamond problem from C++ without actually copying a single benefit of multiple inheritance?


    (*struct, whatever, I'm not learning Go's retarded nomenclature that ignores 30 years of industry standard practice..)



  • @blakeyrat said:

    What's the solution to that?

    Go interfaces: write once, regret everywhere



  • @morbiuswilters said:

    It also introduces some nice ambiguity "Did the programmer mean to implement this interface or did they just happen to create a method with an identical signature by accident?"

    That's a good fucking point. A function named "Write()" could mean something vastly different to, say, a $25,000 plotter than a file handle. And your Go programming genius team won't look so smart when that plotter bent its own arm in half as it was sent a shitload of bytes from a YouTube video.



  • @blakeyrat said:

    Ok but say now you need to get the content length and so you need to change Writer to this:

    type Writer interface {
        Write([]]byte) (int, error)
        GetLength() (int)
    }

    That's no longer a Writer. That's a Write-and-also-get-length-er. If you needed a new interface, make a new interface. Also, what the fuck is the length of a writer?



  • @blakeyrat said:

    @morbiuswilters said:
    It also introduces some nice ambiguity "Did the programmer mean to implement this interface or did they just happen to create a method with an identical signature by accident?"

    That's a good fucking point. A function named "Write()" could mean something vastly different to, say, a $25,000 plotter than a file handle. And your Go programming genius team won't look so smart when that plotter bent its own arm in half as it was sent a shitload of bytes from a YouTube video.

    That's the fault of the programmer who passed an *ExpensivePlotter to a function that reads from *youtube.Video. Go doesn't do things you don't tell it to.



  • @Ben L. said:

    If you needed a new interface, make a new interface.

    So you can never modify an existing interface. Touche, Go.



  • @morbiuswilters said:

    @Ben L. said:
    If you needed a new interface, make a new interface.

    So you can never modify an existing interface. Touche, Go.

    If you have a public API that an automated build system builds against, adding methods to an unrelated, public interface causes problems no matter what language you're using. Builds will fail and production grinds to a halt. "Oh look, now all of our IFoos need to implement the unrelated IBar interface as well because someone edited an interface that was used everywhere. How fun."



  • @Ben L. said:

    Go doesn't do things you don't tell it to.

    THIS IS A TRUE STATEMENT ABOUT EVERY FUCKING LANGUAGE.

    @Ben L. said:

    That's the fault of the programmer who passed an *ExpensivePlotter to a function that reads from *youtube.Video.

    So tell us again how Go's type system is not a clusterfuck that puts burderns on the programmer while providing no benefit?

    I'll say it again: by removing explicit declarations of Interfaces, Go makes it that much harder to make use of substitutability. In Java you have an interface. That interface has been explicitly created by someone. It defines the method signatures a class must implement, but it also includes any number of invariants which are not captured in the method signatures. This interface should have documentation explaining these invariants and what the various methods must do to comply with the behavior that is needed by anyone using instances of the interface.

    What's more, a programmer should comprehend this interface and acknowledge that he comprehends it--part of how he does this is by explicitly declaring it in a class. Go removes this requirement and just assumes any classes which share signatures with an interface must intend to implement that interface. In short, Go is using duck typing here. Now, I don't have a problem with duck typing (although some people do, and it's arguable that a compiled language which supposedly allows system programming might want stricter type safety, but whatever..) The thing is, if you're going to have duck typing, then why have interfaces at all? Or even any declared types? The only plausible reason is performance: by being able to do the duck typing at compile time it doesn't have to be done at runtime.

    But from what you've said so far, Go would allow stupidity like someone defining a "Roundable" interface for objects to have their corners sanded off using a round() method. However, if there was a number object that also had a round() method, it suddenly belongs to the class of objects that were meant to have their corners sanded off which makes no sense. In fact, it's just an awful error waiting to spring up and bite you in the ass.



  • @Ben L. said:

    If you have a public API that an automated build system builds against, adding methods to an unrelated, public interface causes problems no matter what language you're using. Builds will fail and production grinds to a halt. "Oh look, now all of our IFoos need to implement the unrelated IBar interface as well because someone edited an interface that was used everywhere. How fun."

    This is why grown up software built by grown-ups uses the concept of "versioning". That way, I can change the way things behave. Regardless, versioning isn't even what we're talking about here.

    In a real language, the fact that an interface changed would let the compiler (or IDE, but go doesn't have one of those) fail any class which declared the interface but didn't implement the new methods. Therefore it's easy to go in and find everywhere that needs to be updated.

    With Go, how the fuck are you supposed to know? First, you'd have to use the old interface definition to find any class which might have been considered part of that interface due to the fact that it at one time used a particular method name. Then you can search for those classes and add the method to each one to make sure that it now fits the new interface definition. Since Go has such shitty tool support, I'm going to go ahead and assume there is no tool provided which will do any of this for you.

    What's more, what if you end up with classes that didn't mean to implement the interface but--because Go was written by stupid people--they're automatically lumped in by the compiler? Now you the person responsible for adding this new interface must go through these classes and determine if they even meant to implement the interface. Wow, what a time savings over having to type a few interface names in the class declaration.

    Now, I imagine the Go compiler can probably catch some of this for you. If you have an IFoo which changes its definition, and a class Bar which used-to-but-no-longer implements it, then object baz (which is of type Bar) will not be able to be used where IFoo is required. But first off, the error is now at the point where you made the mistake of thinking you could use polymorphism in Go, instead of at the class definition. And second, this does you no fucking good if no objects are actually being used--the class just silently stops being a part of the interface.

    Bravo.



  • I like making fun of Go, we should do this full-time.


  • Trolleybus Mechanic

    @blakeyrat said:

    I like making fun of Go, we should do this full-time.
     

    And you say you never contribute to open source projects.



  • @blakeyrat said:

    I like making fun of Go, we should do this full-time.

    As long as Ben L. doesn't disappear or get a clue we will.



  • @dhromed said:

    The answer is, of course, to not use inheritance in javascript. If you need inheritance, your application has clearly outgrown its scripty nature.

    I perfectly agree with that - but unfortunately, there seem to be more and more people who think javascript is so awesome that it obsoletes all other programming languages on the planet - like, e.g. the developers of Chrome or Firefox. Hence you're kinda stuck with it if you want to write browser extensions or halfway cross-platform compatible mobile apps without getting insane. However complex your application may be.



  • You're missing the fact that the concrete type of any interface value is known at compile time (because at some point, you're giving something that expects an interface a value that is not an interface).



  • @morbiuswilters said:

    The thing is, if you're going to have duck typing, then why have interfaces at all? Or even any declared types? The only plausible reason is performance: by being able to do the duck typing at compile time it doesn't have to be done at runtime.

    From what I got, Go makes most sense if you see it as duck typed with a shiny wrapping. By that thought, "interfaces" really only exist in the context of functions calling members on them, not while they are "implemented".
    So

    
    

    Type FunnyAnimal interface {
    bark() (string)
    meow() (string)
    }

    ...

    doStuff(foo FunnyAnimal)


    would be just a slightly more formal way to write

    doStuff(foo) // assumes foo has methods bark() and meow() that both returns strings and takes no arguments. or else.

    ... but it doesn't tell anything about how other functions view the same foo or if foo was even implemented with FunnyAnimal in mind. I don't think duck typing is a particularly good idea in general, but at least that would make kind of sense. At least, that's how I'd understand it.

    Then again, the official Go FAQ doesn't exactly inspire one with confidence about the development team:

    @Go FAQ said:

    Types can satisfy many interfaces at once, without the complexities of traditional multiple inheritance.
    Inheritance vs polymorphism: Apparently still a hard problem.

    @Go FAQ said:

    (...) the Go compiler does not report warnings, only errors that prevent compilation. There are two reasons for having no warnings. First, if it's worth complaining about, it's worth fixing in the code. (And if it's not worth fixing, it's not worth mentioning.) Second, having the compiler generate warnings encourages the implementation to warn about weak cases that can make compilation noisy, masking real errors that should be fixed.
    Didn't we have that discussion around here just a few days ago?

    @Go FAQ said:

    Interfaces can be very lightweight—an interface with one or even zero methods can express a useful concept.
    Interfaces are defined by their methods. But there are also interfaces with zero methods.

    what

    how

    but

    i dont even



  • @Ben L. said:

    You're missing the fact that the concrete type of any interface value is known at compile time (because at some point, you're giving something that expects an interface a value that is not an interface).
    Depends what you're doing. A lot of the benefits of interfaces in Java come from the fact that you don't need to know the implementation when compiling a class that takes an interface. Without that, plugins would be pretty much impossible after all.



  • @PSWorx said:

    [quote user="Go FAQ"]
    Interfaces can be very lightweight—an interface with one or even zero methods can express a useful concept.


    Interfaces are defined by their methods. But there are also interfaces with zero methods.

    what

    how

    but

    i dont even

    [/quote]
    The type interface{} is "any value with a method set that is a superset of the empty set". That's useful in the same cases java.util.Object and C#'s object are useful. For example, fmt.Print accepts any value and prints it to stdout. reflect.ValueOf allows yo to look at the properties of any value. map[string]interface{} is roughly equivelent to a JSON object.



  • @PSWorx said:

    @Ben L. said:
    You're missing the fact that the concrete type of any interface value is known at compile time (because at some point, you're giving something that expects an interface a value that is not an interface).
    Depends what you're doing. A lot of the benefits of interfaces in Java come from the fact that you don't need to know the implementation when compiling a class that takes an interface. Without that, plugins would be pretty much impossible after all.

    Same with Go. You can make a function that doesn't care what the concrete type of a value is. It's the same in Java - you can't instantiate an interface directly.



  • @blakeyrat said:

    I like making fun of Go, we should do this full-time.

    I bet there's a DARPA contract in it if we can convince Iran or North Korea to use Go.



  • @Ben L. said:

    You're missing the fact that the concrete type of any interface value is known at compile time (because at some point, you're giving something that expects an interface a value that is not an interface).

    I'm not even sure which point you're responding to, but I did not miss that: I said that the compiler can catch when an object is being cast to an interface it doesn't implement, but that this is stupid because a) you now get tons of "object is not of type IFoo" errors instead of a single "class declares IFoo but does not implement" (the Go compiler might be smart enough to condense the former into the latter, but I doubt it..); and b) does you no good if the class is never instantiated since there are no objects to fail.

    And once again I must ask: what if somebody passes an object of a class that inadvertently implements an interface simply because it has the same method signature(s)? That could really fuck some shit up. "Well, that's the programmer's fault, not the language." No shit, the vast, vast majority of bugs are the programmer's fault. That's why languages implement features to make programming safer, like compile-time checking of interfaces. But Go has completely undermined the type-safety aspect of interfaces and just turned them into a hint to the compiler so that it can do compile-time duck typing. Great.

    So back to my original point, which you have so-far failed to answer: what benefit does any of this provide over specifying the interface in a class declaration? It seems like typing a few interface names would be much, much easier than the clusterfuck the Go team has created.



  • @PSWorx said:

    I don't think duck typing is a particularly good idea in general, but at least that would make kind of sense. At least, that's how I'd understand it.

    My understanding is Go supports duck typing and that interfaces are just a compiler hint to let it do the checks at compile-time rather than run-time. It provides no other benefit that I can see. So basically they added a nearly-superfluous language element, named it after an actually-useful element from other languages like Java, all so they could save a few run-time checks. (And it also turns those run-time errors into compiler errors, but this is hardly a plus because: 1) Ben has asserted that using the wrong object is the programmer's fault, so why waste precious compiler resources on this?; and 2) it doesn't protect against the inadvertently-implemented-an-interface case, which in most languages would be a compiler error but which in Go becomes a much more insidious logic error.)

    @PSWorx said:

    Inheritance vs polymorphism: Apparently still a hard problem.

    These guys use vim as an IDE. I'm just shocked Go has compilers written for anything other than a PDP-11.

    @PSWorx said:

    Didn't we have that discussion around here just a few days ago?

    Yes. And while I agree that some languages report far too many errors (C and PHP come to mind), removing them is flat-out dumb. For example, Java is pretty sparing with warnings, but it will give them in some cases; a big one I can think of is when using methods marked deprecated. Does Go even support deprecated methods? Since it doesn't have warnings, what does it do? Halt the compiler? Say nothing? Who cares?

    And C's extreme number of warnings, while annoying, are a result of the fact that it's a pretty messy language at this point that has evolved over the last 40 years, so I can at least understand why they're there. With PHP, there's no fucking reason for most of the warnings, except that PHP was written by people as incompetent as the Go developers. Albeit, PHP at least has a use case: writing simple webapps quickly.



  • @Ben L. said:

    The type interface{} is "any value with a method set that is a superset of the empty set". That's useful in the same cases java.util.Object and C#'s object are useful.

    Oh sweet Jesus...

    1. It's java.lang.Object, not java.util.Object

    2. Who the fuck uses java.lang.Object? Every use case I can think of was superseded nearly a decade ago by generics, which--while full of stupid design decisions--still allow for type safety far beyond what Go supports.

    3. So they needed a void pointer but didn't want to add a new keyword, so instead overloaded the meaning of interface. Classy.

    And you say that it catches any value with a method set, but what about objects that don't have methods? What if you have an object that just has fields, is it matched by this or did they have to introduce some other hack to get that working?



  • @Ben L. said:

    @PSWorx said:
    @Ben L. said:
    You're missing the fact that the concrete type of any interface value is known at compile time (because at some point, you're giving something that expects an interface a value that is not an interface).
    Depends what you're doing. A lot of the benefits of interfaces in Java come from the fact that you don't need to know the implementation when compiling a class that takes an interface. Without that, plugins would be pretty much impossible after all.

    Same with Go. You can make a function that doesn't care what the concrete type of a value is. It's the same in Java - you can't instantiate an interface directly.

    That's not what he said. We know Go supports duck typing. His point was that in Java, you can load a class that implements an interface but that was compiled completely separately from your main project, usually by people completely unrelated to your project and who do not have the source of your project--that's a plugin. How does Go handle this?

    Since:

    1. your project didn't have the source to the plugin at the time it was compiled

    2. Go's interface checking is done at compile-time

    3. Go interfaces aren't declared anywhere, so they don't attach themselves to the classes

    how does Go know what to do? Presumably, it just stays with duck typing but the "method not implemented" errors become run-time errors instead of compiler errors. Which would mean Go supports run-time duck typing but also compile-time duck typing, which is kinda wacky.



  • @morbiuswilters said:

    What part of static language don't you understand?

    When you pass the value to a function that expects an interface, it gets typechecked at compile time. Go doesn't load extra libraries at runtime. It's all statically linked.



  • @Ben L. said:

    @morbiuswilters said:

    What part of static language don't you understand?

    When you pass the value to a function that expects an interface, it gets typechecked at compile time. Go doesn't load extra libraries at runtime. It's all statically linked.

    So Go doesn't support any dynamic linking? (So it doesn't even support plugins as your original comment incorrectly implied..) Wow, Jesus, great fucking systems language there.

    You know, we joke about the Go designers being 40 years behind the times, but static linking?? A feature so out-of-date it's not even supported anymore in C: The Language That Supports More Ill-Conceived Shit From the 70s Than A George McGovern Rally??? Good luck using Go to write real applications: every fucking library is going to be loaded in memory a dozen times over.

    Go: The Language Linux Kernel Developers Use to Test Swap Regressions. "We used to just write our own test cases in horribly-broken C, but nothing beats the shit out of the MMU like Go!"



  • @morbiuswilters said:

    @Ben L. said:
    @morbiuswilters said:

    What part of static language don't you understand?

    When you pass the value to a function that expects an interface, it gets typechecked at compile time. Go doesn't load extra libraries at runtime. It's all statically linked.

    So Go doesn't support any dynamic linking? (So it doesn't even support plugins as your original comment incorrectly implied..) Wow, Jesus, great fucking systems language there.

    You know, we joke about the Go designers being 40 years behind the times, but static linking?? A feature so out-of-date it's not even supported anymore in C: The Language That Supports More Ill-Conceived Shit From the 70s Than A George McGovern Rally??? Good luck using Go to write real applications: every fucking library is going to be loaded in memory a dozen times over.

    Go: The Language Linux Kernel Developers Use to Test Swap Regressions. "We used to just write our own test cases in horribly-broken C, but nothing beats the shit out of the MMU like Go!"

    i dont get it

    You think every computer has five megabytes of RAM and runs 70000 processes?


Log in to reply