Let’s make a language – Part 9a: Prepositional phrases (Intro)

We’ve made quite a bit of progress with our languages since the beginning. They’ve got nouns, pronouns, verbs, and adjectives. We’ve given them plenty of grammar to connect these bits and turn them into sentences. But those sentences are pretty bland. And that’s because there’s no real way for our conlangs to express anything but the most basic of semantic relationships: subject and object. To remedy that, we’re going to talk about a new type of phrase, the prepositional phrase.

English, for example

Prepositional phrases are everywhere you look in English. That last sentence, in fact, ended with one: “in English”. This type of phrase isn’t absolutely necessary to the grammatical “core” of a sentence. It’s extra information that fills in the blanks, giving us more detail about a situation. Prepositional phrases are the way we can learn wheres and whens and hows about something. They’re filler, but that’s exactly what we need.

Looking at English—we’ve got a whole post full of examples, so there’s no reason not to—we can see the general structure of a prepositional phrase. It starts with a preposition, naturally enough, and that’s really the head of the phrase. Prepositions are those words like “in”, “of”, or “below”; they can specify position, location, time, possession, and about a hundred other ideas that are impossible (or just cumbersome) to express with only a verb, subject, and object.

Besides the preposition, the rest of the prepositional phrase is, well, a phrase. It’s usually a noun phrase, but English allows plenty of other options, like gerunds (“in passing“) and adverbs (“until recently“). In theory, there’s nothing stopping a language from letting whole sentences be put into prepositional phrases (“after I came home“), but phrases like that are technically a different kind that we’ll see in a future post. For now, we’ll stick with the more noun-like examples.

Changing it up

English isn’t the only (or even the best) example of how to do prepositional phrases. Other languages do things differently. So, let’s take a look at how they do it.

The first thing to say is that this post’s whole premise is a lie. You don’t need prepositions. You don’t have to have a word that precedes a noun phrase to give more information about a sentence. No, it’s actually a bit more common (according to WALS Chapter 85) to put the preposition after the noun phrase. In that case, it’s not really proper to call it a preposition anymore; pre-, after all, means “before”. So linguists call these words postpositions. Japanese, for instance, uses postpositions, and any anime lover knows of の (no). Thus, “preposition” is technically a misnomer, and the more general term (which we won’t use here) is adposition.

Even rarer types exist, too. There’s the inposition, which pops up in a few languages. It goes somewhere in the middle of the noun phrase. The circumposition has bits that go on either side of the phrase, and this (so Wikipedia says) appears in Pashto and Kurdish. It’s sort of like the French phrasing seen in je ne sais pas. And then there are a few oddballs that don’t seem to have any of these, using verbs or case markers or something like that as stand-ins.

What can fit in the “phrase” part of “prepositional phrase” varies from one language to the next, as well. Noun phrases are let in pretty much everywhere. Not every language has adverbs, though, unless you’re of the belief that any word that doesn’t fit in another class is automatically an adverb. Gerunds aren’t ubiquitous, either. And, of course, languages can go the other way, allowing more possibilities than English.

A note on adverbs

We haven’t really discussed adverbs much, and there’s a good reason: nobody can agree on just what an adverb really is. Sure, it’s easy to say that adverbs are to verbs what adjectives are to nouns, but that doesn’t help much. If you look at it that way, then a prepositional phrase can also be an adverbial phrase. Similarly, some languages—Irish, for example—create regular adverbs by means of a prepositional phrase, their equivalent to English “-ly”. On the other hand, it’s entirely possible for a language to give its adjectives the ability to function as adverbs, as in Dutch.

For a conlang, adverbs are a hard thing to get right. It’s easy to be regular, much harder to be naturalistic. Esperanto mostly goes the regular route, with its productive -e suffix, but there are a few exceptions like hodiau “today”. The best advice I can give here is to have a little bit of both. Have a regular way to derive adverbial meaning from adjectives, but also have a few words that aren’t derived.

In conclusion

How your conlang handles prepositions isn’t something I can tell you. I can give you a few pointers, though. First, there’s a tendency for head-final languages (SOV word order, adjectives preceding nouns, etc.) to use postpositions. This is by no means universal, but it’s something to think about.

And the end of that last sentence brings up another point that often gets overlooked. Can you end a sentence with a preposition? You can in English. The only reason it’s considered “bad” is because of the influence of Latin, which doesn’t allow it. Clearly, a postposition can end a sentence, by its very nature, but the waters are murkier for those that come before. When we get to relative clauses in the next part of the series, the question will be more relevant, but it might be good to have an answer when that time comes.

Other things to consider are what types of phrases can be put in a preposition, where they fit in a sentence (before the verb, after it, at the end, or wherever), case marking (some languages have prepositions that require their nouns to go in a specific case, while case-heavy languages can go with a smaller set of prepositions), person marking (a few languages require this on the preposition), and the relation between prepositional and other types of phrases. And, of course, the main question: do you have prepositions at all?

Once you’re through all that, you’ve greatly increased the expressive power of your language. Not only can you tell what happened and who did it, but now you can specify where, when, why, and how.

Programming paradigm primer

“Paradigm”, as a word, has a bad reputation. It’s one of those buzzwords that corporate people like to throw out to make themselves sound smart. (They usually fail.) But it has a real meaning, too. Sometimes, “paradigm” is exactly the word you want. Like when you’re talking about programming languages. The alliteration, of course, is just an added bonus.

Since somewhere around the 1960s, there’s been more than one way to write programs, more than one way to view the concepts of a complex piece of software. Some of these have revolutionized the art of programming, while others mostly languish in obscurity. Today, we have about half a dozen of these paradigms with significant followings. They each have their ups and downs, and each has a specialty where it truly shines. So let’s take a look at them.

Now, it’s entirely possible for a programming language to use or encourage only a single paradigm, but it’s far more common for languages to support multiple ways of writing programs. Thanks to one Mr. Turing, we know that essentially all languages are, from a mathematical standpoint, equivalent, so you can create, say, C libraries that use functional programming. But I’m talking about direct support. C doesn’t have native objects (struct doesn’t count), for example, so it’s hard to call it an object-oriented language.

Where it all began

Imperative programming is, at its heart, nothing more than writing out the steps a program should take. Really, that’s all there is to it. They’re executed one after the other, with occasional branching or looping thrown in for added control. Assembly language, obviously, is the original imperative language. It’s a direct translation of the computer’s instruction set and the order in which those instructions are executed. (Out-of-order execution changes the game a bit, but not too much.)

The idea of functions or subroutines doesn’t change the imperative nature of such a program, but it does create the subset of structured or procedural programming languages, which are explicitly designed for the division of code into self-contained blocks that can be reused.

The list of imperative languages includes all the old standbys: C, Fortran, Pascal, etc. Notice how all these are really old? Well, there’s a reason for that. Structured programming dates back decades, and all the important ideas were hashed out long before most of us were born. That’s not to say that we’ve perfected imperative programming. There’s always room for improvement, but we’re far into the realm of diminishing returns.

Today, imperative programming is looked down upon by many. It’s seen as too simple, too dumb. And that’s true, but it’s far from useless. Shell scripts are mostly imperative, and they’re the glue that holds any operating system together. Plenty of server-side code gets by just fine, too. And then there’s all that “legacy” code out there, some of it still in COBOL…

The imperative style has one significant advantage: its simplicity. It’s easy to trace the execution of an imperative program, and they’re usually going to be fast, because they line up well with the computer’s internal methods. (That was C’s original selling point: portable assembly language.) On the other hand, that simplicity is also its biggest weakness. You need to do a lot more work in an imperative language, because they don’t exactly have a lot of features.

Objection!

In the mid-90s, object-oriented programming (OOP) got big. And I do mean big. It was all the rage. Books were written, new languages created, and every coding task was reimagined in terms of objects. Okay, but what does that even mean?

OOP actually dates back much further than you might think, but it only really began to get popular with C++. Then, with Java, it exploded, mainly from marketing and the dot-com bubble. The idea that got so hot was that of objects. Makes sense, huh? It’s right there in the name.

Objects, reduced to their most basic, are data structures that are deeply entwined with code. Each object is its own type, no different from integers or strings, but they can have customized behavior. And you can do things with them. Inheritance is one of them: creating a new type of object (class) that mimics an existing one, but with added functionality. Polymorphism is the other: functions that work differently depending on what type of object they’re acting on. Together, inheritance and polymorphism work to relieve a huge burden on coders, by making it easier to work with different types in the same way.

That’s the gist of it, anyway. OOP, because of its position as the dominant style when so much new blood was entering the field, has a ton of information out there. Design patterns, best practices, you name it. And it worked its way into every programming language that existed 10-15 years ago. C++, Java, C#, and Objective-C are the most used of the “classic” OOP languages today, although every one of them offers other options (including imperative, if you need it). Most scripting-type languages have it bolted on somewhere, such as Python, Perl, and PHP. JavaScript is a bit special, in that it uses a different kind of object-oriented programming, based on prototypes rather than classes, but it’s no less OOP.

OOP, however, has a couple of big disadvantages. One, it can be confusing, especially if you use inheritance and polymorphism to their fullest. It’s not uncommon, even in the standard libraries of Java and C#, to have a class that inherits from another class, which inherits from another, and so on, 10 or more levels deep. And each subclass can add its own functions, which are passed on down the line. There’s a reason why Java and C# are widely regarded as having some of the most complete documentation of any programming language.

The other disadvantage is the cause of why OOP seems to be on the decline. It’s great for code reuse and modeling certain kinds of problems, but it’s a horrible fit for some tasks. Not everything can be boiled down to objects and methods.

What’s your function?

That leads us to the current hotness: functional programming, or FP. The functional fad started as a reaction to overuse of OOP, but (again) its roots go way back.

While OOP tries to reduce everything to objects, functional programming, shockingly enough, models the world as a bunch of functions. Now, “function” in this context doesn’t necessarily mean the same thing as in other types of programming. Usually, for FP, these are mathematical functions: they have one output for every input, no matter what else is happening. The ideal, called pure functional programming, is a program free of side effects, such that it is entirely deterministic. (The problem with that? “Side effects” includes such things as user input, random number generation, and other essentials.)

FP has had its biggest success with languages like Haskell, Scala, and—amazingly enough—JavaScript. But functional, er, functions have spread to C++ and C#, among others. (Python, interestingly, has rejected, or at least deprecated, some functional aspects.)

It’s easy to see why. FP’s biggest strength comes from its mathematical roots. Logically, it’s dead simple. You have functions, functions that act on other functions, functions that work with lists, and so on. All of the basic concepts come straight from math, and mistakes are easily found, because they stick out like a sore thumb.

So why hasn’t it caught on? Why isn’t everybody using functional programming? Well, most people are, just in languages that weren’t entirely designed for it. The core of FP is fairly language-agnostic. You can write functions without side effects in C, for example, it’s just that a lot of people don’t.

But FP isn’t everywhere, and that’s because it’s not really as simple as its proponents like to believe. Like OOP, not everything can be reduced to a network of functions. Anything that requires side effects means we have to break out of the functional world, and that tends to be messy. (Haskell’s method of doing this, the monad, has become legendary in the confusion it causes.) Also, FP code really, really needs a smart interpreter, because its mode of execution is so different from how a computer runs, and because it tends to work at a higher level of abstraction. But interpreters are universally slower than native, relegating most FP code to those higher levels, like the browser.

Your language here

Another programming paradigm that deserves special mention is generic programming. This one’s harder to explain, but it goes something like this: you write functions that accept a set of possible types, then let the compiler figure out what “real” type to use. Unlike OOP, the types don’t have to be related; anything that fits the bill will work.

Generic programming is the idea behind C++ templates and Java or C# generics. It’s also really only used in languages like that, though many languages have “duck-typing”, which works in a similar fashion. It’s certainly powerful; most of the C++ standard library uses templates in some fashion, and that percentage is only going up. But it’s complicated, and you can tie your brain in knots trying to figure out what’s going on. Plus, templates are well-known time sinks for compilers, and they can increase code size by some pretty big factors. Duck-typing, the “lite” form of generic programming, doesn’t have either problem, but it can be awfully slow, and it usually shows up in languages that are already slow, only compounding the problem.

What do I learn?

There’s no one right way to code. If we’ve learned anything in the 50+ years the human race has been doing it, it’s that. From a computer science point of view, functional is the way to go right now. From a business standpoint, it’s OOP all the way, unless you’re looking at older code. Then you’ll be going procedural.

And then there are all those I didn’t mention: reactive, event-driven, actor model, and dozens more. Each has its own merits, its own supporters, and languages built around it.

My best advice is to learn whatever you’re preferred language offers first. Then, once you’re comfortable, move on, and never stop learning. Even if you’ll never use something like Eiffel in a serious context, it has explored an idea that could be useful in the language you do use. (In this case, contract programming.) The same could be said for Erlang, or F#, or Clojure, or whatever tickles your fancy. Just resist the temptation to become a zealot. Nobody likes them.

Now, some paradigms are harder than others, in my opinion. For someone who started with imperative programming, the functional mindset is hard to adjust to. Similarly, OOP isn’t easy if you’re used to Commodore BASIC, and even experienced JavaScript programmers are tripped up by prototypes. (I know this one first-hand.)

That’s why I think it’s good that so many languages are adopting a “multi-paradigm” approach. C++ really led the way in this, but now it’s popping up everywhere among the “lower” languages. If all paradigms (for some suitable value of “all”) are equal, then you can use whatever you want, whenever you want. Use FP for the internals, wrapped by an event-driven layer for I/O, calling OOP or imperative libraries when you need them. Some call it a kitchen-sink approach, but I see programmers as like chefs, and every chef needs a kitchen sink.