• evnafets (unregistered) in reply to yuumei
    yuumei:
    HashMap<Long, HashMap<Timestamp, Vector<HashMap<String, Object>>>>

    So the long is the universe, the timestamp is the time, the vector is the dimension and the string is the key.

    What's wrong with that?

    Avtually the String is Life. The Long is the universe, and the HashMap is everything. Funnily enough all this program does is return "42"

  • methinks (unregistered) in reply to real_aardvark
    real_aardvark:
    I'd be interested to hear your arguments as to why Java is a "comparatively clean language." It's a fine language, but it is by no means comparatively clean. Unless, of course, you refer to the syntax -- which is largely irrelevant to anything other tidiosyncrasyhan a compiler.
    Think side effects. The ambiguity of many of C/C++'s idioms (and syntax constructs, at that) do not improve readability and maintainability a lot. You can do that even more in Perl, though ;o) Don't get me wrong, I like to code in C and also Perl too, but I'm always thinking of projects with large code bases where I have to hunt down stupid "guru" tricks which someone thought were a clever idea. Java has its idiosyncrasies too, no doubt, but in many regards you can do less harm in the code.
    real_aardvark:
    Operator overloading is clearly not needed in functional languages. In C-derived languages, though, it can be used as a useful design tool. Yes, it's syntactic sugar (and I personally do not like std::string operator+(...) used as 'concatenate'), but it can make things clear through design. As the Greeks say, μηδὲν ἄγαν. (The other classic Greek aphorism, γνῶθι σεαυτόν, is also relevant here.)
    Well well well, seems I've stumbled across nother guy with classic education. Nice. Still, I'd say that μηδὲν ἄγαν is in fact a point *against* syntactic sugar. I'd even say: Μέγα βιβλίον, μέγα κακόν. ;o)
    real_aardvark:
    The point is, why deny this syntactic sugar in a language? Why not just castrate the silly bastards who misuse it willy-nilly?
    Still the same argument. The more opportunities you give the people to shoot themselves in the foot, the more guns you will hear going off. Operator overloading provides a nice and clean syntax, if used correctly, but you do not really need it. And frankly, I do not count this into "design". There is no difference in the design of a system if you replace overloaded ops with method calls.
    real_aardvark:
    Well, no, Java memory management is not "deterministic."
    Point taken. I was oversimplifying here... γνῶθι σεαυτόν.

    I was under the impression that "deterministic" was not used in the narrower sense here. Repeating my argument from above, I still prefer implicit memory management with many people working on the same code base. Resource management is not trivial in itself and can be done wrong in any language.

    real_aardvark:
    Whilst in no way wishing to suggest that Java is a useless and pointless language that should never have been invented, or should have been strangled at birth before its reinvention from Oak or Green or whatever it was at Sun into "the Language of the Web" (which it subsequently failed to become), I would humbly suggest that other languages do it better, and often without the huge backwash of 'plz send me teh codez' morons.
    But of course the huge backwash of morons is not the fault of the language. Because it is so prevalent in the industry and promoted by the big players (which does not make it dearer...) so many more people try (!) to code in Java than in other languages (that may or may not do some things better) - but there is no ideal language out there, unfortunately. Most of the "plz send me teh codez" comes from the educational side, BTW. When I attended the university, Modula II was the language du jour for educating students. Now it is mostly Java, which is far from ideal in fact. I've had my share of "pleeez give me the codes", only that it was expressed verbally and meant handling over sheets of line printer paper ;o)
    real_aardvark:
    Python, Ruby, or some idealised version of Lisp/Scheme spring to mind.
    Ah yes, I'd really like to take up Lisp again, it's been a while... and Ruby and Python are great. But in big distributed systems where you need the whole stack of technologies in all the different layers, they also leave a lot to be desired.
    real_aardvark:
    Or, going backwards and actually hiring "gurus" (ie sentient human beings, such as you and possibly me) to do your programming for you, C++.
    Thanks a bundle for the hidden compliment ;o) Still, if I think of the developer teams I come across in different companies, I still prefer GC, no operator overloading and no pointers, thank you very much ;o)
  • (cs) in reply to methinks
    methinks:
    ad (1): "More code" is never really a reason to abandon a concept when this is the only "issue", especially with modern IDEs.
    More code won't damn something on its own. However, I would argue that if you have two ways of doing something, both of which have approximately the same efficiency and readability, if one is shorter, it's better.

    I would argue that the C++ RAII mechanism fits these criteria; it's usually just as readable as making it explicit with a try/finally or an anonymous inner class, just as efficient (if not more so), and shorter.

    ad (2): There is a very good reason for this limitation in Java: Such so-called "(anonymous) local classes" have a very limited scope (i.e. the block of code where there are declared) and can also only see local variables in the same scope. The lifetime of their instances, OTOH, is potentially much longer (the references can be passed out of the enclosing code block, compare the Listener-concept in Swing), so the values of the local variables might not exist any more (on the stack) when such an instance is used.
    I understand there's a good reason for it. But it does make some things harder if you know that it won't be called outside of the current function.
    And this is moreover never a limitation:
    Um, what? Please translate the following code that uses try/finally to one that uses an anonymous inner class (I make no guarantee that the syntax is correct, but it should be at least plenty clear enough to determine what I'm trying to do):
    int readFiles(string[] filenames) {
        int bytesRead = 0;
        for(string name : filenames) {
            try {
                FileInputStream f = new FileInputStream(name);
    
            int c;
            while ((c = in.read()) != -1) {
                ++bytesRead;
                System.out.write(c);
            }
    
        }
        finally {
            if(f != null) f.close()
        }
    }
    return bytesRead;
    

    }

    If there is a trivial way of making this use an anonymous inner class, I'm missing it.

    Method parameters can safely be declared final (and should not be "re-used" inside the method body by assigning different values anyway, so one could *always* declare them final!).
    That's a matter of style; it often makes sense.
    Most other local variables that change their values are not used inside a local class - and those local variables that *have* to change their value *and* be used in a local class (think e.g. loop variables...) can always be copied to a scoped final variable if necessary, e.g.: [snip]
    That only works if the inner class doesn't have to modify them.
    methinks:
    Operator overloading is mainly syntactic sugar and does not add to readability - you never know what a given operator behaves like by just looking at it being used - you have to look at it's implementation too.
    You never know what a given function behaves like just by looking at it being used - you have to know it's implementation too. Should we banish functions as syntactic sugar for explicit stack management and a goto as well?

    (I'm being deliberately inflammatory; functions are clearly much more important syntactic sugar than operator overloading.)

    And scope controlled deterministic destruction? Implicit memory management in Java is of course also deterministic - objects going out of scope and not being held on to are destroyed.
    Eventually. When the garbage collector gets around to it. If it does.

    In other words, no, it's not deterministic.

  • (cs) in reply to Anonymous Cowherd
    Anonymous Cowherd:
    ThePants999:
    Not a WTF at all in my opinion. Here's a snippet from our code (which I don't consider a WTF either):
      private static Map<Integer,
                         Map<String,
                             Map<Integer,
                                 Collection<StatisticValueInternal>>>> sExtraInfo =
                             new Hashtable<Integer,
                                           Map<String,
                                               Map<Integer,
                                                   Collection<StatisticValueInternal>>>>();

    You'd be better off doing either:

    1. Creating an aggregated object that encapsulates the Integer, String, Integer tuple, encapsulating the information in the Map's key, or
    2. Creating a TripleMap, which associates a 3-tuple with a value, thus encapsulating the tuple information within the Map.

    No, unfortunately not. This is actually hierarchical, not just a 3-tuple key. We needed to perform operations like deleting all data associated with a particular key at one layer of the hierarchy, etc.

  • (cs) in reply to methinks

    Hey look, another page.

    methinks:
    Still the same argument. The more opportunities you give the people to shoot themselves in the foot, the more guns you will hear going off. Operator overloading provides a nice and clean syntax, if used correctly, but you do not really need it.
    In Java this is much more true than in C++ (read: operator overloading is much more important in C++) since C++ templates are a lot more powerful. The fact that templates use compile-time duck typing means that you can write a template that uses * and it will operate the same on iterators and pointers, you can write a template that uses [] and it will operate the same on vectors and arrays, or you can write a template that uses + and operates the same on ints and quaternion objects.

    You could compensate by writing and overloading a bunch of functions (like "int add(int a, int b) { return a + b; }" then call them, but that's much more work. Java is missing some nice syntactic sugar by omitting operator overloading, but if it were removed from C++ it would greatly increase the overhead of doing many things with templates.

    *Resource* management is not trivial in itself and can be done wrong in any language.
    Aren't you the person who just a moment ago wrote "The more opportunities you give the people to shoot themselves in the foot, the more guns you will hear going off"?

    The converse is that if you make it easier to do something right, the fewer guns you will hear going off. I (and others) are arguing that RAII makes it easier to get resource management right.

  • methinks (unregistered) in reply to EvanED
    EvanED:
    And this is moreover never a limitation:
    Um, what? Please translate the following code that uses try/finally to one that uses an anonymous inner class (I make no guarantee that the syntax is correct, but it should be at least plenty clear enough to determine what I'm trying to do):
    int readFiles(string[] filenames) {
        int bytesRead = 0;
        for(string name : filenames) {
            try {
                FileInputStream f = new FileInputStream(name);
    
            int c;
            while ((c = in.read()) != -1) {
                ++bytesRead;
                System.out.write(c);
            }
    
        }
        finally {
            if(f != null) f.close()
        }
    }
    return bytesRead;
    

    }

    If there is a trivial way of making this use an anonymous inner class, I'm missing it.

    First the few things you missed out: * "string" should be capitalized "String" * "FileInputStream f" must be declared outside the try-block to make it available in the finally-block * We need a catch-block too for various I/O exceptions * A ";" is missing after f.close() * f.close() must unfortunately but clearly also be inclosed in a try/catch - one more reason to do it with a local class.

    Here we go:

       static class FileProcessor {
          public abstract void processChar(char c);
          
          public int processFile(String filename) {
             int bytesRead = 0;
             FileInputStream f = null;
             try {
                f = new FileInputStream(filename);
                int c;
                while ((c = f.read()) != -1) {
                   ++bytesRead;
                   processChar((char)c);
                }
             }
             catch(Exception ex) { 
                System.err.println(ex);
             }
             finally {
                try { 
                   if(f != null) f.close(); 
                }
                catch(Exception ex) {}
             }
             return bytesRead;
          }
       }
    
    int readFiles(String[] filenames) {
       int bytesRead = 0;
       for(String name : filenames)
          bytesRead += new FileProcessor()
             {
                public void processChar(char c) { System.out.write(c); }
             }.processFile(name);
    	    
    	return bytesRead;					
       }	
    

    I took the liberty to implement it as a template pattern with a hook method that allows processing of each character. Each call to processFile() returns the number of chars in this file. BTW, of course one could do the processing of all files in the local class i.e. move the foreach-loop inside the process-method and return the total count:

    static class FilesProcessor {
       public abstract void processChar(char c);
       
       public int processFile(String[] filenames) {
          int bytesRead = 0;
          for(String name : filenames) {
             FileInputStream f = null;
                try {
                   f = new FileInputStream(name);
                   int c;
                   while ((c = f.read()) != -1) {
                      ++bytesRead;
                      processChar((char)c);
                   }
                }
                catch(Exception ex) {
                   System.err.println(ex);
                }
             finally {
                try { 
                   if(f != null) f.close(); 
                }
                catch(Exception ex) {}
             }
             return bytesRead;
          }
       }
    
    int readFiles(String[] filenames) {
       return new FilesProcessor()
       {
          public void processChar(char c) { System.out.write(c); }
       }.processFile(filenames);
    }
    

    The notation of local classes is admittedly something one has to get used to, but it is not more complex than other notations I know from various languages. Real closures would be better though.

    EvanED:
    Most other local variables that change their values are not used inside a local class - and those local variables that *have* to change their value *and* be used in a local class (think e.g. loop variables...) can always be copied to a scoped final variable if necessary, e.g.: [snip]
    That only works if the inner class doesn't have to modify them.
    It doesn't have to. See above.
    EvanED:
    methinks:
    Operator overloading is mainly syntactic sugar and does not add to readability - you never know what a given operator behaves like by just looking at it being used - you have to look at it's implementation too.
    You never know what a given function behaves like just by looking at it being used - you have to know it's implementation too. Should we banish functions as syntactic sugar for explicit stack management and a goto as well?
    That's not what I meant. A function/method has a name that (hopefully...) describes what it does. And it does not change semantics compared to some built-in standard behaviour like an overloaded operator does. *If* it does change its semantics, this is within the rules of polymorphic behaviour, i.e. it is either overloaded (then the parameters provide the context) or overridden (which means its behaviour is specialized in the derived class). OTOH an operator which looks like a built-in standard can have completely different semantics if it is overloaded: a = b + c; is perhaps prettier to look at, but if I see a = b.add(c); I'm more likely to suspect that a,b and c are - say - complex numbers or matrices instead of primitives. It is debatable if this de- or increases readability, but still it is nothing but syntactic sugar, whereas functions are of course not.
    EvanED:
    And scope controlled deterministic destruction? Implicit memory management in Java is of course also deterministic - objects going out of scope and not being held on to are destroyed.
    Eventually. When the garbage collector gets around to it. If it does.

    In other words, no, it's not deterministic.

    I already answered that in another follow-up. Point taken. I was oversimplifying, because I did not think "deterministic" was used in the correct narrow sense in the post I was answering. Sorry for that.

  • methinks (unregistered) in reply to methinks
    methinks:
    static class FileProcessor { ...
    static class FilesProcessor { ...
    

    I'm sorry, I inadvertently omitted the "abstract" keyword:

       static abstract class FileProcessor { ...
    

    which is necessary, because I declared the hook method abstract. The class does not have to be static, but for testing purposes we can put it inside the test class - and then it makes sense to declare it static, because this makes it a "top level" class, similar to a class declared in its own file. When using it outside the declaring class, however, the complete name is "Outer.FileProcessor" (i.e. including the enclosing class' name, e.g. "Outer"). If we just use it inside the enclosing class, we could in fact make it private too:

       private static abstract class FileProcessor { ...
    

    For broad re-use, however, we should normally put it in its own source file and declare it public (and not static, it is "top level" anyway then):

       public abstract class FileProcessor { ...
    
  • (cs) in reply to methinks
    methinks:
    I took the liberty to implement it as a template pattern with a hook method that allows processing of each character. Each call to processFile() returns the number of chars in this file.
    ...which means that if you later want to have it return something else (maybe the file contents), you need to rewrite FilesProcessor. You can't write a generic function that you can use any time you want to open a file and be guaranteed to close it in this manner. This is what I'm getting at.

    The best way to do it (read: I think the only way to do it in a suitably generic manner) would probably be to have the function return an Object, then box up the bytes read in this case within the anonymous class, and unbox it at the end.

    With generics you can keep static type safety, but it still requires an extra boxing and unboxing step if you aren't just returning a primitive. Still way less convenient than not having to do anything special at all.

    BTW, of course one could do the processing of all files in the local class i.e. move the foreach-loop inside the process-method and return the total count
    This has the same problem.
    That's not what I meant. A function/method has a name that (hopefully...) describes what it does. And it does not change semantics compared to some built-in standard behaviour like an overloaded operator does.
    An operator overload also has a name that (hopefully) describes what it does, it's just a funny looking name like "+" that operates in infix notation.

    (With regards to the next point, the semantics don't really change, you're providing new semantics. The exception to this is like the && and || operators in C++. If you look in style books they caution against overloading them because it breaks short-circuiting and such. You could make a strong case that these shouldn't be overloadable, though I have had use for them in a library where operator overloading vastly, vastly improves readability in locations where it's used.)

    It is debatable if this de- or increases readability, but still it is nothing but syntactic sugar, whereas functions are of course not.
    Sure they are. What can you do in functions that I can't do in a language that doesn't provide them?
    I already answered that in another follow-up. Point taken. I was oversimplifying, because I did not think "deterministic" was used in the correct narrow sense in the post I was answering. Sorry for that.
    Agreed; I saw your post too late to do anything about it there.
  • Pseudonym (unregistered)

    The WTF isn't the actual code, here, but two Java quirks:

    1. It doesn't have type synonyms.

    I sometimes write code like this in C++, but it usually looks like this instead:

    shared_ptr<dictionary_t> d(new dictionary_t());

    1. It requires you to type the full names of types in all sorts of places (e.g. in the call to "new") where it is completely unnecessary.

    In Haskell, you could just type this:

    d <- Data.Map.empty

    and let the compiler infer the full type.

    The problem here is that the obvious way to piece together bits of generic libraries is way too verbose in Java, compared with other languages.

  • Pseudonym (unregistered) in reply to Tim Ward

    "Why hasn't Java got typedef?"

    The short answer is: It didn't need them before it got generics.

  • Pseudonym (unregistered) in reply to real_aardvark

    "2) If you want to do flexible partial-match searches: use a friggin' DBMS."

    I hope that was sarcasm. At last count, using a friggin' DBMS where it's not appropriate is the cause of far more Daily WTFs than Java generics.

  • methinks (unregistered) in reply to EvanED
    EvanED:
    methinks:
    I took the liberty to implement it as a template pattern with a hook method that allows processing of each character. Each call to processFile() returns the number of chars in this file.
    ...which means that if you later want to have it return something else (maybe the file contents), you need to rewrite FilesProcessor. You can't write a generic function that you can use any time you want to open a file and be guaranteed to close it in this manner. This is what I'm getting at.
    You're not being fair here - if you want it to do something else, you have to rewrite your C++ code as well - just at another place in the code. What you asked for was a solution to the problem that final local variables accessed from within a local (or local anonymous) class can't be changed.
    EvanED:
    The best way to do it (read: I think the only way to do it in a suitably generic manner) would probably be to have the function return an Object, then box up the bytes read in this case within the anonymous class, and unbox it at the end.

    With generics you can keep static type safety, but it still requires an extra boxing and unboxing step if you aren't just returning a primitive. Still way less convenient than not having to do anything special at all.

    Still another and perhaps more elegant option is to let the processFile()-method return the reference to the FileProcessor instance itself ("this"), so you can use it to transport out whatever information you like in one single expression. This is not totally generic, but OTOH even more self-documenting, because it adds context (via the accessor method's names) that explains what's happening and we add type safety on the way. E.g. we could add the char-counter and the content etc. as fields along with corresponding accessor methods:
    int count = new FilesProcessor() 
       {  
          public void processChar(char c) { System.out.write(c); }
       }.processFiles(names).getCharCount();
    
    and
    String content = new FilesProcessor() 
       {  
          public void processChar(char c) { }
       }.processFiles(names).getContentAsString();
    
    Different getContentAs...() methods could cater for different conversion needs. If processChar() was not abstract (perhaps not a bad idea in this case), we could leave out its empty implementation in the second example as well.

    I find this a quite elegant solution - you are free to think otherwise, of course ;o)

    EvanED:
    That's not what I meant. A function/method has a name that (hopefully...) describes what it does. And it does not change semantics compared to some built-in standard behaviour like an overloaded operator does.
    An operator overload also has a name that (hopefully) describes what it does, it's just a funny looking name like "+" that operates in infix notation.
    Point taken rearding the name. But what I wanted to express is this: you do not have a "standard" version built into the system which behaves in a way everybody expects for each and every method - and then perhaps changes semantics in some but not in all cases. This is true for operators OTOH.
    EvanED:
    It is debatable if this de- or increases readability, but still it is nothing but syntactic sugar, whereas functions are of course not.
    Sure they are. What can you do in functions that I can't do in a language that doesn't provide them?
    Not "in" functions, but "with" them - two words: Structured programming. That does make quite a difference... because missing operator overloading does *not* remove a whole programming paradigm. Go ask Mr. Wirth if this makes any difference ;o)
  • paratus (unregistered) in reply to EvanED

    [quote user="EvanED"][quote user="RobFreundlich"][quote user="EvanED"] [quote]And any Java developer worth shit would be running findbug. Prob Sol.[/quote] For a few of these problems. Will it tell me that I forgot to change the cursor back to a pointer?[/quote] If you set it up correctly, yeah. Else your Unit Tests will.

    But on the whole I agree, here is one of the few cases that Closures (Java 7) will help.

  • (cs) in reply to methinks
    methinks:
    <snip/> That's not what I meant. A function/method has a name that (hopefully...) describes what it does. And it does not change semantics compared to some built-in standard behaviour like an overloaded operator does.
    EvanED:
    An operator overload also has a name that (hopefully) describes what it does, it's just a funny looking name like "+" that operates in infix notation.
    Point taken regarding the name. But what I wanted to express is this: you do not have a "standard" version built into the system which behaves in a way everybody expects for each and every method - and then perhaps changes semantics in some but not in all cases. This is true for operators OTOH.
    EvanED:
    It is debatable if this de- or increases readability, but still it is nothing but syntactic sugar, whereas functions are of course not.
    Sure they are. What can you do in functions that I can't do in a language that doesn't provide them?
    Not "in" functions, but "with" them - two words: Structured programming. That does make quite a difference... because missing operator overloading does *not* remove a whole programming paradigm. Go ask Mr. Wirth if this makes any difference ;o)
    To RAII or not to RAII, that is the question?

    Look, Java does resource management one way (GC, no pointers, etc etc) and C++ does it another way. Take yer pick (or other preferred operating system). I happen to find RAII considerably more elegant than a bunch of "finally"s stuck around the place and some botch-job of vanilla-flavour synchronisation built into the language itself (see Allen Holub for gory details).

    But, to syntactic sugar. Ironic that we should be having this discussion over this particular OP, which is a rather fine example of syntactic sugar in Java.

    Arguably, anything other than an endless paper roll and a pencil is syntactic sugar -- thus the typical futile arguments about "goto" that infest this site and many others. It depends on what you see as "syntactic sugar." So here goes:

    Syntactic sugar is a language artefact that makes little or no difference to the compiled/byte-coded output of the relevant AST.

    Functions (in a type-checking language) are not syntactic sugar. Java generics are, and further suntactic sugar with the introduction of a typedef would therefore have been appropriate. Operator overloading sort-of is, and sort of isn't, because, as EvanED points out, it's hugely useful at the points where it intersects with generics (or indeed templates in general) ... for example, with iterators.

    This (I think) is an interesting example of "syntactic sugar," because although strictly not necessary to alter the generated code, it is certainly necessary to make the resultant behaviour generic.

    It therefore boils down to whether you want to give the programmer the power to do this. In the case of C++ and generics, I don't think you have much choice. In the case of Java, I recognise that a conscious effort was made on the part of the language designers (who were sitting in a small sweaty room at a time where C++ generics were just a gleam in Stepanov's eye) to simplify in this area. The end result, through no fault of their own, is that Java generics are a bit of a botch.

    And I rather like the idea that, in C++, it's easy to shoot your foot off. It's pretty bleeding obvious when you do so, too, and then it's time to hire a better C++ programmer. With due respect to your own knowledge of Java (and indeed C++), I don't agree with your argument concerning large systems teams in Java.

    If they were all in the top 20%, then, yes. Sadly, they're not. They'll leave just enough cruft and animal turds behind them when they leave for a more Enterprisey job to occupy those who are in the top 20% for the rest of their doomed, language-constricted little lives.

    Back to Lisp variants, I suspect.

    PS I think you're right about my use of μηδὲν ἄγαν, btw. I had something specific in mind when I inserted it, but then I took another shot of absinthe. (Well, one more can't hurt, can it?)

  • Michael Chermside (unregistered) in reply to yuumei
    Comment held for moderation.
  • umm... (unregistered)

    Lots of this C++ vs. Java discussion is centering on things which are non-factors in another environment: C++/CLI. If you don't know much about this, rest assured, it's not C# on a C++ compiler. You want deterministic destruction of managed types? No problem - variables come as either heap-allocated 'gcnew' types, or stack-allocated automatic ones...the difference:

    void a() { FileStream^ fs = gcnew FileStream(filename); }

    Upon returning, 'fs' is still alive - on the GC heap. But, if you:

    void b() { FileStream fs(filename); }

    Here, when the call returns, 'fs' is no more.

    So, it is trivial to use any type, managed or not, in the classic way of creating auto-reset objects. For example, in drawing code, you may make calls which affect the underlying DC, so you should reset them before you return. As was pointed out earlier, you can use the 'using' construct in C# or VB.Net as such:

    using GraphicsResetter gr(e.Graphics) { e.Graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit; }

    Where, GraphicsResetter stores the start state, and its' destruction at the end of the using statement resets things in the Graphics object. In C++/CLI, this is simpler yet...somewhere before using the Graphics object, you would just create an automatic local:

    GraphicsResetter gr(e->Graphics); e->Graphics->TextRenderingHint = TextRenderingHint::ClearTypeGridFit;

    No extra block-definition necessary, since 'gr' will be destroyed (i.e. disposed: ~GraphicsResetter() will be called) at the method's closing brace...just like any other stack-allocated C++ object would be.

    This is just one small example of the many advantages C++/CLI has over the other .Net languages; in addition to .Net Generics you also have the STL.Net implemention, applicability of typedefs for managed types, hybrid managed/native types for interop, the ability to do simple things like:

    DestroyIcon((HICON)cursor->Handle.ToPointer());

    ...which are impossible in C# without using PInvoke and dealing with the clutter and associated overhead. Overall, C++/CLI provides an excellent environment for programming .Net - and for Windows it's simply the best, hands-down. It's just surprising that so few people appear to be aware of it.

  • Rhialto (unregistered) in reply to Robin Message
    Robin Message:
    class PhoneBook extends map<string,string> {}

    It's slightly verbose, but it's basically a typedef.

    Except that you can't give this to a function that wants a map<string,string> as an argument. Such as a function that merges two maps. You will have to write your own copy of each such general function you wish to use. So it is not really a typedef, in that it isn't a synonym type at all.

  • Peter Lawrey (unregistered)

    The longest generic type I have written has 119 characters for code in production. It could have been longer but that would have been really silly. :)

  • (cs) in reply to umm...
    umm...:
    Lots of this C++ vs. Java discussion is centering on things which are non-factors in another environment: C++/CLI.
    Well, to be fair, we did miss out quite a few other environments -- Perl 6 running on Parrot, Forth running on a Commodore 64, Brainf*ck attempting to run on anything at all, and quantum computers running in the Van Allen Belt.

    In terms of the Java/C++ discussion, I think we did quite a creditable and non-flameworthy job. Perhaps when there's any distinct reason to mention C++/CLI, we'll get round to it.

    umm...:
    Overall, C++/CLI provides an excellent environment for programming .Net - and for Windows it's simply the best, hands-down. It's just surprising that so few people appear to be aware of it.
    I'm aware of it. Quite a lot of people are, strangely enough (and I believe that Herb Sutter might even be one of those people).

    To put it kindly, I'd suggest that it only has value (in a weird and inverse sort of way) as a "glue" language when you need to tie other .Net assemblies together with legacy (or otherwise) C++ code. In which case, I expect that you'd still need "pinvoke" anyway. But in all honesty, I don't really care.

  • (cs) in reply to Peter Lawrey
    Peter Lawrey:
    The longest generic type I have written has 119 characters for code in production. It could have been longer but that would have been really silly. :)
    Plz send me teh errer mesgz...
  • methinks (unregistered) in reply to real_aardvark
    real_aardvark:
    To RAII or not to RAII, that is the question?

    Look, Java does resource management one way (GC, no pointers, etc etc) and C++ does it another way.

    I can nothing but agree ;o)
    real_aardvark:
    Take yer pick (or other preferred operating system). I happen to find RAII considerably more elegant than a bunch of "finally"s stuck around the place and some botch-job of vanilla-flavour synchronisation built into the language itself (see Allen Holub for gory details).
    Well yes, it is more elegant. Hopefully, the Java designers will do something about it and of course due to GC it will never be exactly the same. But I think that there are good idioms you can use in Java e.g. with inner classes and real closures would (and hopefully will) add to simplicity here.

    However it is still harder (and I mean this in the good sense ;o) to create memory leaks in Java, and that is not because C++ does not deliver the tools like RAII but because of the fact that you can easily produce dirty C code disguised as OO code because it is wrapped inside a class (and admittedly you can even do that in Java, but arguably without lots of the side effect tricks stemming from C which are simply not possible) and do everything else instead of choosing the right construct for the job at hand.

    real_aardvark:
    Arguably, anything other than an endless paper roll and a pencil is syntactic sugar -- thus the typical futile arguments about "goto" that infest this site and many others. It depends on what you see as "syntactic sugar." So here goes:

    Syntactic sugar is a language artefact that makes little or no difference to the compiled/byte-coded output of the relevant AST.

    I see it in a different way, namely that "syntactic sugar" does not affect the functionality of the language in *source* - not purely in object code. I could just use machine code directly otherwise. You say...
    real_aardvark:
    Functions (in a type-checking language) are not syntactic sugar.
    (which is BTW the same I said in a previous post, opposing EvanD's opinion) But with your narrow definition this is not true, because the omission of functions does not make a difference in machine code.

    I'd say that perhaps basically everything else than lambda calculus and variable assignment is syntactic sugar ;o) which makes your point (that functions are not syntactic sugar) true all the more.

    But the sugar is not equally sweet on all levels - some constructs are really unnecessary (think e.g. of those languages which have "while" and "until" loops with only mirrored run and break conditionals), others make for the paradigm of a language (e.g. OO constructs)

    real_aardvark:
    Java generics are, and further suntactic sugar with the introduction of a typedef would therefore have been appropriate.
    D'accord.
    real_aardvark:
    Operator overloading sort-of is, and sort of isn't, because, as EvanED points out, it's hugely useful at the points where it intersects with generics (or indeed templates in general) ... for example, with iterators.
    Hugely useful, yes. But still only a different syntax and more easily misused or misunderstood - that was my main point.
    real_aardvark:
    It therefore boils down to whether you want to give the programmer the power to do this. In the case of C++ and generics, I don't think you have much choice. In the case of Java, I recognise that a conscious effort was made on the part of the language designers (who were sitting in a small sweaty room at a time where C++ generics were just a gleam in Stepanov's eye)
    *grin*
    real_aardvark:
    to simplify in this area. The end result, through no fault of their own, is that Java generics are a bit of a botch.
    Agreed. Mostly because of the fact that they are implemented as erasures.
    real_aardvark:
    And I rather like the idea that, in C++, it's easy to shoot your foot off. It's pretty bleeding obvious when you do so, too, and then it's time to hire a better C++ programmer.
    Amen brother. But that's the problem - it is often hard to find any hires at all, let alone really good or simply better than previous ones...
    real_aardvark:
    With due respect to your own knowledge of Java (and indeed C++), I don't agree with your argument concerning large systems teams in Java.
    Ah yes, that's the main point where we really differ.

    As I said above, if you have more constructs which can be misused, it will happen. All the time. Whenever and wherever possible. That's one of the lessons I really learned in quite some years. And that's the reason why I think Java is better in team environments.

    real_aardvark:
    If they were all in the top 20%, then, yes. Sadly, they're not. They'll leave just enough cruft and animal turds behind them when they leave for a more Enterprisey job to occupy those who are in the top 20% for the rest of their doomed, language-constricted little lives.
    Amen again... I see that we do in fact not differ much, except that we think that different approaches remedy for that.
    real_aardvark:
    Back to Lisp variants, I suspect.
    Count me in!
    real_aardvark:
    PS I think you're right about my use of μηδὲν ἄγαν, btw. I had something specific in mind when I inserted it, but then I took another shot of absinthe. (Well, one more can't hurt, can it?)
    Ah well, who really cares, it was already good fun to read ancient greek in a discussion on syntactic sugar :o)

    On my part I hope that I did not write a lot of bollocks in this post, I've only slept 2 hours %o] and it then gets increasingly easy to shoot yourself in the foot (above all linguistically) in a language that's not your mother tongue.

  • umm... (unregistered) in reply to real_aardvark
    real_aardvark:
    In terms of the Java/C++ discussion, I think we did quite a creditable and non-flameworthy job. Perhaps when there's any distinct reason to mention C++/CLI, we'll get round to it.

    Well, excuse me for living. I mentioned it because, as I said, it offers most of the 'C++' things, with the exception of const (for managed), that people were complaining were 'missing' from managed languages. But I'll try to make sure I get your permission before I post next time.

    real_aardvark:
    To put it kindly, I'd suggest that it only has value (in a weird and inverse sort of way) as a "glue" language when you need to tie other .Net assemblies together with legacy (or otherwise) C++ code. In which case, I expect that you'd still need "pinvoke" anyway.

    While partially true, that statement makes no sense in this context. Why would you only use it to expose native code to managed code, when you can just take care of the managed side right there. Your example assumes that you're going to create client assemblies which will reference this one, so why would you plan on writing them in a less-capable language? Unless, you don't miss typedefs, automatic variables, etc. - in which case, you are obviously not in the group which I was addressing.

    But in all honesty, I don't really care.

    Fine by me - sorry for wasting your precious, precious time.

  • (cs) in reply to methinks

    I've been away, sorry to ignore this nice debate for so long. ;-)

    methinks:
    You're not being fair here - if you want it to do something else, you have to rewrite your C++ code as well - just at another place in the code.
    No, only in one place. If I want to count bytes, the way I use the file object is exactly the same as if I want to do something else. I don't have to go into the fstream class and change the destructor.

    If you don't use the suggestion that the FilesProcessor becomes a generic class FilesProcessor<T> that returns a T, you need to modify FilesProcessor if you want to do something different. This is the part of the code that ensures that you close the file when you are done, the equivalent of the fstream destructor.

    If you do use the suggestion, then things look better, but at the same time you will often have to do an obnoxious amount of boxing and unboxing if you modify more than one variable.

    Otherwise:

    What you asked for was a solution to the problem that final local variables accessed from within a local (or local anonymous) class can't be changed.
    And I don't think that's nearly as good a solution as what you get in C++.
    int count = new FilesProcessor() 
       {  
          public void processChar(char c) { System.out.write(c); }
       }.processFiles(names).getCharCount();
    
    and
    String content = new FilesProcessor() 
       {  
          public void processChar(char c) { }
       }.processFiles(names).getContentAsString();
    
    Different getContentAs...() methods could cater for different conversion needs.
    I don't think I'd have thought of this; I do like it better the other suggestions. Though would you be able to get multiple return values without boxing them up into a single return value? (Again, Java's lack of either pass-by-reference or mutable boxed primitive types comes back to haunt.)

    I would still say that the C++ method is better; even with this you have to pull every value out of the inner class explicitly.

    But what I wanted to express is this: you do not have a "standard" version built into the system which behaves in a way everybody expects for each and every method - and then perhaps changes semantics in some but not in all cases.
    No, but you still have conventions. If I have a pseudo-numeric type that lets me write "a.add(b)", by convention that is returning their sum. (Never mind the fact that "a.add(b)" *still* looks to me more like "a += b" than "a + b", but that's another matter.)

    And Java doesn't have any mechanism for making your add() function not behave that way (because that would be pretty much impossible of course).

    Now, this sounds to me like I'm making a sort of argument that I hate which "we can't fix the problem [operations that behave unexpectedly] entirely, so might as well not make an improvement [disallow operator overloading]"... so I don't claim to be entirely consistent. ;-)

    I guess I would just say that I think that the benefits of being able to write more natural code ("a + b" no matter whether a and b are ints or complex numbers or vectors, rather than "a + b" if they are ints but "a.add(b)" if they are complex or vector types) outweigh the small loss in unexpectedness. In the C++ code I've worked with, I would say undoubtedly that operator overloading has increased readability rather than decrease it.

    From your later post:

    methinks:
    But with your narrow definition this is not true, because the omission of functions does not make a difference in machine code.

    I'd say that perhaps basically everything else than lambda calculus and variable assignment is syntactic sugar ;o) which makes your point (that functions are not syntactic sugar) true all the more.

    But the sugar is not equally sweet on all levels - some constructs are really unnecessary (think e.g. of those languages which have "while" and "until" loops with only mirrored run and break conditionals), others make for the paradigm of a language (e.g. OO constructs)

    I think you said what I was thinking better than I did. ;-)

    I think the reason this whole syntactic sugar discussion came up was that I really don't like dismissing language features [op overloading] solely on the basis of them being syntactic sugar, because I think that in some cases these can be extremely powerful things.

    Even I would not even think of saying that operator overloading is in the same class as functions in general. I would say that, in C++ at least, operator overloading is in the same class as function overloading.

    umm...:
    While partially true, that statement makes no sense in this context. Why would you only use it to expose native code to managed code, when you can just take care of the managed side right there. Your example assumes that you're going to create client assemblies which will reference this one, so why would you plan on writing them in a less-capable language?
    If you're asking what I think you are, Maybe you have legacy C++ code that you don't want to translate to C++.Net? Maybe you have a program that you maintain on multiple platforms and Mono isn't up to snuff?
  • Rhialto (unregistered) in reply to Andy
    Andy:
    In C, a typedef is a source level shortcut for a type that may be too complex to key in many times. Nice, but not really usefull unless you happen to define it exactly the same in several different source files. Of course, most programmers use an include file to increase the probabilty that it will be defined the same in each source file. The compiler doesn't care, it takes your word for it that each source file was compiled with exsctly the same typedef.
    That is not exactly true. The C++ compiler expands the definition of the typedef, and if types don't match in different modules, you get link errors, for all C++ compilers that I have ever tried. They often do this by encoding the full type in the name of the variable/function/etc.

    And using the same complex type multiple times is not so unusual anyway. If you're using, say, a Map<String, String> in several methods, you'll have to declare variables and parameters of that type several times.

    Not to mention the (in my eyes annoying) verbosity of the creation of the object in the first place: Map<String, String> = new SomethingMap<String, String>(); At least in C++, if you declare a map<string,string>, you have a map<string,string>, not a useless pointer to (n)one.

    Andy:
    In C, once the typedef is evaluated at compile time, it is assumed to be correct. It is not checked at link or run time. The compiled object files don't have enough information to check the types. I have gone through the debugging process when two modules thought the type was slightly different and just wrote into memory what they thought it should look like.
    Then you used a strange compiler. As I said, all C++ compilers I sort-of know do have methods to make sure the types match. This includes GNU G++ and MS Visual C++.
  • Rhialto (unregistered) in reply to Rhialto
    Rhialto:
    Robin Message:
    class PhoneBook extends map<string,string> {}

    It's slightly verbose, but it's basically a typedef.

    Except that you can't give this to a function that wants a map<string,string> as an argument. Such as a function that merges two maps. You will have to write your own copy of each such general function you wish to use. So it is not really a typedef, in that it isn't a synonym type at all.
    Oops, reverse that a bit: you can't use it properly with functions that return a map<string,string>.

  • ErikH2000 (unregistered)

    I've got sympathy for person writing the crazy generic definition. There is no "typedef" facility in Java and the workarounds for this limitation all have their problems.

  • (cs) in reply to ErikH2000
    ErikH2000:
    I've got sympathy for person writing the crazy generic definition. There is no "typedef" facility in Java and the workarounds for this limitation all have their problems.
    176 comments, and nothing on.
  • methinks (unregistered) in reply to EvanED
    EvanED:
    No, only in one place. If I want to count bytes, the way I use the file object is exactly the same as if I want to do something else. I don't have to go into the fstream class and change the destructor.
    I have to give you that... in fact I do think myself that this is one of the weaknesses of Java, I only tend to take the "advocatus diaboli" point of view, because I can't stand the myth-based Java bashing done mostly by people who do not know a bit about the language (and their own as well, at that ;o) You do not fit in that category, obviously :o)

    I still think that this weakness is partly counterbalanced by the virtues of implicit memory management which facilitates program correctness in certain regards. It may admittedly decrease resource management correctness if not used correctly - but which errors are more prevalent and more of a problem in typical C(++) systems - memory leaks or failures to close a file?

    There's obviously still room for improvement and there must be some good compromise for these issues and perhaps some lessons can be learned from C# here - C# as a language does have some very good concepts. The main reason why I do not like it is the obvious promotion of the well known MS business model (vendor lock in etc.) by the .net framework... I'm already expecting to get flamed for that by some other readers, but I can take that ;o)

    EvanED:
    If you *don't* use the suggestion that the FilesProcessor becomes a generic class FilesProcessor<T> that returns a T, you need to modify FilesProcessor if you want to do something different. This is the part of the code that ensures that you close the file when you are done, the equivalent of the fstream destructor.

    If you do use the suggestion, then things look better, but at the same time you will often have to do an obnoxious amount of boxing and unboxing if you modify more than one variable.

    Given modern IDEs I really do not see that as a very big problem, but you are right in stating that local variables are easier to handle in that case.
    EvanED:
    Otherwise:
    What you asked for was a solution to the problem that final local variables accessed from within a local (or local anonymous) class can't be changed.
    And I don't think that's nearly as good a solution as what you get in C++.
    int count = new FilesProcessor() 
       {  
          public void processChar(char c) { System.out.write(c); }
       }.processFiles(names).getCharCount();
    
    and
    String content = new FilesProcessor() 
       {  
          public void processChar(char c) { }
       }.processFiles(names).getContentAsString();
    
    Different getContentAs...() methods could cater for different conversion needs.
    I don't think I'd have thought of this; I do like it better the other suggestions. Though would you be able to get multiple return values without boxing them up into a single return value? (Again, Java's lack of either pass-by-reference or mutable boxed primitive types comes back to haunt.)
    Quite naturally, yes. But firstly, using encapsulated objects is (apart from needing a little more code) the much cleaner way to handle multiple return values. I assume that with "lack of (...) pass-by-reference" you mean primitive method/function parameters. Java objects are of course passed by reference, whether as parameters or return values. But I have to say that I think the use of (primitive) out- or in-out-parameters is one of the worst concepts carried over from procedural languages into OO. I think even in C it is much cleaner if function parameters are only used as in-parameters and multiple return values are wrapped in a struct, even when the standard libs do otherwise a whole lot ;o) Primitive (in-)out-parameters in OO languages are IMHO plain evil.
    EvanED:
    I would still say that the C++ method is better; even with this you have to pull every value out of the inner class explicitly.
    Quite, but with the above concept you will (in most of the cases) pull exactly one scalar value with the specialized method call - it is likely that one only needs one value (e.g. the file length OR the contents). Of course in case you need both, you have to do that little more work of (un)boxing.

    That the wrapper classes are immutable is one of the single most important design decisions in early Java - they already blew it with java.util.Date, and this leads to many subtle flaws when passing around mutable Date instances without defensively copying them (which is also more work). The same argument also speaks against the possibility to getting the addresses of primitives and passing the pointers around.

    EvanED:
    Now, this sounds to me like I'm making a sort of argument that I hate which "we can't fix the problem [operations that behave unexpectedly] entirely, so might as well not make an improvement [disallow operator overloading]"... so I don't claim to be entirely consistent. ;-)

    I guess I would just say that I think that the benefits of being able to write more natural code ("a + b" no matter whether a and b are ints or complex numbers or vectors, rather than "a + b" if they are ints but "a.add(b)" if they are complex or vector types) outweigh the small loss in unexpectedness. In the C++ code I've worked with, I would say undoubtedly that operator overloading has increased readability rather than decrease it.

    I see it just the other way round now, but I do have to admit that I did miss op overloading when switching from C++ to Java ;o)
    EvanED:

    From your later post:

    methinks:
    But with your narrow definition this is not true, because the omission of functions does not make a difference in machine code.

    I'd say that perhaps basically everything else than lambda calculus and variable assignment is syntactic sugar ;o) which makes your point (that functions are not syntactic sugar) true all the more.

    But the sugar is not equally sweet on all levels - some constructs are really unnecessary (think e.g. of those languages which have "while" and "until" loops with only mirrored run and break conditionals), others make for the paradigm of a language (e.g. OO constructs)

    I think you said what I was thinking better than I did. ;-)

    Thanks a lot. If I may deduce from your user name that you are an english native speaker, that's even a greater compliment, because I ain't ;o)
    EvanED:
    I think the reason this whole syntactic sugar discussion came up was that I really don't like dismissing language features [op overloading] solely on the basis of them being syntactic sugar, because I think that in some cases these can be extremely powerful things.

    Even I would not even think of saying that operator overloading is in the same class as functions in general. I would say that, in C++ at least, operator overloading is in the same class as function overloading.

    D'accord. This view is backed up by the fact that both concepts are called "overloading" after all ;o)
  • 111 (unregistered)
    Comment held for moderation.
  • some guy (unregistered) in reply to Ciaran McCreesh
    Ciaran McCreesh:
    No-one will ever need typedef operator overloading is clearly always evil there are no legitimate uses of multiple inheritance programmer-controllable memory management is pointless const is silly scope-controlled destruction doesn't help anyone a common base class for everything is good
    - nobody needs typedef in Java, as an otherwise empty class derived from the intended class does the same (you only need to be explicit about nondefault constructors). typedefs are only needed for languages using manually written header files. - operator overloading is clearly evil for any project of nontrivial size and age - manual memory management is stupid for code having a clear notion of scope (OOP or functional code with closures) - const methods would be silly and obsolete in Java, as it add no capabilities. A Java class implementing two interfaces (one const, one nonconst) does the same. Casting to from the const interface to the nonconst interface would be just as evil as a const_cast<> in C++ - scope controlled destruction is currently missing in Java, so you gotta use try/finally which is a bit more verbose. With anonymous classes and closures in Java 7 however, scope controlled destruction in the old C++ sense is obsolete anyway. - a common base class for everthing isnt bad at all if you are not struck in a bad case of C++-think.
  • huojia (unregistered)
    Comment held for moderation.

Leave a comment on “Very Specific Generics”

Log In or post as a guest

Replying to comment #:

« Return to Article