A programozási nyelvek rendszerint támogatnak műveleteket: olyan nyelvi konstrukciókat, amelyek általánosságban a függvényekhez hasonlóan viselkednek, de szintaktikailag vagy szemantikailag eltérnek a szokásos függvényektől. A legegyszerűbb általános példák között vannak az aritmetikai (az összeadásra +, összehasonlításra >) és logikai műveletek (mint AND vagy más nyelveken &&). Összetettebb példaként megemlíthető az értékadás (rendszerint = vagy :=), tag hivatkozása egy rekordban vagy egy objektumban (rendszerint .), és a hatókör-feloldás művelet (leggyakrabban ::). A programozási nyelvek általában tartalmaznak egy sor beépített műveletet, és néhány nyelv lehetővé teszi a felhasználó által definiált műveletek használatát is.
Szintaxis Szintaktikailag a műveletek rendszerint különböznek a függvényektől. A legtöbb programnyelvben, a függvények egy speciális formátumú prefix műveletként értelmezhetőek, adott precedencia szinttel és asszociativitással, gyakran kötelező zárójelezést használva, pl. Func(a) (vagy (Func a) a LISP programozási nyelvben). A programozási nyelvek többsége támogatja a programozó által definiált függvények használatát, de ez önmagában nem teszi lehetővé a programozó által definiált műveletek használatát, ha azokhoz nem elegendő a prefix írásmód és az egyetlen (nyelvfüggő) precedencia szint. Szemantikailag a műveleteket úgy tekinthetjük, mint speciális formájú függvényeket, eltérő hívási írásmóddal és korlátozott számú paraméterrel (rendszerint 1 vagy 2).
A műveleti jel elhelyezkedése az operandusokhoz képest lehet prefix (Lengyel írásmód - operandusok előtti műveleti jel), infix (operandusok közötti műveleti jel) vagy posztfix (operandusok utáni műveleti jel), és egy műveleteket tartalmazó kifejezés szintaxisa függ a műveletek aritásától (az operandusok számától), a precedenciájuktól, és (ha értelmezhető) az asszociativitásuktól. A legtöbb programozási nyelv a kétváltozós (bináris) műveleteket és néhány egyváltozós (unáris) műveletet támogat, ritkán több operandusos műveletet is, mint például a ?: művelet a C nyelvben, amely háromváltozós (ternáris). Az egyváltozós műveletek általában prefix írásmódúak, mint például az egyváltozós mínusz (negáció) -x, esetenként posztfix írásmódúak, mint például az utólagos (változó kiértékelését követő) értéknövelés művelet x++; a kétváltozós műveletek pedig infix írásmódúak, mint például az x + y vagy az x = y. A kettőnél több operandust használó infix írásmódú műveletek további szimbólumokat igényelnek, mint például a háromváltozós, infix ?: művelet a C nyelvben (kóddal írva a ? b : c) – valójában erre ez az egyetlen közismert példa, emiatt gyakran a háromváltozós infix műveletként is emlegetik. A prefix és a posztfix írásmódú műveletek viszont könnyen használhatóak tetszőleges operandus számmal, például 1 2 3 4 +.
NéhaMathematica referenceMaxima reference bizonyos nyelvek egyes elemeit „matchfix” vagy „cirkumfix” műveletként is említik, hogy egyszerűsítsék a nyelv leírását vagy értelmezését. Egy cirkumfix műveleti jel, két vagy több részből áll (szimbólum párok), amik közrefogják az operandusokat. A cirkumfix műveleteknek a legmagasabb a precedenciája, a tartalmuk először kerül kiértékelésre, és ez az érték kerül felhasználásra a külső kifejezésben. Az említetteknek megfelelő legismertebb cirkumfix művelet a zárójelezés, amellyel azt jelezzük, hogy egy kifejezés mely részei értékelődjenek ki először. Egy másik példa a fizikából a Dirac féle Braket-jelölés, állapotvektorok skaláris szorzatának jelölésére. A cirkumfix műveleti jelek különösen hasznosak olyan műveletek jelölésére, amik sok, vagy változó számú operandust tartalmaznak.
Általában egy programozási nyelv specifikációja megadja a támogatott műveletek szintaxisát, míg az olyan nyelvek – mint például a Prolog – amik támogatják a programozó által definiált műveleteket, megkívánják, hogy a programozó definiálja a szintaxist.
Szemantika A műveletek szemantikája elsősorban az értéktől (jobb vagy bal), a kiértékelés stratégiájától és az argumentumok (operandusok) átadási módjától (érték szerinti, referencia szerinti vagy név szerinti) függ. A nem szokványos kiértékelési stratégia egy példája, a logikai kifejezések rövidzár kiértékelése, ahol a kifejezés tagjaiból csak annyi kerül kiértékelésre, amivel a logikai kifejezés értéke már biztosan megállapítható. Egyszerűen, egy műveleteket tartalmazó kifejezés valamilyen módon kiértékelésre kerül, és aztán az eredmény vagy csupán egy érték lesz (egy jobb-érték), vagy egy értékadást lehetővé tevő objektum (bal-érték).
Egyszerű esetben, ez ugyanolyan mint a szokásos függvényhívás; például az összeadás x + y általánosságban ekvivalens az összead(x, y) függvényhívással, a kisebb-mint összehasonlítás x < y pedig az kisebb_mint(x, y) hívással, vagyis az argumentmok a szokásos módon kerülnek kiértékelésre, aztán valamilyen (függvény kódja szerinti) kiértékelés történik, aminek az eredménye lesz a visszaadott érték. Mindazonáltal a szemantika lehet jelentősen eltérő is. Például, az a = b értékadásnál a „cél” a tag nem kerül kiértékelésre, hanem ehelyett az elhelyezkedése (memóriacíme) kerül felhasználásra b értékének tárolásához – a referencia szerinti paraméterátadás szemantikájának megfelelően. Továbbá, egy értékadás lehet egy utasítás is (explicit megadott érték nélkül), vagy egy kifejezés (értékkel), ahol az érték maga lehet jobb-érték (csak egy érték) vagy bal-érték (aminek értéket lehet adni). További példa lehet, a hatókör-feloldás művelet :: és a tag hivatkozás művelet . (kóddal Foo::Bar vagy a.b) amik nem értékekkel végeznek műveleteket, hanem nevekkel, lényegében a név szerinti paraméterátadás szemantikájával, és a kiértékelt eredményük is egy név lesz.
Bal-értékek értékként való használata műveleteket tartalmazó kifejezésekben különösen említésre méltó a tömbök indexelésénél és az unáris inkrementáló és dekrementáló műveleteknél. C nyelvben például a következő utasítások érvényesek és jól definiáltak, és azért használhatóak ebben a formában, mert ezek a műveletek bal-értéket adnak eredményül: y = ++x++; x = ++a[i];
Egy fontos felhasználás, amikor egy balra-asszociatív bináris (két operandusú) művelet módosítja a bal oldali operandust (vagy ott mellékhatásokat okoz) és aztán eredményként erre az operandusra értékelődik ki bal-értékként (vagy fordítva, egy jobbra-asszociatív művelet a jobb oldali operandusára vonatkozóan, habár ez ritkább). Ez megengedi műveletek egy olyan sorozatát, amelyek mindannyian hatással vannak az eredeti operandusra, lehetővé téve egy metódus-láncoláshoz hasonló folyó-interfész kialakítását. Ennek egy gyakori példája a << művelet a C++ iostream könyvtárából, amely lehetővé teszi a folyó kimenetet, a következő példa szerint: cout << "Hello" << " " << "world!" << endl;
Felhasználó által definiált műveletek Egy programnyelv tartalmazhat adott számú beépített műveletet (pl. +, -, *, <, <=, !, =, stb. a C és C++ nyelvekben), vagy lehetővé teheti programozó által definiált műveletek létrehozását (pl. az F#, az OCaml vagy a Haskell). Néhány programnyelvben a műveleti jelek csak speciális karakterek lehetnek, mint például a + vagy a := míg mások megengednek neveket is, mint a div (pl. a Pascal nyelvben).
A legtöbb nyelv rendelkezik beépített (a nyelv részét képező) műveletek egy csoportjával, de nem teszi lehetővé a felhasználó által definiált műveletek használatát, mivel ez jelentősen megnehezíti a forráskód feldolgozását. Egy új művelet bevezetése megváltoztatja a nyelv lexikális specifikációját, ami miatt módosul a lexikális elemzés. A művelet aritása (az operandusok száma) és precedenciája része lesz a nyelv kifejezés-szintaxisának, ezért megváltozik a kifejezés-szintű elemzés is. Például egy @ művelet hozzáadása, megkívánja ennek a karakternek a lexikai felismerését és tokenizálását, és a kifejezés-struktúra (szintaxis fa) függ majd ennek a műveletnek az aritásától és precedenciájától. A legtöbb nyelv a műveletek használatát csak a beépített adattípusokra engedi meg, de vannak amik megengedik a nyelvben létező műveletek használatát felhasználó által definiált típusokra is; ezt hívjuk művelet túlterhelésnek.
Néhány programnyelv lehetővé teszi új műveletek definiálását fordítási időben, de akár futásidőben is. Ez történhet magának a nyelvnek a használatával, vagy más esetekben meta-programozással (ahol a műveleteket egy külön nyelven specifikáljuk). Új műveletek definiálása (különösen futásidőben), gyakran lehetetlenné teszi a programok korrekt statikus elemzését, mivel a nyelv szintaxisa Turing-komplett lehet (egy Turing-gépen szimulálható) így még magának a szintaxis fának a létrehozása is a leállási probléma megoldását kívánná meg (annak eldöntése, hogy egy program egy adott inputtal véges időn belül leáll-e), ami lehetetlen (tetszőleges program-input párokra a megoldhatatlanságot Alan Turing 1936-ban bizonyította). Ez például előfordul a Perl programnyelv és a Lisp néhány dialektusa esetén.
Példák
A függvényektől szintaktikailag eltérő általános példák a matematikai, számokkal végzett műveletek - mint például a > a „nagyobb mint” műveletre - ahol a műveleti jelek nem felelnek meg a nyelv függvény azonosítókra vonatkozó általános szabályainak és használatuk a függvények hívási szintaxisának. Függvényként a „nagyobb mint” általánosságban egy azonosítóval lenne elnevezve, mint például az nm vagy a nagyobb_mint és függvény szintaxissal lenne meghívva is: nagyobb_mint(x, y). Ehelyett, a művelet egy speciális karaktert > használ (ami külön kerül tokenizálásra a lexikális elemzés alatt), és infix írásmódot (a műveleti jel az operandusok között van), a következő módon: x > y.
Szemantikailag eltérő (a paraméter átadás módjában) általános példák a logikai műveletek, amikre gyakran jellemző a rövidzár kiértékelés: például egy rövidzárral kiértékelt X AND Y konjunkció esetén csak akkor kerül kiértékelésre a második operandus, ha az első operandus értéke nem „hamis”, eltérően attól, ahogyan a nyelvben a függvényhívások érték-szerinti paraméterátadással történnek (pl. AND(X,Y) függvényhívás esetén az érték-szerinti paraméterátadás miatt, mindenképpen kiértékelésre kerülne mindkét operandus). Az X AND Y művelet viselkedése így inkább az if/then/else nyelvi konstrukcióhoz hasonló.
A következő kódpéldában IF ORDER_DATE > "12/31/2011" AND ORDER_DATE < "01/01/2013" THEN CONTINUE ELSE STOP, a műveletek a > (nagyobb mint), az AND (logikai és), és a < (kisebb mint).
Néhány kevésbé általános művelet: Szekvencia művelet (Comma operator) Dereferálás művelet (Dereference operator)
Fordítás (compilation) A fordító a műveleteket és a függvényeket szubrutin hívásokkal vagy sorosan kifejtett kóddal (ahol minden hívási helyre beillesztésre kerül a teljes hívott kód) valósítja meg a lefordított programban. Néhány a nyelv által támogatott beépített művelet közvetlenül lefordítható kisszámú, a CPU által ismert gépi kódú utasításra, míg másoknak (pl. a string összefűzésre használt + műveletnek) sokkal összetettebb lehet a megvalósítása.
Művelet túlterhelés
Néhány programozási nyelvben egy művelet lehet ad hoc módon polimorfikus, vagyis lehet (különböző) értelmezése többféle adattípusra, (mint például a Java-ban, ahol a + művelet egyaránt használatos számok összeadására és szövegek összefűzésére is). Az ilyen műveleteket nevezzük túlterheltnek. Az olyan nyelvekben, amik támogatják a programozó általi művelet túlterhelést (mint például a C++) de csak korlátozott számú beépített művelettel rendelkeznek, gyakorta alkalmazzák a művelet túlterhelést, a műveletek egyedi igények szerinti használata érdekében.
Operandus típuskényszerítés