Which language is the least bad?


  • Impossible Mission - B

    @bulb said in Which language is the least bad?:

    I don't really see why it has to be a base visitor class as opposed to visitor interface. And note that Rust traits can provide method implementations. (I failed to verify with quick search whether the same can be done in Go or not).

    I've already covered this with @Gąska. That works just fine until you need to maintain state in the walker.

    Except it was Smalltalk that introduced the term “OOP”, though the term “object” did come to it through Simula from earlier Lisp experiments.

    The term that became generally accepted did come from the Smalltalk community, but it was describing stuff that people had been doing since before Smalltalk ever existed. Smalltalk's implementation of it missed a lot of important points, which is why it's never been successful while Simula-style OOP has gone on to take over the world.

    Thinking about it more, that is not part of the definition of visitor pattern. Note that the (first, C#) example on that page has interface IExpressionVisitor and interface IExpression and no base classes.

    Because it's a very simple example. The most common use case for the visitor pattern is to walk ASTs, and for that you want the visitor style I'm describing. (And yes, you can have it described by an interface. This lets you implement multiple walker base classes that implement the basic interface but with different semantics, such as having one that just visits and one that maintains the necessary state for tree rewriting.)

    Keep in mind that the visitor knows the type of node it is visiting. So it knows about the subnodes and can, and usually does, control the recursion itself. I.e. instead of super(node) it can simply call Visit(node.Left) and Visit(node.Right) in the right places, possibly in the middle of the other logic.

    1. This means that Visit needs to be a method on the same class/struct/whatever-your-language-calls-it that implements the visitor.
      1a) If it's not, then your visitor needs to call an external Visit and pass itself in, which is messy.
    2. This requires your visitor to know about walking logic. Goodbye, Separation of Concerns.

    Now there are certainly cases where delegating to the base method is useful. But there are many alternatives for languages that don't support that. You could have auxiliary method VisitChildren(node) or node.VisitChildren(this) (the base Visit simply calls VisitChildren) or you could have a function or you could have OnOperator() that by default calls OnOperatorBefore(node); Visit(node.Left); Visit(node.Right); OnOperatorAfter(node); and override the right bit or… all of them qualify as visitor pattern.

    Yeah, I'm familiar with the before-and-after version. This still doesn't allow you to decide not to recurse into the child nodes. (You could add even more special cases for that, but your complexity grows and grows and grows...)

    Are you familiar with the history of OpenGL? The Khronos Group wasted a lot of time, and made their specification more and more complex and difficult for manufacturers to implement properly, trying to come up with many more different options for the fixed-function pipeline, while Direct3D had gone a different route and just introduced shaders, which let developers write out their graphics processing however they liked in arbitrary code. It was several years of D3D eating their lunch before Khronos finally came to their senses and implemented shaders for OpenGL.

    Giving developers the ability to call into the traversal in arbitrary code is, like shaders, really the only Right Way to do it.

    And no, calling VisitChildren(node) instead of super(node) is not more complicated. If anything it is simpler because it makes it clearer what it is actually doing.

    Which means that the visitor has to have direct knowledge of the walker. (Which @Gąska said is a bad idea.)



  • @gąska said in Which language is the least bad?:

    through custom preprocessor

    As in custom binary or as in procedural macro?

    Procedural macros (rust#38356) are likely to be finished before specialization (they are surely much more complete at the moment) and then you can do something like #[inherit(Base)] struct Derived { ... } (or maybe struct Derived { #[base] base: Base … }), which would fit the language quite well. And if they are not, you can always resort to the proc-macro-hack or procedural-masquerade using the already stable macros 1.1.


  • Impossible Mission - B

    @cvi said in Which language is the least bad?:

    Not sure on Windows. There was something on r/cpp recently were it was mentioned that Windows Exceptions (SEH) are (maybe?) used under the hood. But I'd guess that they are implemented similarly to the table-driven thing, i.e., they are also referred to as "zero-cost".

    Essentially, on Win32, SEH used the stack-based variety, and on Win64 it uses the table-based variety.


  • BINNED

    @masonwheeler The "only a handful" remark was meant to clarify there's no need to care about performance for the exceptional case. If you don't use them for flow control, they happen only exceptionally, so their performance is irrelevant. Only the non-thrown performance is relevant.

    That the compiler takes care of correctness when using RAII is a good thing, though.


  • Impossible Mission - B

    @topspin said in Which language is the least bad?:

    @masonwheeler The "only a handful" remark was meant to clarify there's no need to care about performance for the exceptional case. If you don't use them for flow control, they happen only exceptionally, so their performance is irrelevant.

    Unfortunately, that's not something you can always rely upon. I've seen two widely-used libraries that do just that. One got rearchitected to stop using exceptions for control flow in a later version, but broke compatibility and required a good deal of rewriting to update. The other never did stop.


  • Banned

    @masonwheeler said in Which language is the least bad?:

    The most common use case for the visitor pattern is

    ...message handling. Far more people handle messages than deal with ASTs.

    Just saying.



  • @masonwheeler said in Which language is the least bad?:

    This means that Visit needs to be a method on the same class/struct/whatever-your-language-calls-it that implements the visitor.
    1a) If it's not, then your visitor needs to call an external Visit and pass itself in, which is messy.

    Yes, on the visitor. You did have it there in your example. Though I don't really think calling node.Accept(this) is any more messy than this.Visit(node), especially since the later just calls the former anyway.

    @masonwheeler said in Which language is the least bad?:

    This requires your visitor to know about walking logic. Goodbye, Separation of Concerns.

    A lot of the time it needs to know it anyway. But when it does not…

    @masonwheeler said in Which language is the least bad?:

    And no, calling VisitChildren(node) instead of super(node) is not more complicated. If anything it is simpler because it makes it clearer what it is actually doing.

    Which means that the visitor has to have direct knowledge of the walker. (Which @Gąska said is a bad idea.)

    For better or worse, as I wrote it they are exactly equivalent as the methods are being called on the same object. I am just working around the lack of super by providing alternate name for the default implementation.


  • BINNED

    @masonwheeler
    Just to clarify, do you mean:

    1. they used exceptions for control flow / were heavily throwing exceptions and that hurt performance, so they tried to refactor.
    2. they used exceptions "rarely" as I described (e.g. I only really got a handler it main to print a user friendly error message why things broke), but not compiling with -fno-exceptions introduced so many RAII paths that it hurt performance.

    If 2) that sounds like a problem, if 1) I don't really care.


  • Banned

    @masonwheeler said in Which language is the least bad?:

    Which means that the visitor has to have direct knowledge of the walker. (Which @Gąska said is a bad idea.)

    Nothing is a bad idea unconditionally.


  • Banned

    @bulb said in Which language is the least bad?:

    As in custom binary or as in procedural macro?

    Custom binary. Macros aren't powerful enough.


  • BINNED

    @gąska said in Which language is the least bad?:

    As I've said in one of my earlier posts. I actually follow this quite closely because it's the most important missing feature that's blocking my attempt to add proper classes to Rust (through custom preprocessor).

    Haven't read everything above... Rust doesn't have classes??
    That seems like a big drawback for something I thought I might look for "powerful enough to replace C++ but actually sane".


  • Banned

    @topspin it has structs and traits. They cover 99% uses of classes, the remaining 1% being when you need field inheritance.


  • BINNED

    @gąska said in Which language is the least bad?:

    Nothing is a bad idea unconditionally.

    PHP? 🚎


  • BINNED

    @gąska I assume (without knowledge just guessing from the names) that structs are for fields and traits handle (polymorphic) methods? Don't structs do inheritance?

    (This post may make no sense at all depending on how much I misunderstood things)


  • Banned

    @topspin you're mostly on point. Structs don't have field inheritance because it's meaningless without methods, and method inheritance collides with type inference and trait resolution.



  • @masonwheeler said in Which language is the least bad?:

    Are you familiar with the history of OpenGL? The Khronos Group wasted a lot of time, and made their specification more and more complex and difficult for manufacturers to implement properly, trying to come up with many more different options for the fixed-function pipeline, while Direct3D had gone a different route and just introduced shaders, which let developers write out their graphics processing however they liked in arbitrary code. It was several years of D3D eating their lunch before Khronos finally came to their senses and implemented shaders for OpenGL.

    Eh. The Khronos Group was founded around that time; the OpenGL ARB on the other hand was busy doing nothing (they largely carried on with that for almost a decade). Vendors pushed out a ton extensions (yes, including the register combiners that you maybe refer to, and yes, those were terrible). The early shaders in DX also weren't that arbitrary, shader model 1.x pixel shaders supported like a grand total of eight arithmetic instructions or something.

    It took a lot of years of D3D eating their lunch before the ARB and Khronos got their thumbs out of their asses, even after GLSL had been released.

    Edit: The point being that you give Khronos/ARB way too much credit in your post. ;-)


  • BINNED

    @gąska said in Which language is the least bad?:

    @topspin Structs don't have field inheritance because it's meaningless without methods

    Does that mean I can't do the following? (really lame example but useful in general)

    struct Point2d {
        double x, y;
    };
    
    struct Point3d : public Point2d {
        double z;
    };
    

    method inheritance collides with type inference and trait resolution.

    Are methods still (optionally) polymorphic / dynamic dispatch?

    I know, I probably should just RTFM.


  • Considered Harmful

    @topspin Nope, you cannot in fact do that. Which is a good thing, because a 3D point should not ever be passed into a function asking for a 2D point. And fields should not be part of the public API. Methods, however, can be inherited, because of traits. And traits are polymorphic, and you can choose whether that's with static dispatch (via generics) or dynamic dispatch (via fat pointers).


  • Banned

    @topspin said in Which language is the least bad?:

    @gąska said in Which language is the least bad?:

    @topspin Structs don't have field inheritance because it's meaningless without methods

    Does that mean I can't do the following? (really lame example but useful in general)

    struct Point2d {
        double x, y;
    };
    
    struct Point3d : public Point2d {
        double z;
    };
    

    You know, you can just make a field.

    method inheritance collides with type inference and trait resolution.

    Are methods still (optionally) polymorphic / dynamic dispatch?

    Trait methods, yes. Both static and dynamic dispatch is supported for all traits automatically (no additional code needed; function author decides).

    I know, I probably should just RTFM.

    You should. It's very fun language!


  • BINNED

    @pie_flavor said in Which language is the least bad?:

    Which is a good thing, because a 3D point should not ever be passed into a function asking for a 2D point. And fields should not be part of the public API.

    A struct (well, depending on whatever the language calls them, a POD type anyways) doesn't have anything other than fields, so they are the public API.

    As I said, the example is pretty lame. Of course you most likely don't want to pass a Point3d to a Point2d by value, because of slicing.


  • BINNED

    @gąska said in Which language is the least bad?:

    I know, I probably should just RTFM.

    You should. It's very fun language!

    That's what I gathered from reading about it. I like the combination of statically enforcing correctness and native code.

    How powerful is the macro capability compared to C++ templates? Can it do crazy but nice meta-programming, such as expression templates for fast linear algebra?


  • Considered Harmful

    @topspin said in Which language is the least bad?:

    @pie_flavor said in Which language is the least bad?:

    Which is a good thing, because a 3D point should not ever be passed into a function asking for a 2D point. And fields should not be part of the public API.

    A struct (well, depending on whatever the language calls them, a POD type anyways) doesn't have anything other than fields, so they are the public API.

    In C++ maybe. In Rust, you may know that certain fields exist, but your access is no less restricted than if they didn't. Anything with pub is part of the public API. Public structs with public fields is widely considered doing it wrong; you should use methods or functions for everything.

    @topspin said in Which language is the least bad?:

    How powerful is the macro capability compared to C++ templates?

    Never used them. But I can tell you that comparing Rust macros to C++ macros is like comparing C to assembly.



  • @gąska said in Which language is the least bad?:

    @pie_flavor @Magus I think you'd both like the Rust way:

    if let Ok(i) = "123".parse() {
        DoSomethingWith(i);
    } else {
        // ...
    }
    

    If you want to handle error:

    match "123".parse() {
        Ok(i) => DoSomethingWith(i), // wrap in code block if different return types
        Err(e) => ReportError(e),
    }
    

    Alternatively:

    "123".parse().and_then(DoSomethingWith).or_else(ReportError);
    

    Or if you're going to return error on error:

    DoSomethingWith("123".parse()?);
    

    I don't like any of those. You're saying I'd have to learn four totally disparate ways of saying exactly the same thing, because any code I come across might use any of those four. I prefer @Magus's original tuple solution much better than any of these. It has the double advantage of being easier to read and easier to remember how to do when I'm writing it.


  • Considered Harmful

    @djls45 Well, the first and second stem from the same knowledge, which is pattern matching, and the third and fourth stem from API knowledge. No less work than Optional in Java takes.



  • @topspin said in Which language is the least bad?:

    A struct (well, depending on whatever the language calls them, a POD type anyways)

    I don't think struct means a “POD” type in any language since C.

    @topspin said in Which language is the least bad?:

    doesn't have anything other than fields

    structs may have methods in Rust and so they can in C++ and C#.

    @topspin said in Which language is the least bad?:

    so they are the public API.

    Besides, the unit of encapsulation in Rust (and Go; the type systems are kinda similar, well more similar to each other than older languages) is the module, not the type.

    @topspin said in Which language is the least bad?:

    How powerful is the macro capability compared to C++ templates? Can it do crazy but nice meta-programming, such as expression templates for fast linear algebra?

    You should be comparing templates with generics. They are a bit weaker (value arguments are not yet implemented, though they are planned), but not much. Certainly turing-complete. But since they have explicit constraints (like concepts in C++), they actually produce understandable errors.

    Macros are a different beast. There are two kinds. Macros-by-example and procedural macros. Macros-by-example are immensely more powerful, because they can do pattern matching and conditions—and are hygienic. While procedural macros are Rust functions that can do anything you want—and still have useful support for hygiene.



  • @zecc said in Which language is the least bad?:

    @masonwheeler said in Which language is the least bad?:

    LOB application

    How dare you use an acronym without defining it? How dare you?

    *sigh* It just doesn't have the same level of @blakeyrat vitriol. :P


  • BINNED

    @bulb said in Which language is the least bad?:

    A struct (well, depending on whatever the language calls them, a POD type anyways)

    I don't think struct means a “POD” type in any language since C.
    @topspin said in Which language is the least bad?:

    doesn't have anything other than fields

    structs may have methods in Rust and so they can in C++ and C#.

    Yes, I know, that's why I said POD is really what I meant. Lots of people still use structs to designate PODs in C++, even if the only difference is the default access.

    You should be comparing templates with generics. They are a bit weaker (value arguments are not yet implemented, though they are planned), but not much. Certainly turing-complete.

    Good enough, that's what I need. I assumed generics mean "generic containers but no meta-programming", thus thinking sane macros might work for that purpose.

    @pie_flavor said in Which language is the least bad?:

    But I can tell you that comparing Rust macros to C++ macros is like comparing C to assembly.

    Well yeah, because C++ macros are :doing_it_wrong: (even though they have their hack-ish use cases).



  • @Evo said in Which language is the least bad?:

    You should definitely NOT pick a subset of C++ (except in some cases such as performance-critical cases), and I'm not sure why you would suggest this even. I've heard this, but it usually comes from people who only know that particular subset of the language. That is, using the subset is a limitation of the coder, not a feature to make the code better.

    This probably describes my familiarity and like for C++. I basically know C++ as C with classes and a little templating (basically what would be used in the standard libraries), probably because I was into it only up to C++11, and then branched off into other languages in college. A lot of the new features that I've seen look like attempts to make it more like Java or C#, i.e. a high-level language, but that may be just my perspective.

    I would like to get back into developing in it more. I have a nice little (useful, I think) open-source project that I originally wrote in C++ and started to convert to C# that could probably use a little more TLC.


  • Discourse touched me in a no-no place

    @djls45 said in Which language is the least bad?:

    A lot of the new features that I've seen look like attempts to make it more like Java or C#, i.e. a high-level language, but that may be just my perspective.

    Some things are a good idea (auto, range-based loops) in that they make it much easier to write correct code and keep code quite a bit shorter for no loss of precision. The progression of for-each type concepts out of very high level languages into lower-level ones has been an interesting feature of programming language evolution over the past couple of decades…


  • BINNED

    @pie_flavor said in Which language is the least bad?:

    Public structs with public fields is widely considered doing it wrong; you should use methods or functions for everything.

    That's a reasonable default approach, but not something I'd generalize. I don't see the use in trivial getters/setters for POD-like structs. To continue the lame example:

    class Point2d {
        double x, y;
    public:
        void setX(double x_) { x = x_; }
        void setY(double y_) { y = y_; }
        double getX() const { return x; }
        double getY() const { return y; }
    };
    

  • Discourse touched me in a no-no place

    @topspin said in Which language is the least bad?:

    I don't see the use in trivial getters/setters for POD-like structs.

    It depends on the language. In Java, you can't easily take a reference to a field (there's a way to do it, but it's ugly and slow and everyone avoids it if they can) so exposing them is actually OK. After all, all you can really do is read it and write it, and that's what the operations you'd otherwise expose are.

    In C++, you can also take a reference or pointer to a field and transport that off somewhere else; the fundamental set of language operations is larger. That discourages the use of public fields.



  • @dkf said in Which language is the least bad?:

    The main problem with exceptions in C++ is that they make the assembly level rather more complex,

    As opposed to writing the code in such a way as to avoid the exception in the first place. Yes, that makes the code more complex, and yes, that's sometimes not possible, but AFAIUI, if you're concerned about the assembly, you're already at the level to best avoid exceptions, which mostly tend to just be coding shortcuts for "sloppy" resource management (e.g. out-of-manamemory errors, null pointers), availability of third-party resources (e.g. missing hardware, network glitches), and if you're really evil, business logic, like input validation (e.g. parse exceptions).

    So… exceptions in C++ now get about a 6/10 (they'd get one more if they didn't support throwing arbitrary types). That's much better than they used to be, but since they used to be terrible to the point where some programmers still say that anything is better than exceptions, that's not the highest bar to beat.

    I'd go the other way and say that having the option of using exceptions (even if they're bad ones) is better than not having exceptions at all.
    Also, why is throwing arbitrary types a bad thing?

    (Performance of exceptions? Performance of an error path? Is that really important?)

    If performance of the error path (and recovery, if possible) is a huge priority, then you might be better off just programming directly in assembly. (half-🚎 )


  • BINNED

    @dkf Many of the new things are a good idea, bringing high-level abstractions from other languages over to C++. The problem is the mind-boggingly ugly syntax they come up with to keep it backwards compatible. (range-based for loop being the exception, there's nothing to complain about there)

    If anything, if you'd want to restrict yourself to a "subset of C++" it should be "none of the old stuff". And certainly not longjmp. 🚎



  • @blakeyrat said in Which language is the least bad?:

    @evo said in Which language is the least bad?:

    The same holds for any other language: would you want an idiot who can't design code to use inheritance everywhere it could possibly be used?

    I'd prefer clear code over "using inheritance everywhere it could be used." For example, maybe I have two POCOs (plain old C# objects) that go out as responses to various things, one to a REST (representational state transfer) request, one to be emailed to an admin. Both of these POCOs have a field labeled "success", but other than that they have absolutely nothing in common, and in fact live in different class libraries.

    Based on your sentence there, what I should do is create a third class library, put in it a POCO named something like "ResponseToSomeKindOfRequestThatHasASuccessBool", put my success bool in it, then go to the REST and email libraries, add a reference to it, and subclass it for their two response POCOs. Yay I'm using inheritance!

    Oh but look at how much complexity I added, when I could have just typed public bool Success; twice.

    I think you're violently agreeing with him there.



  • @pie_flavor said in Which language is the least bad?:

    @gąska said in Which language is the least bad?:

    No I think I've been wrong in at least one case.

    SpectateSwamp?

    No, that guy's a genius. For someone to be able to maintain the level of complication of his codebase in his head in order to be able to add more features to it so quickly, it's a wonder he's not running some massive empire someplace. 🏆


  • Banned

    @djls45 it seems I've been missing a lot of fun. Can someone give me a quick recap?



  • @Gąska It's documented in Nobody shares knowledge better than this and I think another thread or two.

    Basically, SpectateSwamp's Desktop Search (SSDS) is a truly amazing piece of software. It's written in Visual Basic 6 5, and makes veryextremelyhorrendouslyspectacularly liberal use of goto. A few TDWTF locals have attempted to engage with his code, and can provide more information on what they found, if you can get them to stop gibbering madly about fell beasts, the Apocalypse, and eldritch abominations.

    Really, you don't even need to read the whole thread. The first and last page or two should suffice.



  • @masonwheeler said in Which language is the least bad?:

    This is simply not true. That's its selling point today. But "zero-cost abstractions" is a code word for "templates", and those weren't even suggested until the language was several years old; they weren't (pseudo-)standardized until 1990, when it had already been around for more than a decade.

    That just says it started by adding features for object-oriented programming inspired by Simula. Which it did. But that does not say anything about selling point.

    And the selling point was zero-cost abstractions (or maybe more precisely “you only pay for what you use”), as confirmed by Bjarne Stroustrup himself near the beginning of this presentation. If he wanted to use OOP, he could have just used Simula. The problem was all the allocations and virtual dispatch made it too slow with the memory allocator technology of the day (slab allocator would not be heard of for 15 more years), so it was important to allow handling objects by-value to be able to put them on stack and compose them in a single allocation.

    So it was not really OOP per-se that was the reason for C++, it was always about abstractions that only cost anything if you actually use them.

    And no, zero-cost abstractions is not specifically about templates. It is the general design principle behind all of C++.


  • 🚽 Regular

    @djls45 said in Which language is the least bad?:

    @zecc said in Which language is the least bad?:

    @masonwheeler said in Which language is the least bad?:

    LOB application

    How dare you use an acronym without defining it? How dare you?

    *sigh* It just doesn't have the same level of @blakeyrat vitriol. :P

    They say copying is the sincerest form of flattery.
    I considered the implications.


  • ♿ (Parody)

    @djls45 said in Which language is the least bad?:

    Basically, SpectateSwamp's Desktop Search (SSDS) is a truly amazing piece of software. It's written in Visual Basic 6,

    No. It's VB5.


  • Discourse touched me in a no-no place

    @topspin said in Which language is the least bad?:

    And certainly not longjmp. 🚎

    Nobody's claiming that longjmp is nice. I've seen a few legitimate uses in my career of poking around in obscure places, but they were always in places where there was literally no other way to do it. Doing SEH with a compiler that doesn't support the syntax for it (via some nasty pseudo-inline assembly code) is nicer than longjmp


  • BINNED

    @dkf It comes with the same caveat as writing your own crypto: don't. If you're qualified to break the rule, you know you are.


  • :belt_onion:

    @topspin said in Which language is the least bad?:

    @dkf It comes with the same caveat as writing your own crypto: don't. If you're qualified to break the rule, you know you are.

    ... and even if you "know" you are, consider that you may not be.


  • Discourse touched me in a no-no place

    @heterodox said in Which language is the least bad?:

    ... and even if you "know" you are, consider that you may not be.

    I guess I might be qualified to do it… but I also know I really don't want to!



  • @djls45 @Gąska I am still having nightmares from when I tried to parse it..

    Oh, BTW, he posted an updated link last week, in case you were interested in experiencing the madness yourself. Just remember @SpectateSwamp's rules:

    • take a noodle and jam it!
    • indices are evil, use a file with a list of all the file paths instead, which is totally not an index, reeeeally
    • nesting is for the birds, eagles specifically, which fly across the page
    • read code after I wrote it? Naaah...
    • it's not safe to goto alone, take this joint


  • @masonwheeler said in Which language is the least bad?:

    @scholrlea No, I don't remember that. Link?

    I meant to get back to this earlier, I haven't looked for the relevant references yet, but I can tell you what I understand about it. I did find this history page, which does discuss Christopher Alexander and his development of architectural patterns, and their influence on the GoF.

    My understanding is that Kent Beck and Ward Cunningham started out on this road when trying to find a technique for refactoring old programs, either in place or (more often) transliterating them into Smalltalk - most of the programs were legacy mainframe applications for which they no longer had a working compiler. They found that a lot of of the same idioms kept reappearing in different places, which jogged the memory of one of them (Cunningham, I think) about Alexander's books. They started using the techniques described in A Pattern Language and found that they worked well for describing programs as well as buildings.

    That is how I recall it, at least. I need to go back and find where I saw this.



  • Clipper and dBase got so old nobody is mocking it. I remember people used to forget to list an index when opening a .dbf file, and the index always getting out of sync.



  • @sockpuppet7 said in Which language is the least bad?:

    Clipper and dBase got so old nobody is mocking it. I remember people used to forget to list an index when opening a .dbf file, and the index always getting out of sync.

    That must be what was happening with a program I used to use. I tried to help the developers identify bugs/etc, but gave up. ("No! The indices are corrupted because of XP's filesystem!" (literal quote) They also took bug reports as personal attacks on them. I gave up.)

    edit: I got used to running del indexs\* often. Yes, that's how they spelled that directory.
    edit2: I actually wrote a C++ dbf wrapper to dump the data. Well, and "crack" their rot13 "encryption". Yes, you heard that right.



  • @dcon I remember that in my region almost no programmer seemed to be aware you have to put all indexes on the "USE" command, and almost all programs required constant reindexing of everything.



  • @sockpuppet7 said in Which language is the least bad?:

    Clipper and dBase got so old nobody is mocking it. I remember people used to forget to list an index when opening a .dbf file, and the index always getting out of sync.

    I would not be so sure of that:

    Alive and kicking...


Log in to reply