4
$\begingroup$

Some programming languages are implemented via a source code interpreter instead of a compiler, for example Python, PHP, Ruby and Perl, and these programming languages are very popular.

So, this approach has many pros, but cons of course too. Which are some pros/cons of interpreters?

$\endgroup$
14
  • 3
    $\begingroup$ While I agree with @Adám, I feel this post could be more focused. You could maybe talk about the pros-cons of one specific technique; the question seems very broad as is. That being said, I'm not the one who downvoted, so the actual reason may be different. $\endgroup$ Jun 21, 2023 at 10:35
  • 9
    $\begingroup$ Languages are not compiled or interpreted. Their implementations are. $\endgroup$
    – SK-logic
    Jun 26, 2023 at 7:42
  • 3
    $\begingroup$ @kouta-kun - impractical - yes, indeed, if the language is too dynamic, especially using dynamic scoping a lot, then you simply won't get any significant performance gains from compiling it. You still can compile it and get the same performance as interpreter does though. You can compile the said Python, see PyPy for example. There can be other reasons to compile, not just to get a better performance. $\endgroup$
    – SK-logic
    Jun 26, 2023 at 10:53
  • 3
    $\begingroup$ @kouta-kun Futamura projections says no, there is no language that cannot be compiled - at the very least you can always specialise an existing interpreter against a source code and it'll be an (inefficient) compilation, no matter what interpreter it is and how it works. $\endgroup$
    – SK-logic
    Jun 26, 2023 at 11:23
  • 3
    $\begingroup$ This question is based on a massive and common misunderstanding. Very few modern language implementations use source code interpreters. What is extremely common is that they are compiled to an intermediate form, and then the intermediate form is interpreted in some form of VM. That leads to a completely different set of pros and cons making the question ambiguous to the point of uselessness. $\endgroup$
    – IMSoP
    Nov 18, 2023 at 16:54

5 Answers 5

13
$\begingroup$

I'll try to answer from the perspective of a language implementer/designer and as a user.

I'll also try to answer as broadly and generally as possible, but disclaimer: these lists may not apply to specific languages and implementations. And these lists will not be exhaustive.

As a User

Pros

  • Better guarantees about behavior. An interpreter can more easily be written to make user code behave the same no matter what platform it runs on.
  • An interpreter is more likely to be available on many platforms. This is not a guarantee, of course, but it's much simpler to port a well-written C, C++, Rust, etc., interpreter than it is to write a backend for yet another platform, especially when many platforms are much the same (you may have x64, but on Linux and Windows) with subtle and not-so-subtle differences.
    • You can probably port the interpreter to a weird platform yourself. Related to the portability of interpreters, you might be able to port it yourself, as a user, should that be necessary.
  • Your code can often avoid a compile step. Another thing that is not a guarantee, but you may be able to skip compilation. This has many great effects:
    • Faster feedback loop for development.
    • No need to recompile when you update a library.
    • No need for a complicated build process. Okay, this one may not be true in some cases, but it does happen.

Cons

  • Performance sucks. The rule of thumb is that every layer of interpretation is one order of magnitude slower. This holds surprisingly well across many languages and implementations, so users basically have to assume their code will run 10x slower.
    • More memory consumption. Related to the above, when you run your program under an interpreter, you need memory for your program and for the interpreter.
    • Startup time can be prohibitive. Because this is something that had been a problem for several language implementations (JVM, Python, Julia, among others), I thought I'd better mention it specifically.
  • The interpreter is a runtime dependency. You now have to install the interpreter on every machine where the code will run. Have fun.
  • Existing developer tools don't work. You have to wait on the implementer or some driven soul to do it. Or do it yourself.

As an Implementer

Pros

  • It's much easier to make the language behave the same on diverse platforms. No special hacks for weird platforms if your implementation language is good enough or has good enough libraries. (Personal note: I implemented a library of two's-complement arithmetic in portable C that avoids UB. This is some of my secret sauce to make C "good enough.")
    • It's easier to define all behavior. Undefined behavior is bad, and it makes users unhappy. With an interpreter, you can more easily define behaviors in your language and eschew the excuse for poor design that undefined behavior is.
  • It's easier to test. With an interpreter, you're testing the same code on every platform modulo some small bits. With a compiler, you test a different backend for every platform.
  • Portability is as easy as you make it. You can make it easy to port your interpreter to more platforms. It all depends on you, not a finicky platform you don't control.
  • You don't need some general-ish AST format. You can use whatever data structures make sense instead of transforming them into SSA, CPS, or some other form of IR that can enable optimization.
    • You don't need to write optimizations for your language or to target a specific IR format. Related to the above, you can depend on the optimizations of your implementation language and/or avoid the need to use LLVM or some other pre-made optimizer versus writing your own optimizations, which are tricky to get right. (Note: as @SK-logic says, this is technically not required for compilers, and you could implement optimizations in an interpreter. What I meant was that users generally expect optimizations in a compiler because then the extra compile step is "worth it," but they don't expect them in interpreters. In fact, they often don't want them in interpreters because it slows down startup time even more, which loses the feedback loop advantage.)
  • It's easier to sandbox code in an interpreter. Of course, not every interpreter has sandboxing features, but it's still easier to do in an interpreter, and it is done at runtime, which means avoiding static analysis which is complicated, misses truly adversarial code, and had a lot of false positives. Honestly, I don't know why every interpreter doesn't have some sandboxing.

Cons

  • Performance sucks. Developers need a reason to use your language. Now, performance is not that reason.
    • It's easier to make things slower than needed. Related to the above, it's easier to write the interpreter in such a way that some or all operations are slow. Directly executing a custom AST? How much pointer chasing are you doing? I bet it's a lot.
  • You now have to convince end users to install an interpreter. Okay, they might have to install a compiler, unless developers ship binaries, but developers often do ship binaries to make install easy for the same reason you don't want to make end users install an interpreter: they want to reduce friction.
  • Existing developer tools don't work. Better make them work or implement them yourself, which is more work. And that just sounds so boring.
$\endgroup$
3
  • 5
    $\begingroup$ You're not really obliged to do any optimisations in a compiler either. And nothing stops you from having very complex and costly optimisations in an interpreter. $\endgroup$
    – SK-logic
    Jun 26, 2023 at 22:25
  • 2
    $\begingroup$ @SK-logic, you are correct, and I have added a note addressing that. $\endgroup$ Jun 26, 2023 at 22:36
  • 1
    $\begingroup$ Python is actually compiled first to bytecode, then executed by the Python VM. So, this optimizes startup and even allows statements to be simplified. Even so, Python acts like an interpreted language in many ways. $\endgroup$
    – Schilive
    Nov 18, 2023 at 15:39
3
$\begingroup$

In addition to what was said in the other answers:

You'd normally design a language with typically interpreted semantics, as in - some form of a dynamic scoping, dynamic dispatch (probably, duck typing), some form of managed memory, etc., in case if you need a scripting language. An interactive language for a command line interface, for example, or embedded scripting language to control the behaviour of some application.

In such cases interpretation makes sense - firstly, most statements will only be executed once, so no need to waste time compiling them. Secondly, such languages often require very tight interoperability with the functionality of the host system, while code may not be trusted and may require sandboxing. Some believe both are easier to achieve with interpretation.

Having said that, the line between compilation and interpretation is very blurred, all the languages mentioned in the question are in fact bytecode-compiled, and there's quite a few interactive REPLs built on top of natively compiled languages (some Common Lisp implementations, for example).

$\endgroup$
18
  • 3
    $\begingroup$ Very few languages have dynamic scopes ─ typically, any language with a concept of scope will have lexical scopes. Perhaps the distinction you want to draw is between block scope (as in C or Java) and function scope (as in Python or Javascript). $\endgroup$
    – kaya3
    Jun 27, 2023 at 14:21
  • 1
    $\begingroup$ Both Python and Javascript have dynamic scoping (alongside with rudimentary forms of static scoping). So does elisp, even Common Lisp, Tcl, Perl and many many more. $\endgroup$
    – SK-logic
    Jun 27, 2023 at 15:16
  • 1
    $\begingroup$ Python variables are lexically scoped by default, you have to use global and nonlocal to write to dynamically-scoped variables. Similar for Common Lisp; unless you declare a variable special it's lexically scoped. $\endgroup$
    – Barmar
    Nov 18, 2023 at 0:20
  • 1
    $\begingroup$ @Barmar Python scoping rules are weird, there's no nested lexical scoping within a single function definition, all variables share the same scope. I would not call it a proper lexical scoping. You can introduce a variable inside, say, an if branch and then use it outside. $\endgroup$
    – SK-logic
    Nov 18, 2023 at 9:19
  • 1
    $\begingroup$ @SK-logic Just because an if block doesn't introduce a new scope, doesn't make the scopes which do exist "dynamic". In Python, function definitions can be nested, and when they are, it is the lexically surrounding function which provides the parent scope, not the dynamic execution context. Nor is it particularly "weird" - there are probably as many popular languages using function scope as using block scope. $\endgroup$
    – IMSoP
    Nov 19, 2023 at 14:35
1
$\begingroup$

The ability to product self-modifying code, such as by producing strings that are then executed as if they are a piece of code of said language, are trivial to do in an interpreted language, but not so much in a compiled language.

There is a reason why Python has exec(), JavaScript has the Function() constructor function, and Lua has loadstring(), but C or C++ has no such equivalent. In a compiled language, the only way would be to force the language standard library to include a compiler for said language, which would bloat the language considerably. But in an interpreted language, the interpreter is already there so such constructs are trivially implemented as 'invoke the interpreter on the contents of this string.'

$\endgroup$
1
$\begingroup$

An issue I've not seen mentioned yet is that interpreters may be subdivided into multiple categories. Some receive input from a stream, individually processing commands contained therein. Some execute exclusively from data structures which must be pre-loaded before program execution starts. Some are capable of executing directly from a stream, or from objects constructed by the execution of previous commands. The first and third categories may be useful in cases where it may be necessary to start executing parts of a program before the entire program exists (or in some situations, where no "complete program" will ever exist, because each command will be jettisoned from memory after it executes).

Additionally, some interpreters are designed to process source text that would be created by a human or other entity that is designed to produce program source text to accomplish some particular objective, while others are designed to act upon programs which have been first fed through a "transpiler" that might convert e.g. x=y+z; into a sequence of bytes whose meaning might be described as e.g. "pushIntVariable #4; pushIntVariable #7; Add; pushAddressOfVariable #9; StoreInt". An interpreter given a such sequence of bytes could avoid having to spend time on things like symbol-table lookup, and thus run must faster than one which must fully parse everything every time it is encountered.

While some tools that are used to run programs are clearly "compilers", and others are clearly "interpreters", there are many that end up sharing characteristics of both.

$\endgroup$
1
  • $\begingroup$ Upvoted mostly for the last sentence. Dividing languages / implementations into two camps of "compiled" and "interpreted" stopped being useful decades ago. $\endgroup$
    – IMSoP
    Nov 20, 2023 at 18:00
0
$\begingroup$

PROS

  1. Quicker development. Obviously a good thing. There simpler and have dynamic typing which speeds up developemnt. They also have high-level data structures.
  2. Easier debugging. It is more interactive which allows for this. They can experiment with code snippets without a compile build run cycle thats separate.
  3. Simplicity. There is more straightforward syntax and expressive constructs. This is ovbiously good and makes it more readable as well as easy to learn.

CONS

  1. Worse performance. The code is translated and executed line by line at the runtime and this causes the performance hit. JIT improves it to some extent but not fully.
  2. Development complexity. While its simple to use it is quite hard to make. They might need language runtime or an interpreter.
$\endgroup$
3
  • 2
    $\begingroup$ Dynamic typing and late binding are two separate concepts. You can have static typing and late binding (e.g. methods in Java), or dynamic typing and early binding (e.g. local variables in Python). $\endgroup$
    – kaya3
    Jun 26, 2023 at 22:33
  • 2
    $\begingroup$ It's not clear to me that any of these points is fully accurate, if at all. Most of them don't seem to be related to interpretation, and one of the ones that is says "[an interpreted language] might need [...] an interpreter". At the least, the answer could benefit from adding explanation and justification to each of the claims. $\endgroup$
    – Michael Homer
    Jun 26, 2023 at 22:59
  • $\begingroup$ All of the pros can be applied to a compiled language, if designed properly. Dynamic typing may even slow down development by introducing hard to find type errors $\endgroup$
    – Seggan
    Jul 2, 2023 at 20:28

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .