45

I mean it in this way:

<?php
    $number1 = 5;   // (Type 'Int')
    $operator1 = +; // (Type non-existent 'Operator')
    $number2 = 5;   // (Type 'Int')
    $operator2 = *; // (Type non-existent 'Operator')
    $number3 = 8;   // (Type 'Int')

    $test = $number1 $operator1 $number2 $operator2 $number3; //5 + 5 * 8.

    var_dump($test);
?>

But also in this way:

<?php
    $number1 = 5;
    $number3 = 9;
    $operator1 = <;

    if ($number1 $operator1 $number3) { //5 < 9 (true)
        echo 'true';
    }
?>

It doesn't seem like any languages have this - is there a good reason why they do not?

15
  • 28
    In general what you want to do would be covered by all languages that support some form of meta programming with some kind lambdas, closures or anonymous functions which would be the common way to implement such features. With languages where methods are first class citizens you would be able to use them more or less identical to variables. Though not in exactly that simple syntax you use here since in most such languages it must be made clear that you actually want to call the method stored in the variable. Mar 22, 2016 at 14:54
  • 7
    @MartinMaat Functional languages do this a lot. Mar 23, 2016 at 10:05
  • 7
    in haskell, operators are functions like any other function. the type of (+) is Num a => a -> a -> a IIRC. you can also define functions so they can be written infixed (a + b instead of (+) a b)
    – sara
    Mar 23, 2016 at 17:41
  • 5
    @enderland: Your edit completely changed the goal of the question. It went from asking if any languages exist, to asking why so few exist. I think your edit will result in a lot of confused readers. Mar 24, 2016 at 22:16
  • 5
    @enderland: while that's true, completely changing the subject only serves to confuse. If it's off topic, the community will vote to close it. None of the answers (at the time I write this) make sense for the question as its written. Mar 24, 2016 at 22:20

4 Answers 4

103

Operators are just functions under funny names, with some special syntax around.

In many languages, as varied as C++ and Python, you can redefine operators by overriding special methods of your class. Then standard operators (e.g. +) work according to the logic you supply (e.g. concatenating strings or adding matrices or whatever).

Since such operator-defining functions are just methods, you can pass them around as you would a function:

# python
action = int.__add__
result = action(3, 5)
assert result == 8

Other languages allows you to directly define new operators as functions, and use them in infix form.

-- haskell
plus a b = a + b  -- a normal function
3 `plus` 5 == 8 -- True

(+++) a b = a + b  -- a funny name made of non-letters
3 +++ 5 == 8 -- True

let action = (+)
1 `action` 3 == 4 -- True

Unfortunately, I'm not sure if PHP supports anything like that, and if supporting it would be a good thing. Use a plain function, it's more readable than $foo $operator $bar.

11
  • 2
    @tac: Yes, this is nice and can even be sort of ported to other languages :) Regarding Haskell, what I am missing most is this department is the $ operator that obviates parentheses (especially multiple nested) — but this can only work with non-variadic functions, thus excluding e.g. Python and Java. OTOH unary function composition can nicely be done.
    – 9000
    Mar 22, 2016 at 16:25
  • 6
    I've never been a fan of operator overloading because yes, an operator is just a function with special syntax, but there's an implied contract that usually goes with operators that does not go with functions. "+" for example, has certain expectations - operator precedence, commutativity, etc - and going against those expectations is a sure route for confusing people and generating bugs. One reason that, though I love javascript, I would have preferred they distinguish between + for addition and concatenation. Perl had it right there.
    – fool4jesus
    Mar 22, 2016 at 22:11
  • 4
    Note that Python has a standard operator module that would let you write action = operator.add, and have it work for any type that defines + (not just int).
    – dan04
    Mar 22, 2016 at 22:13
  • 3
    In Haskell + works on the Num typeclass so you can implement + for any new data type you create, but in the same way as you do anything else e.g. fmap for a functor. It is such a natural fit for Haskell to allow operator overloading they'd have to work hard not to allow it! Mar 23, 2016 at 0:09
  • 17
    Not sure why people are getting fixated on overloading. The original question isn't about overloading. It looks to me to be about operators as first-class values. In order to write $operator1 = + and then use it an expression you don't need to use operator overloading at all!
    – Andres F.
    Mar 23, 2016 at 13:15
16

There are lots of languages that allow some kind of metaprogramming. In particular, I am surprised to see no answer talking about the Lisp family of languages.

From wikipedia :

Metaprogramming is the writing of computer programs with the ability to treat programs as their data.

Later in the text :

Lisp is probably the quintessential language with metaprogramming facilities, both because of its historical precedence and because of the simplicity and power of its metaprogramming.

Lisp languages

A quick made up intro to Lisp follows.

One way to see code is as a suite of instructions : do this, then do that, then do this other thing... This is a list ! A list of things for the program to do. And of course you can have lists inside lists to represent loops and so on..

If we represent a list containing the elements a, b, c, d like this : (a b c d) we get something that looks like a Lisp function call, where a is the function, and b, c, d are the arguments. If fact the typical "Hello World!" program could be written like so : (println "Hello World!")

Of course b, c or d could be lists that evaluate to something as well. The following : (println "I can add :" (+ 1 3) ) would then print ""I can add : 4".

So, a program is a serie of nested lists, and the first element is a function. The good news is that we can manipulate lists ! So we can manipulate programming languages.

The Lisp advantage

Lisps are not so much programming languages as much as a toolkit for making programming languages. A programmable programming language.

This is not only much easier in Lisps to make new operators, it is also nearly impossible to write some operators in other languages because arguments are evaluated when passed to the function.

For instance in a C-like language let's say that you want to write a if operator yourself, something like :

my-if(condition, if-true, if-false)

my-if(false, print("I should not be printed"), print("I should be printed"))

In this case both arguments will be evaluated and printed, in an order dependant of the order of the evaluation of the arguments.

In Lisps, writing an operator (we call it a macro) and writing a function is about the same thing and used in the same way. The major difference being that parameters to a macro are not evaluated before being passed as arguments to the macro. This is essential to be able to write some operators, like the if above.

Real-world languages

Showing how exactly is a bit out of scope here, but I encourage you to try programming in one Lisp to learn more. For instance you could have a look at :

  • Scheme, an old, quite "pure" Lisp with a small core
  • Common Lisp, a bigger Lisp with a well integrated object system, and many implementations (it is ANSI-standardized)
  • Racket a typed Lisp
  • Clojure my favorite, the examples above were Clojure code. A modern Lisp running on the JVM. There are some examples of Clojure macros on SO as well (but this is not the right place to start. I would look at 4clojure, braveclojure or clojure koans at first)).

Oh and by the way, Lisp means LISt Processing.

Regarding your examples

I am going to give examples using Clojure below :

If you can write an add function in Clojure (defn add [a b] ...your-implementation-here... ), you can name it + like so (defn + [a b] ...your-implementation-here... ). This is in fact what is done in the real implementation (the body of the function is a bit more involved but the definition is essentially the same as I wrote above).

What about infix notation ? Well Clojure uses a prefix (or Polish) notation, so we could make an infix-to-prefix macro that would turn prefixed code into Clojure code. Which is actually surprisingly easy (it is actually one of the macro exercises in the clojure koans) ! It can also be seen in the wild, for instance see Incanter $= macro.

Here is the simplest version from the koans explained :

(defmacro infix [form]
  (list (second form) (first form) (nth form 2)))

;; takes a form (ie. some code) as parameter
;; and returns a list (ie. some other code)
;; where the first element is the second element from the original form
;; and the second element is the first element from the original form
;; and the third element is the third element from the original form (indexes start at 0)
;; example :
;; (infix (9 + 1))
;; will become (+ 9 1) which is valid Clojure code and will be executed to give 10 as a result

To drive the point even further, some Lisp quotes :

“Part of what makes Lisp distinctive is that it is designed to evolve. You can use Lisp to define new Lisp operators. As new abstractions become popular (object-oriented programming, for example), it always turns out to be easy to implement them in Lisp. Like DNA, such a language does not go out of style.”

— Paul Graham, ANSI Common Lisp

“Programming in Lisp is like playing with the primordial forces of the universe. It feels like lightning between your fingertips. No other language even feels close.”

— Glenn Ehrlich, Road to Lisp

13
  • 1
    Note that metaprogramming, while interesting, isn't necessary in order to support what the OP is asking about. Any language with support for first-class functions is enough.
    – Andres F.
    Mar 23, 2016 at 13:10
  • 1
    Example to OP's question: (let ((opp #'+)) (print (apply opp '(1 2)))) Mar 23, 2016 at 13:28
  • 1
    No mention of Common Lisp?
    – coredump
    Mar 23, 2016 at 17:01
  • 3
    I remember on a panel discussion about languages, Ken was talking about precedence in APL and concluded with "I hardly ever use parentheses at all!" And someone from the audience yelled,"that's because Dick used them all up!"
    – JDługosz
    Mar 23, 2016 at 17:08
  • 2
    In a C++-style language, you could reimplement if, but you'd need to wrap the then and else arguments with lambdas. PHP and JavaScript have function(), C++ has lambdas, and there exists an Apple extension to C with lambdas. Mar 24, 2016 at 18:51
9

$test = $number1 $operator1 $number2 $operator2 $number3;

Most languages implementations have a step where a parser analyses your code and builds a tree from it. So for example the expression 5 + 5 * 8 would get parsed as

  +
 / \
5   *
   / \
  8   8

thanks to the compiler's knowledge about precedences. If you fed it variables in place of operators, it would not know the proper order of operations before running the code. For most implementations that would be a serious problem, so most languages don't allow that.

You could of course conceive a language where the parser just parses the above as a sequence of expressions and operators, to be sorted and evaluated at runtime. Presumably there just isn't much application for this.

Many scripting languages allow the evaluation of arbitrary expressions (or at least arbitrary arithmetic expressions as in the case of expr) at runtime. There you could just combine your numbers and operators into a single expression and let the language evaluate that. In PHP (and many others) that function is called eval.

$test = eval("$number1 $operator1 $number2 $operator2 $number3");

There are also languages which allow for code generation at compile time. The mixin expression in D comes to my mind, where I believe you could write something like

test = mixin("number1 " + operator1 + " number2 " + operator2 + "number3");

Here operator1 and operator2 would have to be string constants which are known at compile time, e.g. template parameters. number1, number2 and number3 were left as normal runtime variables.

Other answers already discussed the various ways how an operator and a function are more or less the same thing, depending on the language. But usually there is a syntactic difference between a builtin infix operator symbol like + and a named callable like operator1. I'll leave the details to those other answers.

3
  • +1 You should start with "it's possible in PHP with the eval() language construct"... Technically it provides exactly the desired result asked in the question.
    – Armfoot
    Mar 24, 2016 at 12:00
  • @Armfoot: It's hard to tell where the focus of the question lies. The title emphasizes the “variable of type operator” aspect, and eval doesn't answer that aspect, since for eval the operators are just strings. Therefore I started with an explanation of why operator-typed variables would cause problems, before I start discussing alternatives.
    – MvG
    Mar 24, 2016 at 12:48
  • I understand your points, but consider that by stuffing everything into a string, you're basically implying that variable types are no longer relevant (since PHP was used to exemplify, this seems to be the question's focus), and in the end, you can place them in the same way as if some of those variables were of "type operator" while getting the same result... That's why I believe your suggestion provides the most accurate answer to the question.
    – Armfoot
    Mar 24, 2016 at 13:19
2

Algol 68 had exactly that feature. Your example in Algol 68 would look like this:

int number1 = 5;                              ¢ (Type 'Int') ¢
op operator1 = int (int a,b) a + b; ¢ (Type non-existent 'Operator') ¢
prio operator1 = 4;
int number2 = 5;                              ¢ (Type 'Int') ¢
op operator2 = int (int a,b) a * b;  ¢ (Type non-existent 'Operator') ¢
prio operator2 = 5;
int number3 = 8;                              ¢ (Type 'Int') ¢

int test = number1 operator1 number2 operator2 number3; ¢ 5 + 5 * 8. ¢

var_dump(test);

Your second example would look like this:

int number4 = 9;
op operator3 = bool (int a,b) a < b;
prio operator3 = 3;
if number1 $operator3 number4 then ¢ 5 < 9 (true) ¢
print(true)
fi

You will note that the operator symbols are defined and assigned method bodies that contain the operation desired. The operators and their operand are all typed, and the operators can be assigned priorities so the evaluation will happen in the correct order. You might also notice that there is some slight difference in fonting between the operator symbol and a variable symbol.

Actually, although the language is written using fonting, the machines of the day could not handle the fonts (paper tape and punched cards), and stropping was used. The program would probably be entered as:

'INT' NUMBER4 = 9;
'OP' 'OPERATOR3' = 'BOOL' ('INT' A,B) A < B;
'PRIO' 'OPERATOR3' = 3;
'IF' NUMBER1 'OPERATOR3' NUMBER4 'THEN' 'C' 5 < 9 'C'
PRINT('TRUE')
'FI'

You can also play interesting games with the language when you can define your own symbols for operators, which I exploited once, many years ago... [2].


References:

[1] Informal Introduction to Algol 68 by C.H.Lindsey and S.G. van der Meulen, North Holland, 1971.

[2] Algol 68 Phrases, A tool to aid compiler writing in Algol 68, B.C. Tompsett, International Conference on the Applications of Algol 68, At University of East Anglia, Norwich, UK, 1976..

Not the answer you're looking for? Browse other questions tagged or ask your own question.