تأثير جانبي (علم الحاسوب)

في علم الكمبيوتر، يقال ان دالة أو تعبير لها آثار جانبية إذا غيرت بعض الحالات خارج نطاقها أو لديها تفاعل ملحوظ مع دوال الاتصال الخاصة بها أو بالعالم الخارجي إلى جانب إرجاع قيمة.على سبيل المثال، قد تقوم دالة معينة بتعديل متغير شامل المجال أو متغير ثابت، أو تعديل إحدى المتغيرات الوسيطة، أو رفع استثناء، أو كتابة البيانات إلى شاشة أو ملف، أو قراءة البيانات من لوحة مفاتيح أو ملف، أو استدعاء وظائف أخرى ذات تأثير جانبي. في وجود آثار جانبية، قد يعتمد سلوك البرنامج على التاريخ. والذي هو، ترتيب مسائل التقييم. يتطلب فهم وتصحيح دالة ذات تأثيرات جانبية معرفة السياق وتاريخه المحتمل. ويقال إن الدالة أو التعبير -دون تأثيرات جانبية- تكون نقية.

تعد التأثيرات الجانبية أكثر الطرق شيوعًا في تفاعل البرنامج مع العالم الخارجي (الأشخاص، أنظمة الملفات، أجهزة الكمبيوتر الأخرى على الشبكات). تعتمد درجة استخدام التأثيرات الجانبية على نمط البرمجة. تشتهر البرمجة الامرية Imperative programming لاستخدامها المتكرر للآثار الجانبية.

في البرمجة الوظيفية نادرا ما تستخدم الآثار الجانبية. ويؤدي عدم وجود آثار جانبية إلى تسهيل إجراء عمليات التحقق الرسمية من البرنامج. لا تحد اللغات الوظيفية مثل Standard ML و Scheme و Scala من الآثار الجانبية، ولكن من المعتاد أن يتجنبها المبرمجون. تعبر اللغة الوظيفية Haskell عن تأثيرات جانبية مثلI/O وحسابات أخرى ذات أهمية باستخدام monadic actions.

يجب أن يكون مبرمجي لغة تجميع Assembly على دراية بالآثار الجانبية المخفية - التعليمات التي تقوم بتعديل أجزاء من حالة المعالج التي لم يتم ذكرها في instruction's mnemonic. المثال الكلاسيكي لأثر جانبي مخفي هو تعليمة حسابية تقوم بتعديل شرط الأكواد بشكل ضمني (تأثير جانبي مخفي) أثناء تعديلها بشكل صريح ل register (الأثر الصريح). أحد العوائق المحتملة لمجموعة تعليمات ذات تأثيرات جانبية مخفية هو أنه إذا كان للعديد من التعليمات تأثيرات جانبية على حالة واحدة، مثل شرط الأكواد، فإن المنطق مطلوب لتحديث تلك الحالة بشكل تسلسلي وقد يؤدي إلى ضعف الأداء. المشكلة عصيبة بشكل خاص على بعض المعالجات المصممة مع خط الانابيب (منذ عام 1990) أو مع out-of-order execution . قد يتطلب مثل هذا المعالج دوائر تحكم إضافية لاكتشاف الآثار الجانبية المخفية وتوقف خط الانابيب إذا كانت التعليمات التالية تعتمد على نتائج تلك التأثيرات.

شفافية مرجعية 
إن غياب الآثار الجانبية شرط ضروري، ولكنه غير كاف، للشفافية المرجعية. الشفافية المرجعية تعني أنه يمكن استبدال تعبير (مثل استدعاء دالة) بقيمته. وهذا يتطلب أن يكون التعبير عديم الحالة، أي أن التعبير يجب أن يكون نقيًا (ليس له آثار جانبية) وحتمية أي (يعطي دائمًا نفس القيمة لنفس المدخلات). ويمكن لوظيفة بدون آثار جانبية أن ترجع قيمًا مختلفة وفقًا لتاريخها أو بيئتها، على سبيل المثال إذا كان ناتجها يعتمد على قيمة متغير ثابت محلي أو متغير شامل النطاق على التوالي.

الآثار الجانبية المؤقتة 
عادةً ما يتم تجاهل الآثار الجانبية الناتجة عن الوقت المستغرق لتنفيذ العملية عند مناقشة التأثيرات الجانبية والشفافية المرجعية. هناك بعض الحالات، مثل توقيت الأجهزة أو الاختبار، حيث يتم إدراج العمليات خصيصًا لتأثيراتها الجانبية المؤقتة، على سبيل المثالsleep(5000) أو for (int i = 0; i < 10000; ++i) {};لا تغير هذه التعليمات الحالة الا انها تأخذ مقدار من الوقت لإكمالها.

Idempotence 

يقال أن الدالة f مع التأثيرات الجانبية تكون idempotent تحت التركيب التتابعي f; f؛ إذا قمنا باستدعاء الدالة مرتين بنفس القيم فيعتبر الاستدعاء الثاني ليس لديه أي تاثيرات جانبية وسيقوم بارجاع نفس القيمة (بافتراض انه ليس هناك أي اجراءات بين نهاية الاستدعاء الأول وبداية الاستدعاء الثاني). فعلى سبيل المثال، انظر إلى كود بايثون التالي:x = 0

def setx(n):
    global x
    x = n

setx(5)
setx(5)
هنا setx عبارة عن idempotent لأن الاستدعاء الثاني إلى setx (ببنفس ال ارقيومنت) لا يغير حالة البرنامج المرئي: x تم تعيينه بالفعل على 5 في الاستدعاء الأول، ويتم تعيينه مرة أخرى إلى 5 في الاستدعاء الثاني، وبالتالي حافظ على نفس القيمة. لاحظ أن هذا يختلف عن idempotence تحت تركيب الوظيفة f ∘ f. على سبيل المثال، القيمة المطلقة هي idempotent تحت تركيب الدالة:

def abs(n):
    if n < 0:
        return -n
    else:
        return n

abs(-5) == abs(abs(-5)) == abs(5) == 5

على سبيل المثال 
أحد البراهين الشائعة لسلوك التأثير الجانبي هو عامل التعيين في سي++. على سبيل المثال، تعيد عملية التعيين المعامل الذي على اليمين ولها تأثير جانبي لتعيين تلك القيمة لمتغير. وهذا يسمح نحويا بتعدد التعيين:
int i, j;
i = j = 3;
لأن المشغل right associates ، فهذا يعادل
int i, j;
i = (j = 3); // j = 3 returns 3, which then gets assigned to i
حيث يتم تعيين نتيجة تعيين 3 إلى j ثم يتم تعيينها في i. هذا قد يسبب تشويش للمبرمجين المبتدئين.
while (b == 10) {} // tests if b evaluates to 10
مع
while (b = 10) {} // = returns 10 which automatically casts to true so the test is always true

المراجع 

برمجة
برمجة وظيفية
نظرية لغات البرمجة