Skip to content
Print

Defining High, Mid and Low-Level Languages

27
Feb
2008

I’ve been writing quite a bit recently about the differences between languages.  Mostly I’ve just been whining about how annoying it is that everyone keeps searching for the “one language to rule them all”, the Aryan Language if you will.  Over the course of some of these articles, I’ve made some rather loosely defined references to terms like “general purpose” and “mid-level” when trying to describe these languages. 

Several people have (rightly) called me out on these terms, arguing that I haven’t really defined what they mean, so I shouldn’t be using them to try to argue a certain point.  In the case of “general purpose language”, I have to admit that I tend to horribly misuse the term and any instances within my writing should be discarded without thought.  However, I think with a little bit of reflection, we can come to some reasonable definitions for high-, mid- and low-level languages.  To that end, I present the “Language Spectrum of Science!” (cue reverb)

Language Spectrum of Science 

This scale is admittedly arbitrary and rather loosely defined in and of itself, but I think it should be a sufficient visual aid in conveying my point.  In case you hadn’t guessed, red languages are low-level, green languages are high-level and that narrow strip of yellow represents the mid-level languages.  Obviously I’m leaving out a large number of languages which could be represented with equal validity, but I only have a finite number of pixels in page-width.

The scale is also somewhat myopic.  It defines Ruby as the highest of the high-level languages.  Very few could argue the other side of the scale since there’s not really anything lower than the hardware, but claiming that Ruby is the most high-level language in history seems somewhat odd.  In truth, I picked Ruby as the super high-level language mainly because it’s a) more dynamic than both JavaScript and Perl, b) more prone to RAD frameworks like Rails and c) it’s the most significant high-level language which I’m really familiar with.

It’s also important to note that languages aren’t really points on the spectrum, but rather they span ranges which are more or less wide, depending on the capabilities.  These ranges may overlap considerably (as in the case of Java and Scala) or may be entirely disjoint (Assembly and Ruby).  In short, the scale is somewhat blurry and shouldn’t be taken as a canonical reference.

Low-Level

Of all of the categories, it’s probably easiest to define what it means to be a low-level language.  Machine code is low level because it runs directly on the processor.  Low-level languages are appropriate for writing operating systems or firmware for micro-controllers.  They can do just about anything with a little bit of work, but obviously you wouldn’t want to write the next major web framework in one of them (I can see it now, “Assembly on Rails”).

Characteristics

  • Direct memory management
  • Little-to-no abstraction from the hardware
  • Register access
  • Statements usually have an obvious correspondence with clock cycles
  • Superb performance

C is actually a very interesting language in this category (more so C++) because of how broad its range happens to be.  C allows you direct access to registers and memory locations, but it also has a number of constructs which allow significant abstraction from the hardware itself.  Really, C and C++ probably represent the most broad spectrum languages in existence, which makes them quite interesting from a theoretical standpoint.  In practice, both C and C++ are too low-level to do anything “enterprisy”.

Mid-Level

This is where things start getting vague.  Most high-level languages are well defined, as are low-level languages, but mid-level languages tend to be a bit difficult to box.  I really define the category by the size of application I would be willing to write using a given language.  I would have no problem writing and maintaining a large desktop application in a mid-level language (such as Java), whereas to do so in a low-level language (like Assembly) would lead to unending pain.

This is really the level at which virtual machines start to become common-place.  Java, Scala, C# etc all use a virtual machine to provide an execution environment.  Thus, many mid-level languages don’t compile directly down to the metal (at least, not right away) but represent a blurring between interpreted and compiled languages.  Mid-level languages are almost always defined in terms of low-level languages (e.g. the Java compiler is bootstrapped from C).

Characteristics

  • High level abstractions such as objects (or functionals)
  • Static typing
  • Extremely commonplace (mid-level languages are by far the most widely used)
  • Virtual machines
  • Garbage collection
  • Easy to reason about program flow

High-Level

High-level languages are really interesting if you think about it.  They are essentially mid-level languages which just take the concepts of abstraction and high-level constructs to the extreme.  For example, Java is mostly object-oriented, but it still relies on primitives which are represented directly in memory.  Ruby on the other hand is completely object-oriented.  It has no primitives (outside of the runtime implementation) and everything can be treated as an object.

In short, high-level languages are the logical semantic evolution of mid-level languages.  It makes a lot of sense when you consider the philosophy of simplification and increase of abstraction.  After all, people were n times more productive switching from C to Java with all of its abstractions.  If that really was the case, then can’t we just add more and more layers of abstraction to increase productivity exponentially?

High-level languages tend to be extremely dynamic.  Runtime flow is changed on the fly through the use of things like dynamic typing, open classes, etc.  This sort of technique provides a tremendous amount of flexibility in algorithm design.  However, this sort of mucking about with execution also tends to make the programs harder to reason about.  It can be very difficult to follow the flow of an algorithm written in Ruby.  This “obfuscation of flow” is precisely why I don’t think high-level languages like Ruby are suitable for large applications.  That’s just my opinion though.  :-)

Characteristics

  • Interpreted
  • Dynamic constructs (open classes, message-style methods, etc)
  • Poor performance
  • Concise code
  • Flexible syntax (good for internal DSLs)
  • Hybrid paradigm (object-oriented and functional)
  • Fanatic community

Oddly enough, high-level language developers seem to be much more passionate about their favorite language than low- or mid-level developers.  I’m not entirely sure why it has to be this way, but the trend has been far too universal to ignore (Python, Perl, Ruby, etc).  Ruby is of course the canonical example of this primarily because of the sky-rocket popularity of Rails, but any high-level language has its fanatic evangelists.

What’s really interesting about many high-level languages is the tendency to fall into a hybrid paradigm category.  Python for example is extremely object-oriented, but also allows things like closures and first-class functions.  It’s not as powerful in this respect as a language like Scala (which allows methods within methods within methods), but nevertheless it is capable of representing most elements of a pure-functional language.

As an aside, high-level languages usually perform poorly compared with low- or even mid-level languages.  This is merely a function of the many layers of abstraction between the code and the machine itself.  One instruction in Ruby may translate into literally thousands of machine words.  Of course, high-level languages are almost exclusively used in situations where such “raw-metal” performance is unnecessary, but it’s still a language trait worth remembering.

Conclusion

It’s important to remember that I’m absolutely not recommending one language or “level” over another for the general case.  The very reason we have such a gradient variety of language designs is that there is a need for all of them at some point.  The Linux kernel could never be written in Ruby, and I would never want to write an incremental backup system in Assembly.  All of these languages have their uses, it’s just a matter of identifying which language matches your current problem most closely.

Comments

  1. Nice article. I think Scala can become higher level with support of metaprogramming. It is supposed to be implemented using Java reflection AFAIK. It may decrease performance (however I’m very curious how would it compare to let’s say Ruby) and will decrease refactoring abilities (for Java refactoring can be also unreliable when reflection is used). On the other hand Scala’s features make metaprogramming not so commonly needed. I hope that it is possible to modularize the areas where static typing and dynamic generation of code are used. What do you think about it?

    jau Wednesday, February 27, 2008 at 2:34 am
  2. It would be interesting to recreate the graphic with ranges (as you suggest) rather than just arrows for the locations of the languages. C and C++ for instance, might have 80-90% overlap and would be fairly broad. Scala too would be broad, with a tail extending up toward the higher level languages.

    On thing that makes this difficult is that “high level” is really a collection of different features, and is difficult to simplify down to a simple one-dimensional scale. However, that simplification is exactly what makes this chart so useful.

    Michael Chermside Wednesday, February 27, 2008 at 3:16 am
  3. People will argue all day about whether such-and-such feature really makes a language low-, mid-, or high- level, but I think the value from any such classification system is that it lets you better visualize what a language is capable of and good for. Being universally accepted as a classification system will probably never happen anyway. In those regards, I like your hierarchy.

    As for high-level language lovers being fanatic, I don’t really see that. I’d comment further, but I have to go by a new mouse. My old one was mysteriously destroyed when I blacked out after seeing Python was absent from your graphic…

    Jason Adams Wednesday, February 27, 2008 at 6:22 am
  4. I think the problem with any characterisation of languages like this suffer by defining each language as a point. Most languages are capable of covering a range — as you note with C and C++. With C++ you can get close to higher order functions etc. — pushing it well up into the high level space.

    Perhaps a better example of this range is C#. With the CLR’s help it can directly manipulate memory and use pointers (in unsafe blocks). At the same time it can do higher order functions (much of LINQ does this). Including the ability to dynamically build ASTs and execute them (System.Linq.Expressions).

    However a range is perhaps not enough, since the quality of fit also varies. E.g. C# can manipulate raw memory, but it is harder work than in C/C++ so that is a poor fit, however useful to avoid having to integrate multiple languages).

    Richard Wednesday, February 27, 2008 at 8:29 am
  5. @Jason

    lol I knew there was one I was forgetting. :-) IMHO Python would probably fall right about where JavaScript is: high-level, but not as dynamic as Ruby is (open classes are harder, meta-programming requires special constructs, etc).

    @Richard

    C# would actually cover about the same range as Scala. It’s amazing how many of the “innovative” features in Scala were inspired by C# 3.0 (they freely admit this on the Scala website). As for the ASTs bit, case classes and pattern matching allow this almost as trivially as LINQ, but with more power (see the Scala tour example of case classes for a sample).

    Higher order functions don’t exactly make a language high-level. For example, you can technically have higher order functions in Assembly (by passing the memory address of a label), but that hardly makes it high-level. It’s a worthy point though that many languages have characteristics of alternative classifications.

    @jau

    I’ve been working on some crazy stuff recently with Scala (things which are normally associated with metaprogramming in other languages) and I’ve yet to hear the call of annotations (Scala’s only meta construct) and have only minimally used reflection. Scala’s more flexible syntax, type inference and closures literally by-pass most of the use-cases for metaprogramming in languages like Java or even Ruby.

    Daniel Spiewak Wednesday, February 27, 2008 at 11:13 am
  6. Thanks for a nice post! :-)

    Although it’s not the main point you’re trying to convey in your post, I’d be glad if you would elaborate on how high-level languages increases flexibility in algorithm design “to the point of allowing certain NP-complete problems to be solved in polynomial time”. That would sound like a Turing award right there. :-)

    Also, I’d say that “dynamic programming” more commonly refers to a problem solving method used in algorithms rather than the “use of things like dynamic typing, open classes, etc”.

    Henrik Jernevad Wednesday, February 27, 2008 at 12:05 pm
  7. Most of your bullet points are incidental correlates, not really part of the definition of language levels. Per the definition, it’s just a gradient between expressing what you want done and expressing how to do it.

    In fact, Scala (and possibly Javascript?) is definitely on the other side of Ruby.

    Greg M Thursday, February 28, 2008 at 1:33 am
  8. @Greg

    I would be interested to find out why you think Scala is a more high-level language than Ruby. Given that it has almost no metaprogramming capabilities and is extremely static in nature, I find it hard to see the language as “high-level”.

    Yes, they are incidental correlates, but that’s exactly what I was looking for. Language definitions do very little good when attempting to classify an existing language based on experience.

    @Henrik

    *embarrassed smile* It would seem I need to look up my terms before I use them in an article. It seems you’re right about dynamic programming. The bit about polynomial NP-complete problem solving comes from the Wikipedia article on the Traveling Salesman problem. If you read that article without the knowledge that “dynamic programming” refers to a method of mathematical problem deconstruction, you can see how I came to the (incorrect) conclusion that Ruby could be an enabling factor in the quick solution of NP-complete problems. :-)

    Anyway, I’m correcting the article to remove the erroneous information.

    Daniel Spiewak Thursday, February 28, 2008 at 8:35 am
  9. Can anyone place Delphi on that chart?
    Where it may be located?

    Nanu Tuesday, April 28, 2009 at 7:13 am
  10. > It can be very difficult to follow the flow of an algorithm written in Ruby

    I agree with that. Not only the programming part should be simple in high level languages but also the design part. The flow should be easier to follow.
    Anyway, I prefer lower level languages because they offer full power. For example you can access the hardware directly from a low level language: http://soft.tahionic.com/download-hdd_id/index.html

    Hid Tuesday, April 28, 2009 at 7:18 am
  11. Delphi i believe might go in between java and scala, and C# (c sharp) would most likely be in between scala and perl or in between java and scala as well.

    Izzy Sunday, January 10, 2010 at 1:05 pm
  12. your article was good. but I confused a bit that you are saying that C is low-level language means it is easy to understand by the systems not by the humanbeings but nowadays all are written programs in c. I think it is easy to write and read means it is a higher level language. please justfy about this.

    ch.akshara Wednesday, July 21, 2010 at 2:00 am

Post a Comment

Comments are automatically formatted. Markup are either stripped or will cause large blocks of text to be eaten, depending on the phase of the moon. Code snippets should be wrapped in <pre>...</pre> tags. Indentation within pre tags will be preserved, and most instances of "<" and ">" will work without a problem.

Please note that first-time commenters are moderated, so don't panic if your comment doesn't appear immediately.

*
*