Criteria to determine if it's a programming language - programming-languages

What are the critera or the basic features required to tell that X or Y is (or is not) a programming language?
I've done some reading (Is HTML considered a programming language?, Turing complete, and others), and came to the conclusion that a language or a syntax has to be Turing complete to be considered a programming language. Is this correct? Is it enough?
And how do I determine if something is Turing complete? Are there any specific criteria?
Is having control-of-flow structures (conditional statements and loops) enough to be considered Turing complete?

There exist programming languages which are not Turing complete. For some examples of non-Turing complete languages, take a look at: Practical non-Turing-complete languages?
An advantage of having a language that is non-Turing complete could for instance be that it might be sufficient to perform the tasks you need, while being simple enough to allow you to prove properties about your programs, which you could not otherwise prove. This could, for instance, be useful in cases where it's vital to know that the program will run without error.
What exactly constitutes a programming language is a bit vague, but one could say that it's a language in which you can express computations. If we look at HTML, you cannot create a document that computes anything; it merely tells the browser how the page is supposed to look. The important part to note is, it doesn't compute anything new.
It is, as Marcelo says, quite fuzzy.
As for determining if a language is Turing complete, I will refer you to this question: What are practical guidelines for evaluating a language's "Turing Completeness"?

What are the critera or the basic features required to tell that X or Y is (or is not) a programming language ?
As Marcelo Cantos already told it is somewhat fuzzy, especially since there are Domain specific languages (DSLs; http://en.wikipedia.org/wiki/Domain-specific_language) that are not Turing complete, but also often considered programming languages.
And how do I determine if something is Turing complete ? Are there any specific criteria ?
One way determine whether a programming language is Turing complete is to write a Turing machine in it (or an implementation of the Lambda calculus).
Another way is to prove that all mu-recursive functions
http://en.wikipedia.org/wiki/%CE%9C-recursive_function
can be computed by the programming language.
Since it can be proved that an imperative programming language is Turing complete, if there is a variable assignment, a way to represent number 0, a successor function, a predecessor function and a possibility to represent while-loops this is another way.
A sometimes-used way (that for obvious reasons does not always work) to prove that a programming language is not Turing-complete is to check whether all programs terminate; if yes, it can't be.

So let's think about the consequences of these concrete definitions:
A Turing-complete language is a programming language: CSS becomes a programming language.
A programming language must be turing-complete: maybe, but programs can be written otherwise.
Now a far better definition: A programming language is one that can be used to write programs.

The term "programming language" is somewhat fuzzy. Do regular expressions constitute a programming language? Most programmers would say yes, even though regexes are not turing complete.
As for Turing-completeness, I'm no expert, but I think it is sufficient to have a conditional branch and an infinite stack (therefore real machine only approximate turing-completeness).
EDIT: After a bit of research, I've found that this is isn't sufficient. You need at least two stacks and some minimal number of states (and a state transition table).
Perhaps a more down-to-earth yardstick is that if can remember arbitrary amounts of state and do loops, it's probably turing complete.

Related

Is natural language Turing complete?

I'm pretty sure a human language (e.g. English) is powerful enough to simulate a Turing machine, which would make it Turing complete. However, that would imply natural languages are no more or less expressive than programming languages, which seems questionable.
Is natural language Turing complete?
First of all "Is language X Turing complete" is only a well-defined question given a well-defined semantics for language X. It is nearly impossible to define one for natural languages due to natural languages' complex nature and reliance on context and intuition. Most (all?) natural languages don't even have a well-defined syntax.
That aside, your main confusion is based on the assumption that it's not possible for a computational model to be strictly more powerful than a Turing machine, i.e. be able to simulate a Turing machine, but also to express computations that a Turing machine can not. This is not true. For example we can extend Turing machines with oracles and we get a computational model that's strictly more powerful than plain Turing machines.
In the same vein we could define a programming language MagicLang that can do everything an ordinary programming language can do plus solve the halting problem. Defining a semantics for such a language is easy: just take the semantics of the language we used as a basis and add a function bool halts(string src, string input) with the semantics "returns true if the program described by the source code src successfully terminates after a finite amount of time when given the input input". So that's easy. What's hard, or rather impossible, is implementing this language.
Now one may argue that natural language can also describe the halting problem and our brain can "execute" natural language, i.e. it can answer the question "does this program halt". So if we could build a computer that could do everything our brain can do, it should be able to do this as well. But the thing is our brain can't solve the halting problem with 100% accuracy. Our brain can't even execute regular programs with 100% accuracy. Just remember how often you've stepped through a program in your head and came up with a different result than reality. Our brain is very good at learning, making intuitive connections and applying heuristics, but those things always come with the risk of giving the wrong result.
So could a computer do the same thing? Yes, we can use heuristics and machine learning to approach otherwise unsolvable problems and with that normal programming languages can attempt to solve every problem that can be described in natural language (even the undecidable ones). But just like the brain, those programs will sometimes give wrong results. In fact they will give wrong results much more often as our machine learning algorithms and heuristics aren't nearly as advanced as those of the human brain.
If a software language is sufficiently complex that it can be used to define arbitrary extensions to itself (such as defining arbitrary new functions), then it's clearly Turing-complete.
Using natural language I can, given sufficient time, teach another human terminology and concepts to extend their understanding and ability to discuss arbitrary subjects that they previously couldn't -- I could teach them copyright law, or astrophysics, for example (if they didn't already know them). So, while this may be more of an analogy than an exact identity, there does seem to be a Turing-completeness-like property to natural languages: they can be used to define and transmit arbitrary extensions to themselves. (Admittedly, not every human is really cut out to learn astrophysics -- but then any non-idealized Turning machine has only some finite amount of memory, so it's always possible to define a program that it can't run because it doesn't have enough memory.)

Are there other HOL programming languages besides Caledon that are based on haskell?

There are programming languages and theorem prover based on higher order logic (HOL). Examples include Twelf, lambda prolog, Isabelle. For example Twelf is is both a programming language and a theorem prover, while Isabelle is mainly a theorem prover, but for Isabelle code extraction is available.
I am looking for a HOL programming language based on haskell. The reason is that I like, for instance, lambda prolog very much, but it is not meant as a practical programming language. Lambda prolog lacks a standard library and interfacing with external libraries doesn't seem trivial. The problem is if you need some functionality, like writing a parser for a text file, you can't interface, say, with the many available existing libraries for haskell, and further, there is no standard library so you start from scratch.
Today I came across the Caledon programming language that was implemented as a master thesis, it seems. From the github page:
Caledon is a dependently typed, polymorphic, higher order logic
programming language.
This is interesting, since it is written in haskell so it should be easy to extend and interface with existing haskell libraries. But it seems that the project is in a bit early stage, I am not sure if input-output (IO) is implemented. Since I learned only today about Caledon, I think I might have missed some further projects. (BTW, I am not interested in standard logic programming languages like prolog).
Are there programming languages based on higher order logic besides Caledon that are implemented in haskell?
(I am asking for "implemented in haskell", as it is rather easy to connect programming languages that can be extracted to or are implemented in haskell. For example the Agda programming language can compile to haskell code and haskell libraries can be used conveniently and is extremly easy to use haskell libraries if you know how. Many other programming languages (e.g., ATS) I belive only provide the smallest common denominator which is a C based foreign function interface (FFI). In my eyes it is quite cumbersome to connect two higher programming languages via their respective C-based FFI interface. Thus the seemly abitrary part that "it should be implemented in haskell". Further, as a side note some users have downvoted in the past for my description of Agda as a programming language, but of course this is not true, i.e., consider Curry-Howard )
"Haskabelle is a converter from Haskell source files to Isabelle/HOL theories implemented in Haskell itself."
Haskabelle
Strange Statement: Haskell' is a higher order logic programming language based on Haskell. Type inference in Haskell with multiparameter type classes, type families, undecidable inference and whatnot actually forms a higher order logic programming language. This probably doesn't help you very much because:
The spec is literally constantly changing (I've had a few packages loose compatibility as they were based on hacks that got "fixed")
The type system itself doesn't have IO (yet?)
It can't really call other Haskell libraries from type inference
Its not very fast.
The logic programming semantics aren't exactly clear or stable.
It doesn't permit you to unify with lambdas or other type classes, although it does permit unification with functions.
Sadly, I know of extraordinarily few full HOL languages let alone ones implemented in Haskell - it turns out higher order unification is a huge pain to implement.
short answer: i don't know. long answer: you have small chances you will find purely academic language with thousands of libraries and tools for it. if you for some reason need that specific language for some specific problem then use it ONLY for that problem. not for parsing files, calculating taxes or launching rockets. create library and link it with other programs. or even better: create a microservice or connect the programs in other way (e.g. standard input/output) that doesn't require much effort. always use best tool for the job

Apache Ant as programming language

Is Ant programming language? If yes, is it imperative or functional?
The question should rather be: If you wanted to do computation with Ant, could you do it? Let us restrict ourselves to something simple, like emulating a simplified command-line calculator. If you can do this, Ant certainly qualifies as a programming language, even if that was not the intention of the tool originally.
The perhaps simplest language we could define is "Huttons Razor", which consists of
Constants, like 5, 37, and so on, all Natural numbers (counted from 0)
A + operator, so we can write (37 + 5) + 15 + (42 + 0)
Not a useful language by any means, but a magnificent start, should you try to abuse Ant to do computation; if you can't even do the Razor, you probably can't do something more advanced. Do keep in mind though that other means of computation, like the lambda calculus, is vastly different from this though, so it may be that other paths are also viable. Note: I don't know if Ant can do this. I last looked at it in 2006 and decided to never look at it again.
Note that a language does not have to be Turing Complete to be a programming language. We have several, highly useful, programming languages out there which are not.
For non-Turing Complete useful languages:
Languages that only accept Total programs (i.e., programs that terminate). This is a necessity for many programming languages that are used as theorem provers: Coq, Agda, etc. Another example is the Simply Typed Lambda calculus (the simple typing makes it impossible to define the Y-combinator and get recursion).
Languages that are heavily domain specific. One example is Troll,
http://www.diku.dk/hjemmesider/ansatte/torbenm/Troll/
which is a language for describing dice rolls in tabletop and roleplaying games. The language does not seem to be TC, yet it is highly useful when designing new games as it can quickly calculate probability distributions of dice throw methods.
Nope, Ant is not a programming language. It is a build tool written in Java.
Yes, but only just.
I have had to actually program in ant. I work at a Java shop, and we use ant for its proper purpose, something like a makefile for Java, which it is in fact just the thing for. But we have useful things we wanted to do with it, and the ant script was the logical right place for the complexity in question.
(The official method to do arbitrary programming in ant is to write an extension in Java. The trouble with this is that it's not right there in the script you're looking at, and it requires that compilation step. So, writing in ant itself it was.)
ant is like an esoteric programming language whose conceit is that it's all correctly formed XML. Doing even quite basic things takes a few hours' thought of how to solve this one. You can hide some of the horror behind a macrodef.
You can mostly do stuff with just basic ant, but ant-contrib is needed if you want to preserve the precious dregs of your sanity. It includes slightly useful things like variables, arithmetic and flow control.
Hence my rule: never make your domain-specific language Turing-complete;
because once you can code in it, you will have to code in it.
ant programming is a skill I will not be listing on my CV.
Apache Ant is declarative domain specific language for describing the build process using XML. AFAIK basic constructs are not Turing complete, so I would not describe it as programming language. Of course, through execution of other programs you can achieve whatever those programs can.
Generally, a programming language is considered a formalism to describe algorithms. The turing machine is considered the most general mechanism to execute an algorithm.
Ant (without extension, such as Ant-Contrib) is not turing complete, because properties are immutable once a value is assigned to them. Every property must be named explicitly at least once throughout the script. Every ant script is finite, so the number of properties is finite and therefore the number of states is finite. Hence, ant is not turing complete.
Therefore, one cannot describe any algorithm in ant and thus ant is not a programming language.
Programming language? I would have said Ant was a restricted scripting language for builds. It's an XML-driven make.
If forced to choose, I would say it is not a functional language - it's closer in spirit to imperative/procedural.

What is a computer programming language?

At the risk of sounding naive, I ask this question in search of a deeper understanding of the concept of programming languages in general. I write this question for my own edification and the edification of others.
What is a useful definition of a computer programming language and what are its basic and necessary components? What are the key features that differentiate languages (functional, imperative, declarative, object oriented, scripting, etc...)?
One way to think about this question. Imagine you are looking at the hardware of a modern desktop or laptop computer. Assume, that the C language or any of its variants do not exist. How would you describe to others all the things needed to make the computer expressive and functional in terms of what we expect of personal computers today?
Tangentially related, what is it about computer languages that allow other languages to exist? For example take a scripting language like Javascript, Perl, or PHP. I assume part of the definition of these is that there is an interpreter most likely implemented in C or C++ at some level. Is it possible to write an interpreter for Javascript in Javascript? Is this a requirement for a complete language? Same for Perl, PHP, etc?
I would be satisfied with a list of concepts that can be looked up or researched further.
Like any language, programming languages are simply a communication tool for expressing and conveying ideas. In this case, we're translating our ideas of how software should work into a structured and methodical form that computers (as well as other humans who know the language, in most cases) can read and understand.
What is a useful definition of a computer programming language and what are its basic and necessary components?
I would say the defining characteristic of a programming language is as follows: things written in that language are intended to eventually be transformed into something that is executed. Thus, pseudocode, while perhaps having the structure and rigor of a programming language, is not actually a programming language. Likewise, UML can express many powerful ideas in an abstract manner just like a programming language can, but it falls short because people don't generally write UML to be executed.
How would you describe to others all the things needed to make the computer expressive and functional in terms of what we expect of personal computers today?
Even if the word "programming language" wasn't part of the shared vocabulary of the group I was talking to, I think it would be obvious to the others that we'd need a way to communicate with the computer. Just as no one expects a car to drive itself (yet!) without external instructions in the form of interaction with the steering wheel and pedals, no one could expect the hardware to function without being told what to do. As noted above, a programming language is the conduit through which we can make that communication happen.
Tangentially related, what is it about computer languages that allow other languages to exist?
All useful programming languages have a property called Turing completeness. If one language in the Turing-complete set can do something, then any of them can; they are said to be computationally equivalent.
However, just because they're equally "powerful" doesn't mean they're equally nice to work with for humans. This is why many people are willing to sacrifice the unparalleled micromanagement you get from writing assembly code in exchange for the expressiveness and power you get with higher-level languages, like Ruby, Python, or C#.
Is it possible to write an interpreter for Javascript in Javascript? Is this a requirement for a complete language? Same for Perl, PHP, etc?
Since there is a Javascript interpreter written in C, it follows that it must be possible to write a Javascript interpreter in Javascript, since both are Turing-complete. However, again, note that Turing-completeness says nothing about how hard it is to do something in one language versus another -- only whether it is possible to begin with. Your Javascript-interpreter-inside-Javascript might well be horrendously inefficient, consume absurd amounts of memory, require enormous processing power, and be a hideously ugly hack. But Turing-completeness guarantees it can be done!
While this doesn't directly answer your question, I am reminded of the Revenge of the Nerds essay by Paul Graham about the evolution of programming languages. It's certainly an interesting place to start your investigation.
Not a definition, but I think there are essentially two strands of development in programming languages:
Those working their way up from what the machine can do to something more expressive and less tied to the machine (Assembly, Fortran, C, C++, Java, ...)
Those going down from some mathematical or theoretical computer science concept of computation to something implementable on a real machine (Lisp, Prolog, ML, Haskell, ...)
Of course, in reality the picture is not as neat, and both strands influence each other by borrowing the best ideas.
Slightly long rant ahead.
A computer language is actually not all that different from a human language. Both are used to express ideas and concepts in commonly understood terms. Among different human languages there are syntactic differences, but you can express the same thing in every language (does that make human languages Turing complete? :)). Some languages are better suited for expressing certain things than others.
For example, although technically not completely correct, the Inuit language seems quite suited to describe various kinds of snow. Japanese in my experience is very suitable for expressing ones feelings and state of mind thanks to a large, concise vocabulary in that area. German is pretty good for being very precise thanks to largely unambiguous grammar.
Different programming languages have different specialities as well, but they mostly differ in the level of detail required to express things. The big difference between human and programming languages is mostly that programming languages lack a lot of vocabulary and have very few "grammatical" rules. With libraries you can extend the vocabulary of a language though.
For example:
Make me coffee.
Very easy to understand for a human, but only because we know what each of the words mean.
coffee : a drink made from the roasted and ground beanlike seeds of a tropical shrub
drink : a liquid that can be swallowed
swallow : cause or allow to pass down the throat
... and so on and so on
We know all these definitions by heart, but we had to learn them at some point.
In the same way, a computer can be "taught" to "understand" words as well.
Coffee::make()->giveTo($me);
This could be a perfectly valid expression in a computer language. If the computer "knows" what Coffee, make() and giveTo() means and if $me is defined. It expresses the same idea as the English sentence, just with a different, more rigorous syntax.
In a different environment you'd have to say slightly different things to get the same outcome. In Japanese for example you'd probably say something like:
コーヒーを作ってもらっても良いですか?
Kōhī o tsukuttemoratte mo ii desu ka?
Which would roughly translate to:
if ($Person->isAgreeable('Coffee::make()')) {
return $Person->return(Coffee::make());
}
Same idea, same outcome, but the $me is implied and if you don't check for isAgreeable first you may get a runtime error. In computer terms that would be somewhat analogous to Ruby's implied behaviour of returning the result of the last expression ("grammatical feature") and checking for available memory first (environmental necessity).
If you're talking to a really slow person with little vocabulary, you probably have to explain things in a lot more detail:
Go to the kitchen.
Take a pot.
Fill the pot with water.
...
Just like Assembler. :o)
Anyway, the point being, a programming language is actually a language just like a human language. Their syntax is different and specialized for the problem domain (logic/math) and the "listener" (computers), but they're just ways to transport ideas and concepts.
EDIT:
Another point about "optimization for the listener" is that programming languages try to eliminate ambiguity. The "make me coffee" example could, technically, be understood as "turn me into coffee". A human can tell what's meant intuitively, a computer can't. Hence in programming languages everything usually has one and one meaning only. Where it doesn't you can run into problems, the "+" operator in Javascript being a common example.
1 + 1 -> 2
'1' + '1' -> '11'
See "Programming Considered as a Human Activity." EWD 117.
http://www.cs.utexas.edu/~EWD/transcriptions/EWD01xx/EWD117.html
Also See http://www.csee.umbc.edu/331/current/notes/01/01introduction.pdf
Human expression which:
describes mathematical functions
makes the computer turn switches on and off
This question is very broad. My favorite definition is that a programming language is a means of expressing computations
Precisely
At a high level
In ways we can reason about them
By computation I mean what Turing and Church meant: the Turing machine and the lambda calculus have equivalent expressive power (which is a theorem), and the Church-Turing hypothesis (which is a conjecture) says roughly that there's no more powerful notion of computation out there. In other words, the kinds of computations that can be expressed in any programming languages are at best the kinds that can be expressed using Turing machines or lambda-calculus programs—and some languages will be able to express only a subset of those calculations.
This definition of computation also encompasses your friendly neighborhood hardware, which is pretty easy to simulate using a Turing machine and even easier to simulate using the lambda calculus.
Expressing computations precisely means the computer can't wiggle out of its obligations: if we have a particular computation in mind, we can use a programming language to force the computer to perform that computation. (Languages with "implementation defined" or "undefined" constructs make this task more difficult. Programmers using these languages are often willing to settle for—or may be unknowingly settling for—some computation that is only closely related to the computation they had in mind.)
Expressing computation at a high level is what programming langauges are all about. An important reason that there are so many different programming languages out there is that there are so many different high-level ways of thinking about problems. Often, if you have an important new class of problems to solve, you may be best off creating a new programming language. For example, Larry Wall's writing suggests that solving a class of problems called "systems administration" was a motivation for him to create Perl.
(Another reason there are so many different programming languages out there is that creating a new language is a lot of fun, and anyone can learn to do it.)
Finally, many programmers want languages that make it easy to reason about programs. For example, today a student of mine implemented a new algorithm that made his program run over six times faster. He had to reason very carefully about the contents of C arrays to make sure that the new algorithm would do the same job the old one did. Luckily C has decent tools for reasoning about programs, for example:
A change in a[i] cannot affect the value of a[i-1].
My student also applied a reasoning principle that isn't valid in C:
The sum of of a sequence unsigned integers will be at least as large as any integer in the sequence.
This isn't true in C because the sum might overflow. One reason some programmers prefer languages like Standard ML is that in SML, this reasoning principle is always valid. Of languages in wide use, probably Haskell has the strongest reasoning principles Richard Bird has developed equational reasoning about programs to a high art.
I will not attempt to address all the tangential details that follow your opening question. But I hope you will get something out of an answer that aims to give a deeper understanding, as you asked, of a fundamental question about programming languages.
One thing a lot of "IT" types forget is that there are 2 types of computer programming languages:
Software programming languages: C, Java, Perl, COBAL, etc.
Hardware programming languages: VHDL, Verilog, System Verilog, etc.
Interesting.
I'd say the defining feature of a programming language is the ability to make decisions based on input. Effectively, if and goto. Everything else is lots and lots of syntactic sugar. This is the idea that spawned Brainfuck, which is actually remarkably fun to (try to) use.
There are places where the line blurs; for example, I doubt people would consider XSLT to really be a programming language, but it's Turing-complete. I've even solved a Project Euler problem with it. (Very, very slowly.)
Three main properties of languages come to mind:
How is it run? Is it compiled to bare metal (C), compiled to mostly bare metal with some runtime lookup (C++), run on a JIT virtual machine (Java, .NET), bytecode-interpreted (Perl), or purely interpreted (uhh..)? This doesn't comment much on the language itself, but speaks to how portable the code may be, what sort of speed I might expect (and thus what broad classes of tasks would work well), and sometimes how flexible the language is.
What paradigms does it support? Procedural? Functional? Is the standard library built with classes or functions? Is there reflection? Is there, ideally, support for pretty much whatever I want to do?
How can I represent my data? Are there arrays, and are they fixed-size or not? How easy is it to use strings? Are there structs or hashes built in? What's the type system like? Are there objects? Are they class-based or prototype-based? Is everything an object, or are there primitives? Can I inherit from built-in objects?
I realize the last one is a very large collection of potential questions, but it's all related in my mind.
I imagine rebuilding the programming language landscape entirely from scratch would work pretty much how it did the first time: iteratively. Start with assembly, the list of direct commands the processor understands, and wrap it with something a bit easier to use. Repeat until you're happy.
Yes, you can write a Javascript interpreter in Javascript, or a Python interpreter in Python (see: PyPy), or a Python interpreter in Javascript. Such languages are called self-hosting. Have a look at Perl 6; this has been a goal for its main implementation from the start.
Ultimately, everything just has to translate to machine code, not necessarily C. You can write D or Fortran or Haskell or Lisp if you want. C just happens to be an old standard. And if you write a compiler for language Foo that can ultimately spit out machine code, by whatever means, then you can rewrite that compiler in Foo and skip the middleman. Of course, if your language is purely interpreted, this will probably result in a stack overflow...
As a friend taught me about computer languages, a language is a world. A world of communication with that machine. It is world for implementing ideas, algorithms, functionality, as Alonzo and Alan described. It is the technical equivalent of the mathematical structures that the aforementioned scientists built. It is a language with epxressions and also limits. However, as Ludwig Wittgenstein said "The limits of my language mean the limits of my world", there are always limitations and that's how one chooses it's language that fits better his needs.
It is a generic answer... some thoughts actually and less an answer.
There are many definitions to this but what I prefer is:
Computer programming is programming that helps to solve a particular technical task/problem.
There are 3 key phrases to look out for:
You: Computer will do what you (Programmer) told it to do.
Instruct: Instruction is given to the computer in a language that it can understand. We will discuss that below.
Problem: At the end of the day computers are tools (Complex). They are there to make out life simpler.
The answer can be lengthy but you can find more about computer programming

what is a programming language?

Wikipedia says:
A programming language is a machine-readable artificial language designed to express computations that can be performed by a machine, particularly a computer. Programming languages can be used to create programs that specify the behavior of a machine, to express algorithms precisely, or as a mode of human communication.
But is this true? It occurred to me in the shower this morning that a programming language might just be a set of conventions, something that both a human and an appropriately arranged compiler can interpret. If that's the case, then isn't it this definition of a programming language misleading? If that isn't the case, then what's the difference between a compiler and the language it compiles?
Thanks!
z.
A programming language is exactly that set of conventions, but I don't see why that makes the Wikipedia entry misleading, really. If it makes you feel better, you might edit it to read something like:
A programming language is a machine-readable artificial language designed to express computations that can be performed by a machine, particularly a computer. Programming languages can be used to define programs that specify the behavior of a machine, to express algorithms precisely, or as a mode of human communication.
I understand what you are saying, and you are right. Describing a programming language as a "machine-readable artificial language designed to express computations that can be performed by a machine" is unnecessarily specific. Programming languages can be more broadly generalized as established descriptions of tasks (or "a set of conventions") that allow one entity to control the behavior of another. What we traditionally identify as programming languages are just a layer of abstraction between machine code and programmers, and are specifically designed for electronic computers.
Programming languages are not limited to traditional computers (see the K'NEX Computer), and aren't even necessarily limited to computational devices at all. For example, when I am pleased with my dog's behavior, he gets a treat. When I am displeased, he gets nothing. Over time the dog learns the treat/no treat programming and I can use the treats to control his behavior (to an extent).
I don't see what is different between what you are asking...
It occurred to me in the shower this morning that a programming language might just be a set of conventions, something that both a human and an appropriately arranged compiler can interpret.
... and the Wikipedia definition.
The key is that a programming language is just "a machine-readable artificial language".
A compiler does indeed act as an effective specification of a language in terms of a reduction to machine code - however, as it's generally difficult to understand a language by reading the compiler's source, one generally considers a programming language in terms of an abstract processing model that the compiler implements. This abstract model is what one means when one refers to the programming language.
That said, there are indeed many languages (Hi there, PHP!) in which the compiler is the only specification of the language in existence. These languages tend to change unpredictably at times as compiler bugs are fixed or introduced.
Programming languages are an abstraction layer that helps insulate the programmer from having to talk in electrical signals to the computer. The creators of the language have done all the hard work in creating a structure (language) or standard (grammar, conjugation, etc.) that then can be interpreted by a compiler in terms that the computer understands.
All programming languages are really nothing more than domain specific languages for machine code or manipulating the registers and memory of a processing entity.
This is probably the true explanation of what a programming language really is:
Step 1: Think of a language and its grammar, which is a set of rules for making syntactically valid statements using the language. For example, a language called GRID has tiles {0,1} as its alphabet and grammar rules that make sure every GRID statement has equal length and height.
Step 2 (definition of program): GRID, so far, is useless. I'd dare to think of any valid statement of GRID as just data. We need to add something else to GRID: a successor function. So GRID={Grammar, alphabet, successor function}. To make this clear, lets use the rules of "The Game of Life" as successor function.
Step 3: The Game of Life is actually Turing Complete, so GRID={Grammar, alphabet, successor function = GOL} can perform any computation that is computable.
A programming language is nothing but a language with a successor function. The environment that evaluates a valid statement of the language(program) does nothing but follow those successor functions. Variables, for example, are things whose successor functions = (STAY THE SAME)
Computers are just very fast environments ;)
Wikipedia's definition might have been taken out of context. For one thing, only programs written in machine code are machine-readable. Otherwise, you need a compiler to convert C++, Java or even assembly code to machine code so the computer can carry out your instructions. Unless you include comments that are only readable to humans, or unless you are strictly discussing a topic within the realm of your program, programming is insufficient for human communication.

Resources