Slashdot Mirror


User: EvanED

EvanED's activity in the archive.

Stories
0
Comments
6,434
First seen
Last seen
Profile
(view on slashdot.org)

Comments · 6,434

  1. Re:Why not just do duck typing? on Bjarne Stroustrup On Concepts, C++0x · · Score: 1

    In any case, my Java example wasn't about variables at all.

    This is true; I should have said "expressions" instead of "variables".

    Technically, the only real difference is that Java guarantees that you will get an exception when you try to treat an object "wrongly" ... On the other hand, C specification doesn't actually preclude the implementation from doing full type checking on such casts as well.

    To me that's like saying "the only difference is exactly the difference you were talking about". Sure, an implementation could do such type checking, but I dare you to find me one in anywhere near common use that actually does. CCured is about the closest thing I know of.

    Practically speaking, C doesn't do said type checking, and the difference exists.

    (Put another way, Java is type-safe from this perspective. C is not type-safe even if a particular implementation is. Type safety to me means a guarantee, and since the C standard doesn't make such a guarantee, it's not type safe. Since Java's spec does make such a guarantee, it is.)

  2. Re:Why not just do duck typing? on Bjarne Stroustrup On Concepts, C++0x · · Score: 1

    That's not particularly difficult to fake, if you're willing to do a little maintenance.

    I'd dispute the characterization as "a little".

    If your project uses a common base class for whatever reason (not terribly out-there; e.g. Qt defines the QObject class), you could even put one implementation of callMethod in that base class and have the same interface everywhere. That's very different than "you have to update callMethod() each time you add a class."

    This has the added benefit of preventing people from calling functions on that class that you don't want them to call - using reflection, they could in theory call any member function of the class, regardless of whether it's part of your scripting API, unless you explicitly filter for it in your callMethod function.

    If your language supports annotations, you could annotate the function explicitly; this is preferable IMO because it co-locates the specification that the function should or should not be called with the function, instead of having them in different locations.

    (Okay, so maybe my argument is really that reflection + custom annotations would be a great and very useful addition. Reflection on its own I could see as rather less useful.)

    Of course, if your functions take different numbers and/or types of arguments, you might have to handle each individual function separately anyway, making the whole reflection thing moot.

    Why? You could use the reflection API to get the number and types, and do conversions based on that. I can think of a not-very-complicated way to implement this with reflection once (i.e. it would still work with my "put one callMethod() class in your base" statement above).

    Bottom line is, I don't think reflection would really add anything to the language that you couldn't fake with some ugly and/or elegant code (at least as far as your example goes).

    You can say the same thing about classes. Why should C++ have classes? After all, you can simulate classes with structs and explicit function pointers. Personally, I think the syntactic gains you get from supporting classes is around the same as the syntactic gains you'd get from real language-supported reflection over some hacked-up solution like that.

    The problem with the above is that I'm only thinking of what the difference would be with a program that uses reflection somewhat heavily. The fact that classes are used all over the place means that the gains in terms of code effort are much higher with classes. So I'm not arguing that reflection is as worthwhile as classes, just that the syntactic gains are, I think, comparable.

    Besides, that was just one example of two and a half. The second (and I think third, but don't count that) relies on enumerating the members of a class.

  3. Re:Why not just do duck typing? on Bjarne Stroustrup On Concepts, C++0x · · Score: 1

    Yes, this isn't the same as treating raw bytes of object in a wrong way, but in terms of type safety violation it's still breakage.

    That's only if you take the position (as I occasionally do, but only occasionally) that types are a property of a variable and not a value. That's basically the strictest possible interpretation, and to take it would mean that you think that Python, Scheme, etc. aren't typesafe. There's not any real difference between the sort of runtime checking that dynamically-typed languages do and the sort of runtime checking that Java does on a downcast from a type safety point of view.

    In both cases, either the compiler or runtime system enforces that the globs of memory that correspond to objects are treated in a way that is consistent with their interface; this is something that you don't get with unsafe C casts. (And incidentally you also don't get it if you overwrite those bytes by going off the end of an array, which is why memory-safety violations can be considered type-safety violations from a certain point of view.)

  4. Re:Why not just do duck typing? on Bjarne Stroustrup On Concepts, C++0x · · Score: 1

    Er, you said it yourself - there's "type safety" and there's "memory safety". Those are two entirely orthogonal concepts, and I'm not aware of anyone who's into PL design (professionally or otherwise) who would mix those.

    I would say that lack of memory safety implies a lack of type safety.

    This is probably just a matter of definition of "type safety"; I'm likely being more picky about what counts as that than you are. I definitely think that they are not at all "entirely orthogonal".

    Well, Java lets you do unsafe casts as well.

    Java's unsafe casts are entirely different than C's unsafe casts. Downcasts are still checked at runtime. In C you can cast a double* to a int* and actually read the bytes of the double as if it were an int; there's no way to do that in Java, and such operations are most decidedly not type-safe.

    With that in mind, consider that C#, for example, also has raw pointers with arithmetic and unsafe casts. I don't see how it makes it any more type-unsafe than Java for the end-user (i.e. your average developer).

    This is certainly true. There is definitely a spectrum from "type safe" to "type unsafe". Something like ML is basically entirely on the "type safe" side; something like C is pretty darn close to "type unsafe". (Implicit casts between pointer types and a couple other things that C++ fixes completely break C's type safety IMO.)

    Java is most of the way across towards "type safe" and C# is just a little less safe.

  5. Re:Why not just do duck typing? on Bjarne Stroustrup On Concepts, C++0x · · Score: 1

    I think I mostly agree with basically everything you said. That particular example of objects in O'Caml seems very closely-aligned with what I would call 'static duck typing', though I don't know much about O'Caml specifically. I have done rather a bit of SML programming though; with the part of type inference that fits in that language's model (i.e. no objects), I would say they are related but still sufficiently different that I would still distinguish.

    There's just one more comment I have, which is about your statement that "[O'Caml] doesn't error out at the point where the function tries to use the member that's not available - it fails immediately at the function call." If anything, I think that templates are a little closer to the dynamic notion of duck typing for exactly that reason -- if the same code were written in a dynamic language, the compile-time type-checking error becomes a run-time error. But it will be an error at the site of the call to the missing function rather than the call to f -- in which case the backtrace corresponds almost exactly to the "instantiated from" stack that the C++ compiler gives. ;-) (That's not to say that the O'Caml compiler error is worse than C++'s, just that templates are closer to the dynamic notion of duck typing on that count. In fact, the goal concepts was to bring C++ errors closer to what you'd get in O'Caml in that example.)

  6. Re:Runtime vs. compile-time checking on Bjarne Stroustrup On Concepts, C++0x · · Score: 1

    If a compiler can inline blorz for a particular call, and that call is for a specific instance with well-known type (i.e. not itself a reference), then it can definitely do a non-virtual call.

    That doesn't surprise me; what I don't think any of them will do is, when compiling blorz on its own, create specialized versions. (E.g. it'd be theoretically possible to do something like:

    blorz(B const & obj) {
        if (obj is actually a B) {
            obj.foo(); // inlined B::foo();
        }
        else if (obj is actually a D1) {
            obj.foo(); // inlined D1::foo();
        }
        else if (obj is actually a D2) {
            obj.foo(); // inlined D2::foo();
        }
    }

    (Assuming D1 and D2 are the only subclasses of B. You could add a default case though.)

    This could give a benefit if the body of blorz was bigger and the benefits from inlining and enabled optimizations from that were greater than the cost of the checks.

    I should have been more clear.

    (By contrast, I think JIT compilers will do stuff like this. They might notice that blorz is almost always called with a D1 object, create a compiled and optimized version for that, and add a dynamic check for it.)

  7. Re:Why not just do duck typing? on Bjarne Stroustrup On Concepts, C++0x · · Score: 1

    Can you give an example of where C++ is "not static enough" to be type-unsafe?

    I'm not the original poster, but many PL people would consider memory safety violations as breaking type safety. This includes buffer overruns, multiple frees, etc.

    You can get around using arrays in C++ with strings and vectors most to almost all of the time, but usually not all the time. And vector's [] operator isn't bounds checked anyway with most implementations not in debug mode; you have to use .at() to be safe.

    There's also an argument that the fact that you can even do unsafe casts means that C++ isn't fully type-safe. (Especially because it's probably too easy to be in a situation where it seems like a cast is safe but it actually isn't.)

  8. Re:Why not just do duck typing? on Bjarne Stroustrup On Concepts, C++0x · · Score: 1

    But those useful things are usually due to the common inheritance in Java (i.e. given a list of objects, do something different with the objects depending on what type it is).

    There are still plenty of things that you could use them for that don't have anything to do with that:

    It would be much easier to write a simple scripting language to interface with the core of your program if the core is written in a language with reflection. Right now you basically have to write or generate a bunch of wrapper functions, so that if in your scripting language you say "a.foo()", there is a translation layer that eventually calls "foo" on object "a". If C++ had reflection, you could write a "callMethod()" function in "a"'s class, pass it "foo" as a string, and just use reflection to call it.

    This example requires annotations too, but assume that you can annotate functions and access those annotations from the reflection API. One of my favorite command line argument processing engines I've seen is Mono's GetOptions library. (So super-duper better than getopt and variants it's not even funny. There is at least one other technique, shown in Mono.Options and a Boost library and Python, that give GetOptions a run for its money though. I'm not sure which I like better.) Basically you write a class that has a field for each option you want (I'm not sure if it will let you call functions; there are some improvements that can be made while keeping the idea the same), and you annotate each field with what command line option should set it, what should be accepted as arguments to that option, etc. You then call some parse function with the actual argv array. The parse function uses reflection to look through the object you give it to find the fields with annotations, looks at the annotation to get the option, the looks through the command line for that option. If it's present, it sets the field accordingly.

    About 8 years ago I was an intern at IBM, and was programming a Java Servelet for the summer. One of the other interns on my team (we were a team of 3 interns plus some supervisors) pushed for using Java Apache Struts for the project, and we did that. In general I'm unhappy when I see Java have support for things that are harder to do in C++ (if I were to program in a language in the statically-typed C-like language area, C++ would be my clear choice; D is the only one I've seen that I would consider much), so I thought about what you would need to do to write basically a C++ version of Struts. The only part of it that I think would be hard to duplicate for a C++ library was some part that used reflection. (Not to say it wouldn't take a lot of time. I'm certainly not going to do it.) Unfortunately for the life of me I can't remember what it was though.

    None of these require adding or removing methods from an object, and none of them require classes inheriting from some base type. Templates somewhat take the place of the latter problem.

  9. Re:Runtime vs. compile-time checking on Bjarne Stroustrup On Concepts, C++0x · · Score: 1

    Right; I know that. If your post was meant as a response to me (to dispute something I said or something like that), I think you just misunderstood what I meant. For instance, in C++ consider the following code:

    class B { virtual void foo() { ... } };
    class D1 : public B { virtual void foo() { ... } };
    class D2 : public B { virtual void foo() { ... } };
     
    blorz(B const & obj) {
      obj.foo();
    }

    All I'm saying is that you can't resolve the call to foo inside blorz, because it could be any of B::foo(), D1::foo(), or D2::foo(). Unless you generate code for blorz that is specific to the dynamic type of obj (and I have no reason to think that compilers do this), the call could be to any of the three functions so you can't inline it or do any of the other fancy stuff.

  10. Re:Why not just do duck typing? on Bjarne Stroustrup On Concepts, C++0x · · Score: 1

    Both of those can be very useful without being able to change what methods are available on the fly. For instance, Java doesn't let you modify the available methods of an instance (at least AFAIK; I'd be astonished if you could), but still has reflection capabilities that are occasionally very useful and let you do things you couldn't otherwise.

  11. Re:Why not just do duck typing? on Bjarne Stroustrup On Concepts, C++0x · · Score: 1

    It's a marketing term, nothing more. It adds nothing to the actual discussion, other than to sound all neat and cutsey.

    As another poster has said, giving something a name has merit too. I mean, look how much the Gang of Four's Design Patterns has been cited, and how influential it was. They themselves said they didn't actually invent anything, but merely described existing practice and gave it a name. And now most programmers know what a singleton is.

    "A language like Smalltalk or Python or Ruby doesn't have class definitions in the same way as Java and C++ do"

    Of course they do. Every single language you listed is strongly typed. The only difference is that Smalltalk, Python, and Ruby are dynamically typed, while Java and C++ are statically typed. That's it.

    No they don't. Python, Smalltalk, and Ruby can (I think; I know Python can) add and remove methods dynamically. About the most you can say is that an object specifies its own class definition.

    If I have a python definition of a class and create an instance obj then add a method foo to it, where is the definition of that class plus method foo? There isn't one. It's implicit in the code.

    By contrast, in C++, if I have an object, I know that the interface it supports is listed in the source relatively locally. I know that somewhere there is a class definition that contains (with its inheritance ancestors) exactly that object's interface.

    Put another way, in general the shortest description an object's interface in Python is the list of methods it supports, even if you have the source code. The shortest description of an object's interface in C++ is the name of its class.

    "Doesn't have a class definition" != "not strongly typed". (Not that I think "strongly typed" has much meaning. See here, at the message a few down written by Mark-Jason Dominus.)

    Duck typing specifically necessitates binding at runtime.

    I'm not sure I agree. I think describing templates as "compile-time duck typing" conveys a lot about how templates behave in a reasonably accurate manner. (I do think that the arguments about compile-time being run-time for the templates are a bit of a red herring.)

    I mean, yes, in some sense "compile-time duck typing" is an oxymoron, but that doesn't mean it can't be a useful term. I think the essential aspect of "duck typing" is less the "dynamic" part and more the "no explicit interface specification" part.

    And that's a *bad thing*. Because if the object doesn't support the method, the compile will generate the templated code, the code will fail to compile, and the compiler will emit an exceptionally cryptic error. This is the whole reason Concepts were invented...

    Yes, I know that.

    In fact, in the past I've described concepts as being the template analogy of a class definition, in that they explicitly specify what types support the interface a template expects. So "class definition : object :: concept : class".

    (Incidentally, I say this fits right into what we're saying here. Explicit immutable class definitions are the opposite of duck typing for objects, so templates that used concepts would be not duck-typed.)

  12. Re:Why not just do duck typing? on Bjarne Stroustrup On Concepts, C++0x · · Score: 1, Informative

    First, what you young bucks like to call 'duck typing', as cutesy as that sounds, the computing science world calls 'dynamic dispatch'.

    That's not true.

    First, 'duck typing' is actually a reasonably-standard term. I would define it as "the operations your object supports are not defined a priori". A language like Smalltalk or Python or Ruby doesn't have class definitions in the same way as Java and C++ do; instead, an object just has some methods, and if it supports the method you're trying to call it works, otherwise it fails. C++ templates are duck typing -- they're just compile-time duck typing. For instance:

    template <typename T>
    void foo(T & a) {
      a.bar();
      return a + 1;
    }

    This function imposes an interface on T: it needs to support a zero-argument bar function and operator+ with an int argument (or something int can be converted to), be it a member or free function. But there is nowhere that interface is actually specified; it's implicit in the function. For any type T that supports those two operations, you can instantiate foo with that type. This is what is meant by "if it looks like a duck and quacks like a duck, it's a duck."

    By contrast, you can have dynamic dispatch in a statically-typed language. C++ does: virtual methods. Virtual method dispatch is dynamic dispatch, even if it's through a statically-defined interface that produces a vtable.

  13. Re:Runtime vs. compile-time checking on Bjarne Stroustrup On Concepts, C++0x · · Score: 4, Informative

    It is disputed whether the more direct dispatch outweighs the overhead of loading the duplicated instructions into the CPU's instruction cache.

    Keep in mind that if you can statically determine what function is going to be called, you can potentially inline it. This is important for the stereotypical object-oriented designs where you have a lot of small functions (like getters and setters). This eliminates not just the function call overhead entirely, but will often enable other optimizations. (Of course, virtual functions make determining the function to be called hard or impossible in many cases.)

    So it's not just the increased call overhead that dynamic languages have to deal with, it's the lack of ability to perform a bunch of other optimizations as well. Given C++ templates vs. a true interpreted language, I would wager C++ templates would win virtually every time, I-cache pressure and all.

    However, not all is lost; JIT compilers start introducing a lot of the benefits from static typing back into the dynamic context once it decides to for-real compile part of the code. Once it does that, dynamic languages can pick up most of the benefits of the static language. (And my understanding is sometimes more, because the JIT compiler has some dynamic information while the static compiler has to make safe assumptions.) Of course, not all languages have true JIT compilers (e.g. Python (specifically CPython, the typical implementation) is bytecode-interpreted with no JIT compiler).

  14. Re:Why not just do duck typing? on Bjarne Stroustrup On Concepts, C++0x · · Score: 1

    Neither duck typing nor introspection requires variable-sized structs.

  15. Re:Why not just do duck typing? on Bjarne Stroustrup On Concepts, C++0x · · Score: 2, Informative

    Other people have said that that breaks C++'s static typing, but what they didn't say is that static typing is one of the explicit design goals of C++.

    Some quotes from The Design and Evolution of C++:

    "The importance of static type checking was also strongly emphasized. In other words, C++ follows the Simula rather than the Smalltalk model of inheritance and type checking."

    "This delayed type-error detection [as present in Smalltalk or Python] was considered unacceptable for C++."

    "However, the most fundamental reason for relying on statically checked interfaces was that I was -- as I still am [at least 1994] -- firmly convinced that a program composed out of statically type-checked parts is more likely to faithfully express a well-thought-out design than a program relying on weakly-typed interfaces or dynamically-checked interfaces."

  16. Re:paint.net? on Best Free Open Source Software For Windows · · Score: 1

    Strange ... drawing a straight line in the Gimp is a pretty straightforward (pardon the pun) thing to do, all you need is the paint tool of your choice and the shift key.

    Sure. Once you know how to do it. In retrospect it even makes a lot of "sense"... a lot of programs use shift or something similar to lock the modification in some way. Make a line only horizontal or vertical, resize an object keeping aspect ratio, etc.

    But even I'd never think to try it to see what happens.

    Even if you can't figure it out intuitively, the interface tells you down the bottom when a paint tool is selected "(try Shift for a straight line, Ctrl to pick a color)".

    The version I have installed doesn't. Granted, it's now quite out-of-date (2.2.13, current is 2.6; mine is 3 years old), so this is still a relatively new feature. I didn't realize my version was that old. (Ah the joys of RHEL.)

    Anyway, if you want to draw straight lines, a vector graphics program like Inkscape is probably going to be more your scene.

    Maybe. Depends what you're doing. There are plenty of times you might want to draw a line on an existing picture or something like that.

  17. Re:Worth the wait. on StarCraft II Delayed Until 2010 · · Score: 1

    You can tell when gamers don't know anything about network software implementation.

    You can always tell when /. posters don't have enough imagination about how you could implement something.

    I'm being somewhat facetious here; I really don't know much about networking. (Though the "gamer" label barely applies. I actually still do a few hours of Starcraft a week with a friend, but not a whole lot else. I'm playing Mass Effect now, but I'm having a hard time thinking of any game I've bought myself since shortly after The Orange Box came out.) (I also know I've used BitTorrent through a NAT without opening ports on the router, at least that I know about.)

    What I do know though is a good bit about Starcraft (though I'm not all that good of a player; almost certainly no better than D-class on iCCup). I know that Starcraft was the 10th best selling PC game in the US in June. And I am pretty sure that the popularity of Starcraft in the competitive gaming scene, e.g. South Korea's pro gamers, is a major driving force for why it remains so popular. And as I said in another post, I can think of few things that Blizzard could do that would cripple competitions more than making network games go through Blizzard servers. And this means that: (1) Blizzard has a bunch of stupid people making decisions and will require all traffic to be external, (2) Blizzard will be releasing a special version of the game or server for these high-profile competitions (but then what will they practice on, how will they figure out who gets the special version, etc.), or (3) Battle.Net will only mediate

  18. Re:Just who do they think they are anyway? on Apple Balks, Finally Relents, At Possible User Queries of Dictionary App · · Score: 5, Insightful

    Who appointed Apple to be the legal guardian and nanny of iPhone users?

    To be fair, the iPhone users did.

    Which is why I don't have an iPhone.

  19. Re:paint.net? on Best Free Open Source Software For Windows · · Score: 5, Insightful

    Is there a reason, other than complexity of interface, that one might choose it over gimp.

    "complexity of interface" is a pretty damn good thing to base a decision on.

    I suppose gimp does not have all the shapes of a drawing program, but it does paint, with colors.

    When you have to look up documentation to figure out how to draw a straight line in the Gimp, and that documentation is somewhat condescending, you might start to think that the Gimp isn't actually that good for simple tasks.

  20. Re:GIVE US LAN BACK on StarCraft II Delayed Until 2010 · · Score: 1

    (replying to myself to finish a thought) ...it could still be far better than going all the way to Blizzard's servers.

    Though admittedly still far worse than staying entirely local.

  21. Re:GIVE US LAN BACK on StarCraft II Delayed Until 2010 · · Score: 1

    When the P2P packets are sent out, they will be sent to the IP address that was registered on Battle.net when everyone logged in - a public address.

    Maybe. Or maybe Starcraft will ask Windows what it thinks its IP is, and pass that to Battle.Net, which will use some sort of heuristic decision to determine if it looks like a LAN game.

    I have no idea how it will work when it finally comes out. You could very well be right. But unless you're violating an NDA, I think it's a pretty safe wager to say you don't know how it's going to work either. All I'm saying is that using internet traffic isn't necessarily what this is going to do even if they hold to their current plans.

    Most people will still end up using internet bandwidth for each person even for LAN if that is the case.

    Further, even going to your ISP's router is probably a short (and fast) hop compared to going to Battle.Net, so even if it does step out of your LAN for a second, it could still be far better than going all the way to Blizzard's servers.

  22. Re:GIVE US LAN BACK on StarCraft II Delayed Until 2010 · · Score: 2, Insightful

    Now if I'm going to have an 8 man LAN in my garage, it's all gotta go through battlenet, sucking up my bandwidth?

    That's not a necessary conclusion. Blizzard already uses P2P stuff for, e.g., the Blizzard downloader; it's very possible that Battle.Net will only mediate such connections at the beginning, then drop out.

    From my chair, Blizzard would be utterly stupid to require that LAN play go through their servers. Starcraft 1, a decade-plus-old-game, was the 10th best-selling PC game in June in the US. I'm sure that the SC2 announcements have helped over the past months, but one of the main reasons it has such holding power is that it's such a popular competition game. Look at South Korea's pro gamers.

    You think that Jaedong or Flash or Boxer are going to be happy if their competitions have any chance of being disrupted by a little excess lag (remember, these are players that have 300 to 400 actions per minute sustained for a 20 minute game and peaking higher), or Blizzard's servers going down, or anything like that? Hell no. I suspect there are few things that Blizzard could do that would be more likely to cripple SC2 on the pro gaming scene than what you suggest. And that means that (1) Blizzard has a bunch of stupid people making decisions and will require all traffic to be external, (2) Blizzard will be releasing a special version of the game or server for these high-profile competitions (but then what will they practice on, how will they figure out who gets the special version, etc.), or (3) Battle.Net will only mediate.

    Which is it going to be? I don't have a crystal ball. It could be any of the three. But I think that assuming that it will be #1 is a big assumption.

  23. Re:Worth the wait. on StarCraft II Delayed Until 2010 · · Score: 1

    Oh and even if you have your friends over, your game will lag by all of you having to use Battlenet from one connection

    This isn't a necessary conclusion of that decision. It's entirely possible that Battle.Net will mediate the connection then drop out; for LAN connections, this could mean it's still kept local. Blizzard already uses P2P for the Blizzard downloader for instance.

  24. Re:Guy doesn't work at a college, obviously on 20 Years of MS Word and Why It Should Die a Swift Death · · Score: 1

    It's lighter than a printed journal paper (or not, depending on the page count, but it's close).

    14 oz is not bad; that's light enough to fit my request. That said... WTF kind of journal papers to you read that are nearly a pound? :-)

    We're not talking a whole proceedings here, just a single article printed. A 500-paper ream of 20lb paper is 5lb; you'd need 175 pages (88 sheets) to get to 14 oz. That's a good 13 or 14 conference papers. ...it's big enough that you can easily read a paper which uses the standard LaTeX margins on A4 or US letter paper

    What about double column format with narrow margins though? I mostly-randomly pulled out 10 papers from a filing cabinet drawer; 8 were typeset in this format. 1 was typeset in single-column format with relatively narrow margins, and 1 appears to have been "typeset" in Word, with inch or inch-and-a-half margins. There are a few papers in LNCS format (single column, wide margins), but these are by far the exception.

    Looks like the same company (iRex)'s DS1000S has a larger screen (10.2") and isn't much heavier. It's still almost 30% smaller dimensionwise than a letter paper though.

    You also say that it's as easy to read as newsprint, but newsprint still doesn't have the contrast of standard printer paper.

    I'm still not convinced this satisfies "easy to read" for academic articles. It's close though... and the fact that they got the wacom in there is quite impressive. In a few years I think things may be different, but I think paper is still a pretty clear winner for me for now.

  25. Re:PDFs? on 20 Years of MS Word and Why It Should Die a Swift Death · · Score: 1

    A whole variety of tax submissions are now provided as PDFs that start out as complex, interactive forms with a variety of UI widgets, listviews, pop-up help, self-calculating fields and such and - when submitted back to the tax overlords (from within Acrobat Reader, without any browser involved) - become cryptographically sealed, non-editable, printable records of the data collected.

    Wisconsin's state income taxes were like this too. I had to upgrade Acrobat Reader to be able to file my taxes that way. (The alternatives were paper or pay TurboTax $10 or something.)