زبان برنامه‌نویسی

المان‌ها

تمام زبان‌های برنامه‌نویسی تعدادی بلوک‌های ابتدایی برای توضیح داده و پردازش یا تبدیل آن‌ها (مانند جمع کردن دو عدد با انتخاب یک عضو از یک مجموعه) دارند. این «عناصر ابتدایی» به وسیلهٔ قوانین معناشناسی و دستوری تعریف می‌شوند که ساختار و معنای مربوط را توضیح می‌دهند.

معناشناسی ایستا

معناشناسی ایستا محدودیت‌هایی بر روی ساختار مجاز متن‌ها تعیین می‌کند که بیان آن‌ها در فرمول دستوری استاندارد مشکل یا غیرممکن است. مهم‌ترین این محدودیت‌ها به وسیله سیستم نوع‌گذاری انجام می‌شود. برخلاف باور برخی از برنامه‌نویسان محدودیت‌های اعمال شده، اعمال نشده‌اند بلکه در واقع ناشی از زیرساخت‌های آن زبان برنامه‌نویسی هستند تا سرعت پردازش را افزایش دهند و قدرت پردازش پردازنده را برای تایپ‌کستینگ‌های خودکار متعدد صرف نکنند. استفاده از روش ایستا صرفاً سرعت پردازش‌هایی را افزایش می‌دهد که در کسری از ثانیه به محاسبه حجم انبوهی از داده‌ها نیاز دارند. این موضوع بهبود پردازش برای مثال خودش را در گرافیک‌های سنگین بازی‌های کامپیوتری نشان می‌دهد همچنین برای مثال در سرورهای بک‌اند (پشتی) بانکداری با حجم انبوهی از ثبت ترنزاکشن‌ها که از زبان ایستای جاوا استفاده می‌شود اما حتی در موضوع گرافیک جاوا به دلیل استفاده از گاربیج‌کالکشن یا جمع‌آوری زباله و البته استفاده از ماشین مجازی برای مدیریت حافظه سرعت پردازشی C++ را ندارد از طرفی استفاده دات نت از زبان میانجی IL برای پشتیبانی از زبان‌های متعدد مانند C# یا F# و کامپایل نکردن مستقیم به باینری ماشینی باعث می‌شود که سرعت پردازش C# نیز از جاوا پایین‌تر باشد اما امکانات گسترده‌تری را در زمینه‌های خاصی مانند طراحی وب یا ساخت برنامه‌های سبک‌پردازشی روی سیستم‌عامل را فراهم کند اگرچه با صرف میزان بیشتری از منابع حافظه و پردازشی.

سیستم نوع‌گذاری

یک سیستم نوع‌گذاری مشخص می‌کند که یک زبان برنامه‌نویسی چگونه مقادیر و عبارات را در نوع(type) دسته‌بندی می‌کند، چگونه می‌تواند آن نوع‌ها را تغییر دهد و رفتار متقابل آن‌ها چگونه‌است. این کار عموماً توضیح داده ساختارهایی که می‌توانند در آن زبان ایجاد شوند را شامل می‌شود. طراحی و مطالعه سیستم‌های نوع‌گذاری به وسیلهٔ ریاضیات قراردادی را تئوری نوع‌گذاری گویند.

زبان‌های نوع‌گذاری شده و بدون نوع‌گذاری

یک زبان نوع‌گذاری شده‌است اگر مشخصات هر عملیات، نوع داده‌های قابل اجرا توسط آن را با نشان دادن نوع‌هایی که برای آن‌ها قابل اجرا نیست، تعیین کند. برای مثال، «این متن درون گیومه قرار دارد» یک رشته‌است. در غالب زبان‌های برنامه‌نویسی، تقسیم یک رشته با یک عدد معنایی ندارد. در نتیجه غالب زبان‌های برنامه‌نویسی مدرن ممکن است اجرای این عملیات را توسط برنامه‌ها رد کنند. در برخی زبان‌ها، عبارات بی‌معنی ممکن است هنگام ترجمه(compile) پیدا شود (چک‌کننده نوع ایستا)، و توسط کامپایلر رد شود، در حالی که در سایر برنامه‌ها، هنگام اجرا پیدا شود. (چک‌کننده نوع دینامیک) که به استثنای در حال اجرا منتج شود(runtime exception).
حالت خاص زبان‌های نوع دار زبان‌های تک نوعند. این زبان‌ها غالباً اسکریپتی یا مارک آپ هستند، مانند rexx وSGML و فقط یک داده گونه دارند—غالباً رشته‌های کاراکتری که هم برای داده‌های عددی و هم برای داده‌های سمبلی کاربرد دارند.
در مقابل، یک زبان بدون نوع گذاری، مثل اکثر زبان‌های اسمبلی، این امکان را می‌دهد که هر عملیاتی روی هر داده‌ای انجام شود، که معمولاً دنباله‌ای از بیت‌ها با طول‌های متفاوت در نظر گرفته می‌شوند. زبان‌های سطح بالا که بی نوع هستند شامل زبان‌های ساده رایانه‌ای و برخی از انواع زبان‌های نسل چهارم.
در عمل، در حالیکه تعداد بسیار کمی از دیدگاه نظریه نوع، نوع‌گذاری شده تلقی می‌شوند (چک کردن یا رد کردن تمام عملیات‌ها)، غالب زبان‌های امروزی درجه‌ای از نوع‌گذاری را فراهم می‌کنند. بسیاری از زبان‌های تولیدکننده راهی را برای گذشتن یا موقوف کردن سیستم نوع فراهم می‌کنند.

نوع‌گذاری ایستا و پویا

در نوع‌گذاری ایستا تمام عبارات نوع‌های خود را قبل از اجرای برنامه تعیین می‌کنند (معمولاً در زمان کامپایل). برای مثال، ۱ و (۲+۲) عبارات عددی هستند؛ آن‌ها نمی‌توانند به تابعی که نیاز به یک رشته دارد داده شوند، یا در متغیری که تعریف شده تا تاریخ را نگه دارد، ذخیره شوند.
زبان‌های نوع‌گذاری شده ایستا می‌توانند با مانیفست نوع‌گذاری شوند یا با استفاده از نوع استنباط شوند. در حالت اول، برنامه‌نویس بیشتر صریحاً نوع‌ها را در جایگاه‌های متنی مشخص می‌نویسد (برای مثال، در تعریف متغیرها). در حالت دوم، کامپایلر نوع عبارات و تعریف‌ها را بر اساس متن استنباط می‌کند. غالب زبان‌های مسیر اصلی(mainstream) ایستا نوع‌گذاری شده، مانند C#,C++ و Java، با مانیفست نوع‌گذاری می‌شوند

نوع‌گذاری قوی و ضعیف

نوع‌گذاری ضعیف این امکان را ایجاد می‌کند که با متغیری به جای متغیری دیگر برخورد شود، برای مثال رفتار با یک رشته به عنوان یک عدد. این ویژگی بعضی اوقات ممکن است مفید باشد، اما ممکن است باعث ایجاد برخی مشکلات برنامه شود که موقع کامپایل و حتی اجرا پنهان بمانند.
نوع‌گذاری قوی مانع رخ دادن مشکل فوق می‌شود. تلاش برای انجام عملیات روی نوع نادرست متغیر منجر به رخ دادن خطا می‌شود. زبان‌هایی که نوع‌گذاری قوی دارند غالباً با نام «نوع-امن» یا امن شناخته می‌شوند.
تمام تعاریف جایگزین برای «ضعیف نوع‌گذاری شده» به زبان‌ها اشاره می‌کند، مثل perl, JavaScript, C++، که اجازه تعداد زیادی تبدیل نوع داخلی را می‌دهند. در جاوااسکریپت، برای مثال، عبارت ۲*x به صورت ضمنی x را به عدد تبدیل می‌کند، و این تبدیل موفقیت‌آمیز خواهد بود حتی اگر x خالی، تعریف نشده، یک آرایه، یا رشته‌ای از حروف باشد. چنین تبدیلات ضمنی غالباً مفیدند، اما خطاهای برنامه‌نویسی را پنهان می‌کنند.
قوی و ایستا در حال حاضر عموماً دو مفهوم متعامد فرض می‌شوند، اما استفاده در ادبیات تفاوت دارد، برخی عبارت «قوی نوع‌گذاری شده» را به کار می‌برند و منظورشان قوی، ایستایی نوع‌گذاری شده‌است، و یا، حتی گیچ‌کننده تر، منظورشان همان ایستایی نوع‌گذاری شده‌است؛ بنابراین C هم قوی نوع‌گذاری شده و هم ضعیف و ایستایی نوع‌گذاری شده نامیده می‌شود.

معناشناسی اجرا

وقتی که داده مشخص شد، ماشین باید هدایت شود تا عملیات‌ها را روی داده انجام دهد. معناشناسی اجرا ی یک زبان تعیین می‌کند که چگونه و چه زمانی ساختارهای گوناگون یک زبان باید رفتار برنامه را ایجاد کنند.
برای مثال، معناشناسی ممکن است استراتژی را که به وسیله آن عبارات ارزیابی می‌شوند را تعریف کند یا حالتی را که ساختارهای کنترلی تحت شرایطی دستورها را اجرا می‌کنند.

کتابخانه هسته

اغلب زبان‌های برنامه‌نویسی یک کتابخانه هسته مرتبط دارند (گاهی اوقات "کتابخانه استاندارد" نامیده می‌شوند، مخصوصاً وقتی که به عنوان قسمتی از یک زبان استاندارد ارائه شده باشد)، که به‌طور قراردادی توسط تمام پیاده‌سازی‌های زبان در دسترس قرار گرفته باشند. کتابخانه هسته معمولاً تعریف الگوریتم‌ها، داده ساختارها و مکانیزم‌های ورودی و خروجی پرکاربرد را در خود دارد.
کاربران یک زبان، غالباً با کتابخانه هسته به عنوان قسمتی از آن رفتار می‌کنند، اگرچه طراحان ممکن است با آن به صورت یک مفهوم مجزا رفتار کرده باشند. بسیاری از خصوصیات زبان هسته‌ای را مشخص می‌کنند که باید در تمام پیاده‌سازی‌ها موجود باشند، و در زبان‌های استاندارد شده این کتابخانه هسته ممکن است نیاز باشد؛ بنابراین خط بین زبان و کتابخانه هسته آن از زبانی به زبان دیگر متفاوت است. درواقع، برخی زبان‌ها به گونه‌ای تعریف شده‌اند که برخی از ساختارهای دستوری بدون اشاره به کتابخانه هسته قابل استفاده نیستند.
برای مثال در جاوا، یک رشته به عنوان نمونه‌ای از کلاس “java.lang.String” تعریف شده‌است؛ مشابهاً، در سمال تاک(smalltalk) یک تابع بی‌نام (یک "بلاک") نمونه‌ای از کلاس BlockContext کتابخانه می‌سازد. به‌طور معکوس، Scheme دارای چندین زیرمجموعه مرتبط برای ایجاد سایر ماکروهای زبان می‌باشد، و در نتیجه طراحان زبان حتی این زحمت را نیز تحمل نمی‌کنند که بگویند کدام قسمت زبان به عنوان ساختارهای زبان باید پیاده‌سازی شوند، و کدام یک به عنوان بخشی ازکتابخانه.

مدیریت منابع و نشت حافظه

مدیریت منابع به دوشاخه پردازش و حافظه (موقت یا دائم یا دورگه (موقت مجازی)) تقسیم می‌شود و در یک عبارت خلاصه می‌شود:در ایده‌آل‌ترین حالت صرفه‌جویی در حافظه، میزان پردازش را افزایش می‌دهد و صرفه‌جویی در پردازش، حافظه مصرفی را افزایش می‌دهد (این موضوع در بانک‌های اطلاعاتی با عنوان نرمالیزیشن و دینرمالیزیشن شناخته می‌شود). مقدار حافظه اختصاص داده شده برای یک متغیر عدد کوتاه ۸ بیتی در زبان ایستا قابل مقایسه با یک متغیر طولانی ۳۲بیتی نیست که این موضوع در زبان ایستا به صراحت و مستقیم توسط کد نوشته‌شده برنامه‌نویس تعیین می‌شود (در زبان پویا نیاز به اجرای کد زیرساختاری اضافی کوچک دیگری هست تا تشخیص دهد که چه نوع مقداری به این متغیر اختصاص داده شده؟ استرینگ رشته یا عدد بزرگ یا کوچک؟). این تفاوت ظرفیت عددها در زمان تفکیک برنامه‌های ۶۴بیتی در مقایسه با برنامه‌های ۳۲بیتی حتی برای عموم مردم کاملاً فاحش می‌شود (با اشاره به این نکته حاشیه‌ای که برنامه‌های ۳۲بیتی به دلیل محدودیت عددی توان اندازه‌گیری حافظه بیشتر از ۴میلیارد و اندی بایت را ندارند که معادل ۴گیگابایت است که درمبنای ۲ به توان ۳۲ محاسبه شده باشد در نتیجه سیستم‌عامل ۳۲بیتی نمی‌تواند روی یک سیستم با رَم بیش از ۴ گیگابایت نصب شود).
تفاوت زبان‌های پویا با ایستا فقط در تعریف نوع متغیر نیست بلکه همان‌طور که دربارهٔ اعداد مطرح شد به زمینه مدیریت حافظه یا مموری منیجمنت نیز کشیده می‌شود همچنین بارگذاری خودکار کتابخانه‌ها و گسترده کردن کتابخانه‌های محیط اجرای برنامه. البته زبان‌های ایستای مدرن مانند C# با زبان میانجی IL یا جاوا و ماشین مجازی‌اش از مدیریت حافظه بهره می‌برند و به نوعی دورگه و هایبرد هستند تا تعادلی بین مدیریت حافظه و مدیریت پردازش را بدون تایپ‌کستیگ ایجاد کنند.
تفاوت سرعت پردازش زبان‌های ایستا و پویا در برنامه‌هایی که نیاز به پردازش‌های بسیار حجیم در مدت زمان بسیار کوتاه دارند خودش را نشان می‌دهد بنابراین تفاوت سرعت پردازش در مورد حتی وبگاه‌های بسیار شلوغ آنقدر نیست که امکانات فراهم شده توسط یک زبان سطح بالا مانند php یا python را فدای سرعت C++ کنند البته برای مثال فیسبوک مجبور شد که php را بواسطه c++ بر روی facebook hiphop گسترش دهد تا به سرعت پردازش مطلوب خودش برسد. برای مثال دربارهٔ python توصیه شده‌است که حلقه‌های چرخشی روی حجم بالای داده را بواسطه لوپ خود پایتون و با اندیس index انجام ندهید بلکه یک آرایه array تهیه کرده و چرخش را به زیرساخت C++ آن واگذار کنید.
اما مسئله مدیریت حافظه و نشت آن یا memory leakage از آنجاست که در زمان اجرای برنامه (runtime) ابتدا فضایی از حافظه به نام کداسپیس به کد اختصاس داده می‌شود و به بقیه حافظه در دسترس heap گفته می‌شود. مسئولیت استفاده و تضمین خالی کردن بخش اشغال‌شده از heap پس از پایان کار به عهده خود برنامه‌نویس است در غیر اینصورت برنامه ممکن است دچار نشت حافظه شود. در زمان ران‌تایم جدولی از کداسپیس درون ساختار زبان برای تعیین حافظه مورد استفاده هر بخش از کد (مثلاً یک تابع یا فانکشن) اختصاص داده می‌شود که به هر بخش از آن اصطلاحاً Scope گفته می‌شود که تعیین می‌کند که کدام متغیرها توسط کدام بخش از کد استفاده می‌شوند تا در آغاز یک scope آن متغیرها درون محیط مثلاً یک تابع ایجاد شوند و پس از اجرا کاملاً پاک شوند (کسانی که در محیط ساده بورن شل کار کرده‌اند با این موضوع به خوبی آشنا می‌شوند). اما در زمان استفاده از ریفرنس خارجی در قالب pointer (اختصاصی زبان C++) آن متغیری که به ظاهر در محیط اسکوپ استفاده می‌شود در واقع فقط یک میان‌بر ریفرنس است به پوینتری که فضایی را روی حافظه تعیین کرده‌است که خارج از محدوده اسکوپ مثلاً تابع فعلی ما قرار دارد در نتیجه با پایان کار فانکشن ما، فضای اختصاصی آن پوینتر تخلیه نمی‌شود بلکه فقط متغیر ریفرنسی که به آن اشاره می‌کرده پاک می‌شود و نشت حافظه رخ می‌دهد (به آن pointer که هیچ متغیر ریفرنسی نداشته باشد پوینتر وحشی یا wild گفته می‌شود) و این پوینتر می‌تواند یک شی بانک اطلاعاتی دارای حجم انبوهی از داده‌های استخراج شده باشد یا یک شی ارتباطی بانک اطلاعاتی که از دسترس خارج شده‌اند اما فضای حافظه را تا زمان بازبودن برنامه اشغال می‌کنند. ریفرنس‌های پوینتر حتی در زبان‌های سطح بالا مانند php و python یا C# هنوز در لایه زیرین ناپیدا استفاده می‌شوند برای انتقال متغیرها در سراسر برنامه اما زبان سطح بالا تعداد ریفرنس‌های موجود به هر پوینتر را حفظ می‌کند و زمانی که این تعداد صفر بشود زمان مرگ پوینتر و تخلیه آن فرا رسیده یا آن را به سطل زباله گاربیج‌کالکشن می‌فرستد تا همه آن‌ها را با هم و در یک نوبت تخلیه کند و از بار پردازشی اضافه ناشی از تخلیه مکرر بکاهد اگرچه تعداد نوبت‌های تخلیه گاربیج‌کالکشن در تنظیمات برنامه یا سرور ماشین مجازی قابل تنظیم است.

عمل

طراحان زبان و کاربران باید مصنوعاتی ایجاد کنند تا برنامه‌نویسی را در عمل ممکن سازند و کنترل کنند. مهم‌ترین این مصنوعات خصوصیات و پیاده‌سازی‌های زبان هستند.

پیاده‌سازی

پیاده‌سازی یک زبان برنامه‌نویسی امکان اجرای آن برنامه را روی پیکربندی مشخصی از سخت‌افزار و نرم‌افزار را فراهم می‌کند. به‌طور وسیع، دو راه رسیدن به پیاده‌سازی زبان برنامه‌نویسی وجود دارد. کامپایل کردن و تفسیر کردن. به‌طور کلی با هر بک از ابن دو روش می‌توان یک زبان را پیاده‌سازی کرد.
خروجی یک کامپایلر ممکن است با سخت‌افزار یا برنامه‌ای به نام مفسر اجرا شود. در برخی پیاده‌سازی‌ها که از مفسر استفاده می‌شود، مرز مشخصی بین کامپایل و تفسیر وجود ندارد. برای مثال، برخی پیاده‌سازی‌های زبان برنامه‌نویسی بیسیک کامپایل می‌کنند و سپس کد را خط به خط اجرا می‌کنند.
برنامه‌هایی که مستقیماً روی سخت‌افزار اجرا می‌شوند چندین برابر سریعتر از برنامه‌هایی که با کمک نرم‌افزار اجرا می‌شوند، انجام می‌شوند.
یک تکنیک برای بهبود عملکرد برنامه‌های تفسیر شده کامپایل در لحظه آن است. در این روش ماشین مجازی، دقیقاً قبل از اجرا، بلوک‌های کدهای بایتی که قرار است استفاده شوند را برای اجرای مستقیم روی سخت‌افزار ترجمه می‌کند.

پیشرفت‌های اولیه

اولین زبان برنامه‌نویسی به قبل از رایانه‌های مدرن بازمی‌گردد. قرن ۱۹ دستگاه‌های نساجی و متون نوازنده پیانو قابل برنامه‌نویسی داشت که امروزه به عنوان مثال‌هایی از زبان‌های برنامه‌نویسی با حوزه مشخص شناخته می‌شوند. با شروع قرن بیستم، پانچ کارت‌ها داده را کدگذاری کردند و پردازش مکانیکی را هدایت کردند. در دهه ۱۹۳۰ و ۱۹۴۰، صورت گرایی حساب لاندای آلونزو چرچ و ماشین تورینگ آلن تورینگ مفاهیم ریاضی بیان الگوریتم‌ها را فراهم کردند؛ حساب لاندا همچنان در طراحی زبان مؤثر است.
در دهه ۴۰، اولین رایانه‌های دیجیتال که توسط برق تغذیه می‌شدند ایجاد شدند. اولین زبان برنامه‌نویسی سطح بالا طراحی شده برای کامپیوتر پلان کلکول بود، که بین سال‌های ۱۹۴۵ و ۱۹۴۳ توسط کنراد زوس برای ز۳ آلمان طراحی شد.
کامپیوترهای اوایل ۱۹۵۰، به‌طور خاص ÜNIVAC ۱ و IBM ۷۰۱ از برنامه‌های زبان ماشین استفاده می‌کردند. برنامه‌نویسی زبان ماشین نسل اول توسط نسل دومی که زبان اسمبلی نامیده می‌شوند جایگزین شد. در سال‌های بعد دهه ۵۰، زبان برنامه‌نویسی اسمبلی، که برای استفاده از دستورهای ماکرو تکامل یافته بود، توسط سه زبان برنامه‌نویسی سطح بالا دیگر: FORTRAN,LISP , COBOL مورد استفاده قرار گرفت. نسخه‌های به روز شده این برنامه‌ها همچنان مورد استفاده قرار می‌گیرند، و هر کدام قویا توسعه زبان‌های بعد را تحت تأثیر قرار دادند. در پایان دهه ۵۰ زبان algol ۶۰ معرفی شد، و بسیاری از زبان‌های برنامه‌نویسی بعد، با ملاحظه بسیار، از نسل algol هستند. قالب و استفاده از زبان‌های برنامه‌نویسی به شدت متأثر از محدودیت‌های رابط بودند.

یکپارچگی و رشد

دهه ۸۰ سال‌های یکپارچگی نسبی بود. C++ برنامه‌نویسی شیء گرا و برنامه‌نویسی سیستمی را ترکیب کرده بود. ایالات متحده ایدا (زبان برنامه‌نویسی سیستمی که بیشتر برای استفاده توسط پیمان کاران دفاعی بود) را استانداردسازی کرد. در ژاپن و جاهای دیگر، هزینه‌های گزافی صرف تحقیق در مورد زبان نسل پنجم می‌شد که دارای ساختارهای برنامه‌نویسی منطقی بود. انجمن زبان کاربردی به سمت استانداردسازی ML و Lisp حرکت کرد. به جای ایجاد مثال‌های جدید، تمام این تلاش‌ها ایده‌هایی که در دهه‌های قبل حلق شده بودند را بهتر کرد.
یک گرایش مهم در طراحی زبان در دهه ۸۰ تمرکز بیشتر روی برنامه‌نویسی برای سیستم‌های بزرگ از طریق مدول‌ها، یا واحدهای کدهای سازمانی بزرگ مقیاس بود. مدول-۲، ایدا؛ و ML همگی سیستم‌های مدولی برجسته‌ای را در دهه ۸۰ توسعه دادند. با وجود اینکه زبان‌های دیگر، مثل PL/i، پشتیبانی بسیار خوبی برای برنامه‌نویسی مدولی داشتند. سیستم‌های مدولی غالباً با ساختارهای برنامه‌نویسی عام همراه شده‌اند.
رشد سریع اینترنت در میانه دهه ۹۰ فرصت‌های ایجاد زبان‌های جدید را فراهم کرد. Perl، در اصل یک ابزار نوشتن یونیکس بود که اولین بار در سال ۱۹۸۷ منتشر شد، در وبگاه‌های دینامیک متداول شد. جاوا برای برنامه‌نویسی جنب سروری مورد استفاده قرار گرفت. این توسعه‌ها اساساً نو نبودند، بلکه بیشتر بهینه‌سازی شده زبان و مثال‌های موجود بودند، و بیشتر بر اساس خانواده زبان برنامه‌نویسی C بودند.
پیشرفت زبان برنامه‌نویسی همچنان ادامه پیدا می‌کند، هم در تحقیقات و هم در صنعت. جهت‌های فعلی شامل امنیت و وارسی قابلیت اعتماد است، گونه‌های جدید مدولی(mixin، نماینده‌ها، جنبه‌ها) و تجمع پایگاه داده.
۴GLها نمونه‌ای از زبان‌هایی هستند که محدوده استفاده آن‌ها مشخص است، مثل SQL. که به جای اینکه داده‌های اسکالر را برگردانند، مجموعه‌هایی را تغییر داده و برمی‌گردانند که برای اکثر زبان‌ها متعارفند. Perl برای مثال، با «مدرک اینجا» خود می‌تواند چندین برنامه ۴GL را نگه دارد، مانند چند برنامه جاوا اسکریپت، در قسمتی از کد پرل خود و برای پشتیبانی از چندین زبان برنامه‌نویسی با تناسب متغیر در «مدرک اینجا» استفاده کند.