1. 12
  1.  

  2. 21

    Evaluating your own code can still be very useful. Some examples include: interpreting JSON data

    Nooooooope. No. No. No.

    While I generally agree with this article, I strongly disagree with everything said about eval- which wasn’t much. Code should never be passed around as string-ly typed data. Not to parse JSON, not to evaluate HTML templates, not to handle mathematical expressions, and… if you need to use eval to detect whether or not a feature is available, how can you use that feature in your code later anyway?

    1. 4

      The example is pretty bad..

      But I’m not sure I understand complaining about eval existing in interpreted languages as a feature.

      1. 2

        I got great utility out of eval just today. In 8 lines of python, I wrote a local testing system for aws lambda/gce compute functions/azure function code. The program reads in argv, with each argument being http_route:file_path:handler_func_in_file, slurps in each file, then attaches the handler func to a flask route. This program saves me enormous effort of deploying code to a cloud provider before knowing if some basic functionality works. My productivity increased dramatically because of eval. You can black label anything you want, but you’re just shooting yourself in the foot.

        1. 4

          You don’t need to use eval for that. You can access globals with strings, which is far safer.

          1. 2

            safety has 0 priority for this use case. it’s lube on the machine that spits out the hard fast product, not the hard fast product itself. your abstractions sit on top of a mountain of mis-aligned interfaces that do things far more evil than eval. they are ready for abuse without much trouble. s/eval// doesn’t keep you safe.

            1. 2

              Safety has 0 priority, if and only if, this script is disposable. If I intend to use this more than once, I want to write something that isn’t going to hose my entire environment.

        2. 2

          Agreed. Language-theoretic security and ‘data as code’ is applicable here.

          1. 2

            if you need to use eval to detect whether or not a feature is available, how can you use that feature in your code later anyway?

            By using a fallback to that feature. Though, truth be told, you should probably just be using that fallback anyway in the first place.

            1. 1

              if you need to use eval to detect whether or not a feature is available, how can you use that feature in your code later anyway?

              Two cases that come to mind:

              1. lazy loading of additional modules (JS), you might get code size gains with some features and might want to load the smaller code if possible
              2. for statistical purposes, to know what your users’ browsers support
            2. 10

              Exploring the features and idioms of a language is always a rewarding bit of study, but I disagree with some of the suggestions here.

              But there are a few practical cases where it [goto] leads to code that is maintainable and easy to read.

              While I agree that there’s good use for goto in low-level, resource-intensive code, the given examples of error handling are more neatly expressed with Maybe, Either, and monadic composition in general.

              In this example, Swimmer and Flyer are completely separate abstractions, so inheriting from both is not going to lead to a mix-up of responsibilities in the child class [FlyingFish].

              I’d like to see an interface for these three classes. The ones I imagine include things like can_fly? or a generic move that are going to be implemented by whichever parent class was inherited from last. If the three are used at some polymorphic code site, at least FlyingFish is going to constantly jump through hoops deciding which of its parent class’s implementations it should be reusing.

              I think the MysqlDriver example mentioned would be a much stronger case, but it’s probably an example of mixins rather than multiple inheritance - if Queryable and Persistent couldn’t be used in place of MysqlDriver, they’re mixins.

              Eval

              Good call on eval being too dangerous for use. The examples of when people want to use it are why parsers should be a familiar, lightweight tool in every language’s toolbox. The example of detecting functionality is a nice new trick on me, and maybe even a place where eval might actually be the right tool for a job.

              Performance-wise, it’s generally best to convert recursive functions to their iterative versions.

              If your compiler doesn’t do this conversion, get a lazier one. :)

              1. 5

                I think he meant tail recursion rather than recursion. As mentioned there, but not too clearly, you will see recursion used all over the place when processing trees, no matter what the language is. Very few people will rewrite their tree algorithms with an explicit stack.

                Though one place I recall it being done is in re2 [1]. If I recall correctly, parsing the regular expression is done with a stack instead of recursion so you can accept untrusted regular expressions from users. (It was developed for Google Code Search). Although I wonder why you couldn’t just limit the stack depth with a test.

                (There is no recursion involved in executing/matching the regular expression.)

                Agreed on multiple inheritance and eval. Eval is almost always a workaround, although I suppose it’s useful to have mechanisms for workarounds. Feature detection is a good example.

                I’m working with some old Python code right now with multiple inheritance, and I plan to take it out.

                Goto does have some limited uses, even if you have exceptions. re2c compiles regexes to gotos, which is a natural and efficient use of the program counter as state.

                (re2 and re2c are completely unrelated, despite the similar names and both dealing with regexes…)

                [1] https://github.com/google/re2

                [2] http://re2c.org/

                1. 5

                  Although I wonder why you couldn’t just limit the stack depth with a test.

                  One possible answer to this is that it’s hard to know exactly what the depth is. Even if you know how much stack you’re using on each recursive call, you’d also need to check against whatever the stack size is. Are there easy cross platform APIs to access that info?

                  (There is no recursion involved in executing/matching the regular expression.)

                  This is also notable because there are several places in execution where an explicit stack is used in lieu of recursion.

                  1. 1

                    Chicken Scheme makes extensive use of the stack to allocate objects. It assumes a default stack of 1MiB, with the option to adjust the size of the stack on invocation.

                    IIRC one can also modify the stack size during the build; it’s a feature one makes use of doing performance testing on the GC. It’s easy enough to detect how much stack you’re using by taking the address of a local variable and computing the difference between it and another variable you declare further up the stack. The platform difference to account for is whether the stack grows up or down.

                    1. 2

                      Can you detect the stack size limit at runtime?

                      1. 1

                        getrlimit with RLIMIT_STACK will get you the stack size of your main thread, and you can set the stack size of a new thread with pthread_attr_setstacksize. Signal handling, which by default uses the stack of the thread it is handled with, can also be given separate stack space to use in advance so that it won’t cause stack overflow.

                        1. 2

                          I understand it can be done. Sure. But that doesn’t sound cross platform to me (although I suppose you could get a pthread compatible API working on Windows), and putting the stack on the heap sounds a heck of a lot easier. :-)

                          Maybe a better question in this context is: does the C++ standard library itself provide any cross platform facilities to query the stack size and do appropriate depth checks?

                          1. 2

                            The production code I see dealing with the stack (Chicken Scheme, Python) just assume a stack size, let you set a parameter when that assumption is wrong, and leave it at that.

                  2. 2

                    Recursion is a neat way of specifying things, but often to get tail recursion you have to make it less neat and split it into two functions.

                    ie. A client API and a tail recursive helper.

                    The other thing people often get wrong with recursion in OOPS is the notion of class invariant.

                    A class method has the responsibility to enforce the class invariant, ensuring it holds before and after invocation.

                    Conversely, often the class invariant is a precondition for correct execution of a public method.

                    However, it can “take the invariant apart” during the execution, provided it puts it back together before returning.

                    If a naive programmer recurses he can end up executing a public method while the class invariant doesn’t hold.

                    Usually subtle Bad Things ensue.

                  3. 4

                    https://lobste.rs/s/vnaih0/goto_multiple_inheritance_eval Comment thread from last time (a differently formatted version of) this article was posted.

                    1. 2

                      Are those forgotten?

                      Multiple inheritance is at the core of Ruby (though Modules).

                      eval is used in many popular scripting languages, at least as a core primitive, but sometimes at the loading stage of many frameworks.

                      Recursion is very popular and the the thing to use in many functional languages.

                      1. 6

                        I think any post calling recursion “forgotten” or obscure is not worth being the top post on Lobsters.

                      2. 2

                        Curiously enough, apart from some deep scars from having once maintained FORTRAN II code… I’m not ideologically opposed to goto’s.

                        I go with whatever results in cleanest / most readable code.

                        Yet I can’t remember when last I wrote a goto.

                        As I say, I’m not ideologically opposed to them… I just very very seldom need them.

                        1. 1

                          re goto. In my 4GL, I just macro’d things to make something clean that looks kind of like goto but converts the code into regular code that is easy to analyze. However, if using C, I looked for alternatives with readability of goto but advantages of stricter control. I lost my best one but here’s another I saw on HN that would be readable and has good control of frees:

                          https://news.ycombinator.com/item?id=9828068

                          Note that in your examples on error and conditional handling you could also just call a function.

                          re eval for parsing. What!? No, just use a parser generator that outputs safe, static-as-possible code. You might even want to isolate it using any available methods from rest of code. I used to keep them in dedicated, deprivileged processes then check their inputs. Parsing is both very dangerous and has proven, fast methods for handling it. Eval shouldn’t be used for that. For HTML templates, you can use either macros or generator libraries depending on your language. Will probably be faster, too.

                          Multiple inheritance section seemed good. Recursion I still don’t understand or use so no comment. Except that I was told The Little Schemer taught it really well. Plan to read it when tackling that topic.