Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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?

share|improve this question
24  
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. – thorsten müller 2 days ago
3  
Forth and other stack-based languages like Factor do this, kind of. They structure their syntax so every user-defined function is basically an operator... but because it's RPN, the operators behave differently than you might expect. – kbelder 2 days ago
6  
@MartinMaat Functional languages do this a lot. – Thorbjørn Ravn Andersen yesterday
4  
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) – kai yesterday
2  
@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. – Bryan Oakley 6 hours ago

15 Answers 15

up vote 92 down vote accepted

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.

share|improve this answer
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 2 days ago
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 2 days ago
3  
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 2 days ago
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! – Martin Capodici 2 days ago
12  
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. yesterday

You can do this in tcl:

set number1    5
set operator1  +
set number2    5
set operator2  *
set number3    8

set test [expr $number1 $operator1 $number2 $operator2 $number3]
share|improve this answer
4  
+1 (but if you'd want to do this is a different question:) – Ajasja yesterday
1  
I've heard that TCL doesn't even parse a given line until right before it runs that line, and so syntax errors inside if statements won't be an issue until the condition passes. I'm not surprised TCL's wackiness allows literal assignment of operators to identifiers... I'd love to see the parser code. – tac 12 hours ago
    
@tac: it only takes 12 rules to fully describe the tcl parser. Printed, it's slightly over three pages. It's a marvel of simplicity. A lot of people have a hard time wrapping their head around it, but if you're one of of those that fully groks the language it can be a real joy to program in. – Bryan Oakley 11 hours ago

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 :

“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

share|improve this answer
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. yesterday
1  
Example to OP's question: (let ((opp #'+)) (print (apply opp '(1 2)))) – Kasper van den Berg yesterday
1  
No mention of Common Lisp? – coredump yesterday
2  
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 yesterday
1  
CL would perfectly fit in your "Real-world languages" section. – coredump yesterday

Sure, most shells can do this:

OP=-f
if [ $OP ~/.bashrc ]; then
  echo FOUND FILE
fi

The reason why this isn't usually done is that it forces the language runtime to operate entirely in interpreter mode, never knowing what operations will be performed the next time the same line of code is executed. Usually this amount of flexibility is not necessary, and it's horribly expensive compared to just compile the code into machine code or byte code, and running that.

share|improve this answer
7  
Why, passing a function around does not require operation in interpreter mode, and parsing an infix operator as a user function is supported in more than one compiled language. The shell example of textual substitution is nice. But you can do the same using #define in plain C. – 9000 2 days ago
    
It also seems to majorly violate the KISS principle... – jamesqf 2 days ago
    
Actually you can still optimise such code as long as you deoptimize your optimized code as soon as the function is redefined. This is pretty similar to what HotSpot does for Java code to inline virtual functions after all. There are people working on this for Python (and probably other languages as well), but it's all more academic projects. Mostly because the projects where python is used either aren't that performance sensitive or do the performance relevant parts in C. – Voo yesterday
3  
Technically, -f is not a shell operator; it's just a string argument to test (which unfortunately can be spelled [). – chepner 14 hours ago
1  
It seems to lead many beginning shell programmers to think that [ ... ] is part of the syntax of the if statement, leading to syntax errors like [ [ foo -lt 3 ] && [ bar -gt 9 ] ], and missing the fact that if statements work on the exit code of any command, not just comparisons enclosed in brackets. – chepner 12 hours ago

$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.

share|improve this answer
    
+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 16 hours ago
    
@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 15 hours ago
    
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 15 hours ago

User-definable operators have been around for a long time. ML did it long before Haskell was invented, and SNOBOL 4 did it long before ML was invented.

In SNOBOL 4, I could do something like:

OPSYN('@', find, 2)

...and then use @ as an operator, so I'd so something like my_table @ my_key to invoke find(my_table, my_key).

In case it wasn't clear, that final 2 indicates that this should be a binary operator. You could also specify 1 to create unary operators. You could use this to define new operators, or (if you were sufficiently insane) redefine nearly any existing operator in the language (the sole exception being its implicit pattern matching that happened when you put a pattern after a string).

For anybody who cares how this worked at the level of SNOBOL's own parser and such: they reserved a number of operators, but didn't designate any behavior for them. You were free to use any of these as a target for opsyn, to define behavior for them. Each operator had a pre-defined precedence, so you'd choose the operator name from those at the precedence level you wanted.

This is significantly different from ML, for example, where you can choose (more or less) arbitrary names for your operators, and designate the precedence and associativity when you define the operator. For example:

infix 1 op1;    // left-associative, low precedence
infix 6 op2;    // left-associative, same precedence as additive operators
infixr 8 op3;   // right associative, higher precedence than multiplicative operators
share|improve this answer
6  
Yay for SNOBOL! lemme go convice my boss to use this language in our products, now. – tac 2 days ago
1  
@9000: I don't know if Forth qualifies as "deeper", but yeah--in fact, much of a typical Forth implementation was written in Forth. – Jerry Coffin 2 days ago
1  
Deeper down to the simplicity lane, so to say. Snobol is more about everything being a string, and Forth is about everything being a word :) It's still newer (1970) than Snobol 4 (1967). – 9000 2 days ago
1  
@AndrewPiliser: Nope--the third parameter must be either 1 or 2. – Jerry Coffin 2 days ago
1  
@9000: Every thing is a string, every operation is a pattern substitution. Beautiful simplicity, awesome power. I actually read something about someone using it fairly recently in a commercial project. – Jörg W Mittag 2 days ago

This is possible in R as well. Any function whose name begins and ends with % is treated as a binary operator by the parser. A few examples exist in base R, like the %in% function. Another notable example is the %>% pipe operator from the magrittr package.

You could argue that this isn't a special "type," but under the standard S3 class system type dispatch is based on the function name anyway. And functions are first-class objects in R.

share|improve this answer

I'd like to mention Forth.

In Forth, roughly put all you have is words. Words are compiled in order to create new words. So are the operators. So assume you define a type "complex number" and you wish to implement an operator called C+ to add two such numbers, it could look like this:

: C+   ROT + ROT ROT + SWAP ;

Forth uses a stack as it's main domain of data manipulation, so this would happen on the stack if you would call C+ with arguments:

Arguments on Stack: r1 i1 r2 i2
1. ROT     -->      r1 r2 i2 i1
2. +       -->      r1 r2 (i1+i2)
3. ROT ROT -->      (i1+i2) r1 r2
4. +       -->      (i1+i2) (r1+r2)
5. SWAP    -->      (r1+r2) (i1+i2)

You are also basically free to redefine existing operators like + in the same fundamental way shown above. Define it as you need. In bigger applications, you just have to consider how your dictionaries are built, i.e. what is relying on what.

share|improve this answer

perl and many shells allow you to concatenate substrings into a longer string such as:

'5 * 4 + 3'

Then either eval $string

Or exec $string

Will evaluate the expression.

share|improve this answer
5  
Correct and relevant, though it should be emphasised that this is a great deal more brittle than other ways of defining operators. – leftaroundabout 2 days ago

Even C has this:

int add(int a, int b) { return a+b; }
int mul(int a, int b) { return a*b; }

int (*operator)(int,int) = &add;
printf("%d\n", operator(3,4));

operator = &mul;
printf("%d\n", operator(3,4));
share|improve this answer
    
Isn't it C++ syntax? – Mahdi yesterday
2  
@Mahdi: No, operator would be a reserved word in C++, and it's used as an ordinary identifier here. This is definitely C. But rename operator to op and it also becomes valid C++ – MSalters yesterday
1  
I mean do we have std:: in C? Isn't it a static method call? I don't know C++ at all, so I might be wrong. – Mahdi yesterday
    
@Mahdi: My bad, fixed. Thanks. – MSalters yesterday
    
Using the preprocessor with #define operator1 + might be closer to the OP. – Potatoswatter 2 hours ago

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 all 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..

share|improve this answer

What you are looking for in many languages is basically a closure, or lambda function.

To take your example

<?php
$number1 = 5;   // (Type 'Int')
$operator1 = function(a, b) { return a + b}; // (Type non-existent 'Operator')
$number2 = 5;   // (Type 'Int')
$operator2 = function(a, b) { return a * b}; // (Type non-existent 'Operator')
$number3 = 8;   // (Type 'Int')

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

var_dump($test);
?>

Now clearly the syntax is not as nice as what you are looking for, but the result is much the same, the closure just wraps the functionality you want to be able to swap out.

share|improve this answer
4  
Closures and lambda functions are different concepts. What you wrote are lambda functions; they don't "close" over anything in the environment. Even then, the general concept isn't really about lambdas either -- maybe PHP doesn't allow it, but in other languages you can simply say $operator1 = + and there is no lambda involved; the function is named, and its name is + :) You wpuldn't be defining a new anonymous function, but simply binding the alias $operator1 to the existing function +. This is very common in many functional languages. – Andres F. yesterday

C# would allow something of this sort:

int a = 1;    //compiler determines this is "int"
int b = 2;    //compiler determines this is "int"

Func<int, int, int> operation = (x, y) => x + y;

return operation(a, b);    //returns 3

Technically, you're not storing the operator; you're storing a function that makes use of the operator. Still, the end result is more or less what you're after.

share|improve this answer

The only reason to have operators in a programming language is to make things easier to read and understand. The effect is small for a single line of code, but has a cumulative effect on large programs.

 a = (1 + 2) - (3 + 4);

is easier to read than

 a = Math.Subtract(Math.Add(1, 2), Math.Add(3, 4));

An operator compresses a simple operation into a single symbol - which is instantly recognized by the human vision system. Their meaning and usage is ingrained into programmers, so the meaning is clear with zero effort.

 a = x + y;

Because operators can only be used in expressions, the compiler can spot commonly-made mistakes.

a = apply(1, 2, +);            // could be a typo
a = apply(1, 2, Math.Add);     // definitely not a typo

Simplicity is lost if you start using operators outside their normal context.

Although it would be possible to treat operators just like any other function, the whole purpose of operators is to be special.

share|improve this answer

Another example is Scala where operators are basically functions.

The idea is extended so much that you can pretty much use them like functions:

scala> 5.+(4)
res0: Double = 9.0

The idea is that you can call any function which takes one argument with separating the function name by spaces:

scala> 1.to(2)
res1: scala.collection.immutable.Range.Inclusive = Range(1, 2)

scala> 1 to 2
res2: scala.collection.immutable.Range.Inclusive = Range(1, 2)

Taking into account that binary operators are no different than functions with 2 arguments, Scala extends this idea to make operators no different than functions.

share|improve this answer

protected by Community yesterday

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site.

Would you like to answer one of these unanswered questions instead?

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