سینتکس قندی

یک نمونه Syntactic sugar در تعریف متغیر در زبان go. در Syntactic sugar کامپایلر زبان گو، تایپ متغیر را استنتاج می‌کند
یک نمونه سینتکس قندی در تعریف متغیر در زبان گو. وقتی از این سینتکس قندی استفاده می‌شود، کامپایلر زبان گو، تایپ متغیر را استنتاج می‌کند.[۱]

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

مثلاً در خیلی از زبان‌های برنامه‌نویسی، روش‌های متفاوتی برای بررسی و ایجاد و تغییر عناصر یک آرایه، ارائه می‌شود که اگر بخواهیم کلی تر نگاه کنیم، واکشی مقدار یک عنصر می‌تواند یک تابع با دو آرگومان ورودی باشد: یکی خود آرایه و دیگری اندیس عنصر مدنظر در همان آرایه که می‌تواند به صورت get_array(Array, vector(i,j)) بیان شود. ولی بسیاری از زبان‌ها در کنار این، نحو دیگری مانند Array[i,j] هم ارائه می‌کنند که نمونه ای از سینتکس قندی است.

در عمل هم می‌توان سینتکس قندی را کاملاً از زبان حذف کرد و این کار هیچ تأثیری در عملکرد و قدرت زبان نمی‌گذارد، فقط حذف شدن سینتکس‌های قندی باعث طولانی‌تر شدن و ناخواناتر شدن کدها می‌شود، ولی توانایی‌ها، همان است.

بیشتر کامپایلرها و مفسرها، ساختار سینتکس‌های قندی را قبل از پردازش، به معادل‌های اصلی زبان تبدیل می‌کنند. به این فرایند گاهی اوقات به آن «قند زدایی» یا «desugaring» هم می‌گویند.

نمونه ها

به عنوان مثال در خیلی از زبان‌های برنامه‌نویسی به این صورت داریم:[۲]

int i = 2
i = i + 3

// Sugar
i += 3
i -= 3

//Sugar
i++ // i += 1    --===> i = i + 1
i-- // i -= 1    --===> i = i - 1

نمونه بالا بسیار رایج است و می‌شود آن را در خیلی از زبان های برنامه نویسی دید[۳]

در زبان سی شارپ، استنتاج نوع (Type Inference) از طریق کلمه کلیدی var هم نوعی سینتکس قندی است :

int y = 4 // Base Syntax
var x = 8 // Syntactic Sugar

همین فرایند در ++C با استفاده از کلمه کلیدی auto یک نمونه سینتکس قندی است :

int x = 16 // Base syntax
auto y = 8 // Syntactic Sugar

به عنوان مثال در جاوا اسکریپت تعرف یک شئ از طریق class که در ES6 اضافه شده است، به شکل زیر یک سینتکس قندی است:

class ClassA {
  constructor(foo, bar) {
    this.Foo = foo
    this.Bar = bar
  }
}

 const bux = new ClassA("Hello", "World")
 
// bux.Foo is "Hello"
// bux.Bar is "World"

نمونه پایین حالت واقعی همین مثال بالاست که از ES5 به قبل استفاده می‌شد[۴] :

const ClassA = function (foo, bar) {
this.Foo = Foo;
this.Bar = bar;
}
const bux = new ClassA("Hello", "World")
 
// bux.Foo is "Hello"
// bux.Bar is "World"

جاوا اسکریپت در واقع ارثبری را در میان اشیاء از طریق پروتوتایپ‌ها انجام می‌دهند . constructor تعریف شده در هر دو مثال، پروتوتایپ شئ تولید شده خواهد بود.[۴]

در پایتون، دکوراتورها یک سینتکس قندی هستند. در واقع حالت زیر حالت واقعی سینتکس زبان است:[۳]

# pre-decorators
def Y():
    pass
Y = X(Y)

که سینتکس قندی آن به این صورت است :

@X
def Y():
    pass

دستور زیر در زبان گو، نوعی سینتکس قندی است[۵] :

var m map[string]int = map[string]int{} //Base syntax
m := map[string]int{} //Syntactic sugar

یا مثال دیگر آن کلمه کلیدی any است [۶][۷][۸]:

func main() {
	var a interface{} // any
	var b any         // interface{}

	a = "Hello, world"
	b = a

	fmt.Println(a == b) // true
}

در این مثال میبینید که any همان {}interface است (در Playground ببینید) و کامپایلر مشکلی ندارد نوع any با {}interface مقایسه شود یا مقدار دیگری در این یکی قرار گیرد. در ابتدا هم {}interface در سینتکس وجود داشت اما بعد کلمه کلیدی any را به عنوان یک alias برای {}interface به زبان گو اضافه کردند. مفهوم {}interface از پیچیده ترین مفاهیم سیستم تایپ دهی در گولنگ است. علت اینکه {}interface هر نوع داده ای را در خود جا می دهد، روش پیاده سازی آن است. هر اینترفیس در زبان گو از دو نشان گر به دو مجموعه تشکیل شده است. یکی از این نشان گر ها، اشاره به محتوای بین دو { } دارد. نشانگر دیگر اشاره به محتوایی که بعدا در interface قرار میگیرد. تایپ سیستم باید بین آن چیز هایی که در زمان اجرا در interface قرار میگیرند، محتوای داخل { } را پیدا کند. اگر پیدا کرد، اجازه میدهد که داده ها در آن متغیر ذخیره شوند. وگرنه برنامه را میبندد. اگر محتوایی بین { و } قرارندهید، هر داده ای در متغیر ذخیره می شود و اینگونه تایپ any به تایپ سیستم go راه می یابد.[۸][۹][۱۰]

بطور ویژه هرگونه استنتاج نوع در زبان های برنامه نویسی مثل سی شارپ، سی پلاس پلاس، تایپ اسکریپت، پایتون و... یک سینتکس قندی است.

همان‌طور که گفته شد، سینتکس قندی را می‌توان حذف کرد و هیچ تأثیری در کارایی زبان ندارد فقط کد را خواناتر و تمیزتر می‌کند.

اصطلاحات مربوط

سینتکس شور

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

مثلاً در سی شارپ برای جلوگیری از مشکلاتی که نحوه پیاده سازی دستور switch وجود دارد، برنامه‌نویس ملزم است که از عبارت break (یا عبارت‌های goto ،return یا throw) در انتهای هر case استفاده کند. در زبان های سطح پایین تری مثل C یا C++ فلسفه وجودیا ین دستور این است که اگر داده A باید با داده B و C و... مقایسه شود، بجای اینکه هر بار خود A را در کنار B و C و... در رجیستر پردازنده قراردهیم، یک بار A را در رجیستر بگذاریم و فقط B و C و... را در یه رجیستر قرار دهیم و مقایسه کنیم. باگ پیاده سازی این ماجرا این است که وقتی در یکی از case ها (فرضا این case در خط n قرار دارد) به برابری میرسیدیم، دستور کامپایر این بود که دیگر شرطی بررسی نشود، A از رجیستر خارج و کد ها از همان خط n به بعد اجرا شوند. وقتی محدوده case ما به پایان می رسید، کامپایلر متوفق نمیشد چون شرطی بررسی نمیشد و قرار بود کد ها از خط n به بعد اجرا شوند و به همین ترتیب ممکن بود کد های case های دیگر نیز اجرا شوند. به همین علت برنامه نویس ها مجبور بودند در انتهای هر case یک دستور break یا goto یا return و... که کار بلوک را تمام میکنند را قرار میدادند تا بقیه کد ها ناخواسته اجرا نشوند. اما در زبان سطح بالاتری مانند سی شارپ که اصلا با پردازنده ارتباط چنین مستقیمی ندارد می شد کامپایلر را به گونه ای طراحی کرد که دستور break در انتهای هر case لازم نباشد. ایراد اینجاست که سی شارپ به عنوان یه ویژگی این را ارائه نکرده است. اگر شما break در انتهای هر case قرار ندهید، کامپایلر به شما ارور سینتکس میدهد که یعنی گذاشتن آن break آنجا اجباریست. این یک سینتکس شور است. 4 کاراکتر اضافه به کد های شما افزوده می شود در حالی که لازم نبوده اند و کد های شما شلوغ تر می شوند.[۱۱] [۱۲]

منابع

  1. "Everything You Always Wanted to Know About Type Inference - And a Little Bit More - The Go Programming Language". go.dev (به انگلیسی). Retrieved 2024-09-04.
  2. «Python is (mostly) made of syntactic sugar [LWN.net]». lwn.net. دریافت‌شده در ۲۰۲۴-۰۹-۰۳.
  3. 1 2 «Python is (mostly) made of syntactic sugar [LWN.net]». lwn.net. دریافت‌شده در ۲۰۲۴-۰۹-۰۳.
  4. 1 2 «Object.prototype.constructor - JavaScript | MDN». developer.mozilla.org (به انگلیسی). ۲۰۲۴-۰۷-۲۵. دریافت‌شده در ۲۰۲۴-۰۹-۰۳.
  5. "Everything You Always Wanted to Know About Type Inference - And a Little Bit More - The Go Programming Language". go.dev (به انگلیسی). Retrieved 2024-09-03.
  6. "Difference between any/interface{} as constraint vs. type of argument?". Stack Overflow (به انگلیسی). Retrieved 2025-07-25.
  7. «A Tour of Go». go.dev. دریافت‌شده در ۲۰۲۵-۰۷-۲۵.
  8. 1 2 "Effective Go - The Go Programming Language". go.dev (به انگلیسی). Retrieved 2025-07-25.
  9. "Is an Interface a Pointer?". Stack Overflow (به انگلیسی). Retrieved 2025-07-25.
  10. good_effective_flow (۲۰۲۲-۱۱-۰۳). «interfaces are pointers to pointers?». r/golang. دریافت‌شده در ۲۰۲۵-۰۷-۲۵.
  11. «Why Breaks are used in C++ Switch Statement?». GeeksforGeeks (به انگلیسی). ۲۰۲۴-۰۵-۲۰. دریافت‌شده در ۲۰۲۵-۰۷-۲۵.
  12. "Why do we need break after case statements?". Stack Overflow (به انگلیسی). Retrieved 2025-07-25.