سلف (زبان برنامه‌نویسی)

سِلف   یک زبان برنامه‌نویسی شی گرا مبتنی بر مفهوم پروتوتایپ (نمونه اولیه) است. این زبان عمدتاً به عنوان یک سیستم آزمون تجربی برای طراحی زبان در سال‌های ۱۹۹۰–۱۹۸۰مورد استفاده قرار گرفت. در سال۲۰۰۶سلف هنوز به عنوان بخشی از پروژهٔ klein، که یک ماشین مجازی سلف که کاملاً به زبان سلف نوشته شده بود، در حال توسعه بود. آخرین ورژن آن self۴٫۴می‌باشد که درجولای ۲۰۱۰ منتشر گردید.
چندین تکنیک just-in-time compilation در سلف پیشگام بودند و در پروژهٔ سلف بهبود بخشیده شدند و به‌طور هم‌زمان نیاز آن‌ها به یک زبان شی گرا سطح بالا که حداقل نصف سرعت بهینه c را داشته باشدعملی شد. این تکنیک‌ها بعدها برای ماشین مجازی Java’s HotSpot گسترش داده شدند.

تاریخچه 
سلف عمدتاً توسط David UngarوRandall Smith در سال ۱۹۸۶ در حالی که روی پروژهٔ Xerox Parcکار می‌کردند، طراحی شد. هدف آن‌ها این بود که حالتی از هنر را در پروژهٔ برنامه‌نویسی وارد کنند، یکبار smalltalk۸۰توسط آزمایشگاه‌ها عرضه شد و به‌طور جدی در صنعت به کار گرفته شد. آن‌ها به دانشگاه استنفورد رفتند و کار روی زبان را ادامه داد، ساختن اولین کامپایلر سلف در سال ۱۹۸۷ صورت گرفت. در آن موقع تلاش برای رشد دادن یک سیستم کامل برای سلف بر خلاف صرفاً یک زبان بودن متمرکز شد.
اولین نسخه عمومی در سال ۱۹۹۰ عرضه شد، و سال بعد تیم محقق به کمپانی SunMicrosystems منتقل شدند و کار روی زبان را ادامه دادند. تا سال ۱۹۹۵ چندین نسخهٔ جدید تا نسخه۴ به بازار آمد.
نسخهٔ ۴٫۳ آن در سال ۲۰۰۶ عرضه شد و بر روی سیستم‌های Mac OS XوSolaris اجرا شد. نسخهٔ جدید آن یعنی self۴٫۴ برای سیستم‌های Mac OS XوLinux توسط یک گروهی از اعضای سابق و برنامه نویسان مستقل توسعه داده شد.

زبان سلف همچنین الهام بخش زبان‌های بعدی بر مبنای مفاهیمش شد. از قابل توجه‌ترین آن‌ها شاید زبان‌های NewtonScript برای Apple Newton و JavaScript است، که در درجهٔ اول برای صفحات وب پویا در تمامی مرورگرهای امروزی استفاده می‌شود. از زبان‌های دیگر می‌توان Io,Cel,Lisaac وAgora را نام برد.IBM Tivoli Framework که سیستم شی گرا را تعمیم داد، در سطوح پایین‌تر، سیستم شی گرا مبتنی بر prototype سلف است.

زبان‌های برنامه‌نویسی مبتنی برنمونه اولیه(prototype) 
 زبان‌های برنامه‌نویسی شی گرا مبتنی بر کلاس سنتی، بر مبنای یک دوگانگی دیرینه هستند:
 کلاس‌ها خصوصیات پایه و رفتار اشیا را تعریف می‌کنند.
 نمونه‌های اشیا جلوه‌های خاصی از یک کلاس می‌باشند.
برای مثال، اشیایی از کلاس vehicle را که یک اسم و قابلیت انجام اعمال زیادی را دارد را در نظر بگیرید، مانند drive to work و deliver construction materials.Bob’s Car یک شی خاص از کلاس vehicle است. در نظریه یک فرد می‌تواند یک پیام به Bob’s Car بفرستد، و به آن بگوید: deliver construction materials.
این مثال یکی از مشکلات این رویکرد نشان می‌دهد. Bob’s Car ممکن است یک ماشین اسپورت باشد، قادر به حمل وتحویل وسایل ساختمان نمی‌باشد. (از لحاظ مفهومی)، اما این یک قابلیت است که vehiclesاین مدل را داشته باشند. یک مدل سودمندتر استفاده از زیر کلاس‌ها برای ساختن ویژگی‌هایی از vehicle است؛ برای مثال ماشین مسابقه و تراکتور. فقط اشیا کلاس تراکتور نیازمند یک سازوکار برای تحویل مصالح ساختمانی اند؛ ماشین‌های مسابقه که برای آن کار نامناسبند، فقط باید سریع بروند. با این حال این مدل نیازمند بینش عمیق‌تری است، که فقط ممکن است به عنوان مسائل به وجود بیایند.

این مسئله یکی از عوامل ایجاد انگیزه در پس نمونه‌های اولیه‌است. مگر در مواردی که می‌توان با اطمینان پیش‌بینی کرد چه ویژگی هایی؛ مجموعه‌ای از اشیا و کلاس‌ها در آینده‌ای دور خواهند داشت، یک فرد نمی‌تواند یک کلاس را به صورت سلسله مراتبی درست طراحی کند. اغلب اوقات برنامه در نهایت رفتارهای اضافی نیاز دارد، و بخش‌های سیستم نیاز به طراحی دوباره دارند (یا refactored) تا اشیاء به شیوه‌ای متفاوت بشکنند. تجربه با زبان‌های شی گرا اولیه مثل smalltalk نشان داد که این نوع مشکل دوباره و دوباره پیش آمد. سیتم‌ها تمایل دارند که به یک نقطه برسند و سپس سفت و سخت شوند همانطریکه در کلاس‌های اولیه هنگامی کد برنامه‌نویس بزرگ می‌شود، به سادگی موجب اشتباه می‌شود. بدون یافتن راه‌هایی برای تغییر آسان کلاس، مشکلات جدی به وجود می‌آید.
زبان‌های دینامیک از قبیل smalltalk برای این نوع از تغییر از طریق متدهای شناخته شده در کلاس‌ها استفاده کردند؛ با تغییر کلاس، اشیاء بر اساس آن رفتار خود را تغییر می‌دهند. با این حال، چنین تغییراتی باید با دقت بسیار انجام شود، زیرا به عنوان اشیاء دیگر مبتنی بر همان کلاس ممکن است یک رفتار غلط باشد: «اشتباه» اغلب وابسته به چهار چوب است. (این یک فرم از مشکل کلاس پایه شکننده‌است). علاوه بر این، در زبان‌های مثل c++ که در آن کلاس‌های مشتق شده را می‌توان به‌طور جداگانه از کلاس‌های ما فوق کامپایل کرد، یک تغییر در کلاس ما فوق می‌تواند متدهای از پیش کامپایل شده کلاس مشتق شده را بشکند. (این شکل دیگری از مشکل کلاس پایه شکننده‌است، و همچنین یک فرم از مشکل واسط دوتایی شکننده). در self و دیگر زبان‌های مبتنی بر نمونه اولیه، دوگانگی بین کلاس‌ها و instanceها از بین رفته‌است.
به جای داشتن «نمونه» از یک شی که مشتق شده از تعدادی کلاس است، درself یک کپی از شی موجود می‌گیریم و تغییرات را روی آن اعمال می‌کنیم؛ بنابراین Bob’s car با ساخت یک کپی از شی "Vehicle" ساخته می‌شود، و سپس متد drive very fast به آن اضافه می‌شود. اشیا اولیه‌ای برای ساختن کپی استفاده شده‌اند به عنوان نمونه اولیه شناخته شده‌اند. این روش ادعا کرده‌است ساده‌ترین شیوهٔ پویایی برنامه‌است. اگر یک شی موجود (یا مجموعه‌ای از اشیاء) یک مدل ناکافی باشد، برنامه‌نویس به سادگی می‌تواند یک شی اصلاح شده با رفتار صحیح ایجاد کند و بدون آنکه کد آن شی تغییر کرده باشد از آن استفاده کند.

تعریف زبان 
اشیا سلف یک مجموعه‌ای از اسلات‌ها هستند. اسلات‌ها متدهای دسترسی که مقادیری را برمی‌گردانند و با قراردادن یک دو نقطه بعد از نام اسلات می‌توان به آن مقدار داد. برای مثال برای یک اسلات به نام “name” داریم:
 myPerson name

 my Person name:'foo'
سلف مانند زبان smalltalkاز بلاک‌ها برای کنترل‌های رایج و وظایف دیگر استفاده می‌کند. متدها اشیایی هستند که علاوه بر اسلات‌ها، کدها را نیز شامل می‌شوند (از آن‌ها برای آرگمان‌ها و مقادیر موقتی استفاده می‌کنند) و می‌توانند در یک اسلات سلف مانند هر شی دیگری قرار بگیرد، برای مثال یک عدد. نحو آن در هر حالت ثابث باقی می‌ماند.
باید توجه شود که در سلف هیچ تفاوتی بین فیلدها و متدها وجود ندارد، هر چیزی یک اسلات است. از آنجا که دسترسی به اسلات‌ها از طریق پیغام‌ها بخش اعظمی از نحو را در سلف شکل می‌دهد، بنابراین پیغام‌های زیادی به سلف فرستاده شده و سلف می‌تواند قطع شود.

نحوهای ابتدایی 
نحوی که برای دسترسی به اسلات‌ها استفاده می‌شود، شبیه همان چیزی است که درsmalltalk داریم. سه نوع پیغام وجود دارد:
unary receiver slot_name
binary receiver + argument
keyword receiver keyword: arg1 With: arg2

همهٔ پیغام‌ها نتایجی را برمی‌گردانند، بنابراین دریافت‌کننده (اگر وجود داشته باشد) و آرگمان‌ها خودشان می‌توانند نتیجهٔ پیغام‌های دیگر باشند و سلف مقدار بازگردانده را دور می‌ریزد. برای مثال:

'Hello, World!' print.

این یک برنامه «hello world» در سلف است. نحو آن یک شی رشته‌ای لفظی را نشان می‌دهد. دیگر لفظ‌ها شامل اعداد، بلاک‌ها و شی‌های اصلی می‌شود.
گروه‌بندی می‌تواند با استفاده از پرانتزها اعمال شود. در غیاب گروه‌بندی صریح، پیغام‌های یگانی بیشترین اولویت را نسبت به دوگانی دارند (گروه‌بندی از چپ به راست) و پیغام‌های keyword کمترین اولویت را دارند. استفاده ازkeywordها برای تکالیف در جاییکه عبارات نیز پیغام‌های keyword را دارند می‌تواند منجر به پرانتزهای اضافی شود؛ بنابراین برای جلوگیری از آن سلف باید اولین قسمت انتخاب‌کننده یک پیغام keyword را با حرف کوچک شروع کند و قسمت‌های بعدی آن را با حرف بزرگ شروع کند:
valid: base bottom between: ligature bottom + height and: base top / scale factor.
 می‌تواند به صورت واضح تجزیه شده باشد و معنی مشابه زیر را می‌دهد:
valid: ((base bottom) between: ((ligature bottom) + height) and: ((base top) / (scale factor))).
در smalltalk۸۰ عبارت شبیه بالا به صورت زیر نوشته می‌شود:
 valid:= self base bottom between: self ligature bottom + self height and: self base top / self scale factor.
باید در نظر داشت base, ligature, height, scale در حقیقت متدهای سلف هستند و نه یک متغیر نمونه.

ساختن اشیا جدید 
یک مثال پیچیده‌تر را در نظر بگیرید:
  labelWidget copy label: 'Hello, World!'.
کد بالا یک کپی از شی "labelWedget" را با پیغام کپی می‌سازد (بدون میانبر)، سپس یک پیغام به آن می‌فرستد تا «hello,world» را در یک اسلات به نام label قرار دهد.
(desktop activeWindow) draw: (labelWidget copy label: 'Hello, World!').
در این حالت(desktop activeWindow) اول اجرا می‌شود و پنجرهٔ فعال را از بین لیست پنجره‌هایی که شی desktop می‌شناسد برمی‌گرداند. سپس (از داخل به بیرون و از چپ به راست) کدی که در قسمت قبل اجرا کردیم، labelWidget را برمی‌گرداند. سرانجام widget به اسلات رسم از پنجره فعال فرستاده می‌شود.

وراثت/نمایندگی 
هر شی سلف یک واحد مستقل است. سلف کلاس و متا کلاس ندارد. تغییرات روی یک شی روی اشیا دیگر تأثیر نمی‌گذارد، با اینکه در بعضی از مواقع این کار مطلوب تر است. به‌طور معمول یک شی تنها پیغام مربوط به اسلات محلی خود را متوجه می‌شود، ولی با داشتن یک یا چند اسلات که اشیا پدر را نشان می‌دهند، یک شی می‌تواند هر پیامی را که خودش متوجه نمی‌شود به شی پدر محول کند. هر اسلات می‌تواند یک اشاره گر به پدر با اضافه کردن یک ستاره به صورت پسوند بسازد. ویژگی نمایندگی نیز برای اجرا کردن ویژگی‌هایی چون فضای نام و حوزهٔ لغوی به کار می‌رود.
برای مثال فرض کنید یک شی به نام “bank account” داریم که شاید متدهایی چون deposit وwithdraw دارد. این یک نمونه اولیه که فقط مختص شیوه‌ای است که از آن استفاده می‌شود.

ویژگی‌ها 
با ساختن یک کلون از این شی برای Bob’s account یک شی جدید با همان متدها و داده‌ها ایجاد کرده‌ایم. یک راه حل این است که ابتدا یک شی ساده به نام trait object که آیتم‌هایی را شامل می‌شود که یک فرد به‌طور معمول در یک کلاس با آن مرتبط است.
در این مثال شی bank account متدهای ذکر شده در بالا را ندارد ولی یک شی پدر دارد که کار آن‌ها را انجام می‌دهد، در این روش کپی هی زیادی از شی bank account می‌توانیم داشته باشیم که رفتار آن‌ها را با تغییر در شی ریشه می‌توانیم تغییر دهیم.
  myObject parent: someOtherObject.
این نمونه کلاس myObject را در زمان اجرا تغییر می‌دهد. برخلاف وراثت و حوزهٔ لغوی، نمایندگی می‌تواند در زمان اجرا تغییر کند.

اضافه کردن اسلات‌ها 
اشیا در سلف می‌توانند به گونه‌ای تغییر یابند که چند اسلات اضافی را شامل شوند. این کار را می‌توان با محیط برنامه‌نویسی گرافیکی یا _AddSlots انجام داد. در زیر چند نمونه را مشاهده می‌کنیم:
_AddSlots: (| vehicle <- (|parent* = traits clonable|) |).

vehicle _AddSlots: (| name <- 'automobile'|).

 _AddSlots: (| sportsCar <- vehicle copy |).
  sportsCar _AddSlots: (| driveToWork = (some code, this is a method) |).

_AddSlots: (| porsche۹۱۱ <- sportsCar copy |).
  porsche911 name:'Bobs Porsche'.

محیط سلف 
یکی از ویژگی هی سلف این است که مبتنی بر سیستم virtual machine است که دراسمال تاک نیز استفاده می‌شد. به موجب این ویژگی برنامه‌ها بر خلاف زبان‌هایی چون C واحدهای مستقلی نیستند ولی از تمام محیط حافظه برای فعال‌سازی استفاده می‌کنند. به خاطر این ویژگی محیط سلف می‌تواند ابزارهای دیباگ کردن قدرتمندی را فراهم آورد. برای مثال یک فرد می‌تواند در حین دیباگ ایست دهد و مقادیری را تغییر دهد و دوباره به دیباگ ادامه دهد. این شیوه‌های on the flyموجب افزایش بهره‌وری برنامه می‌شود.
از دیگر ویژگی‌های محیط سلف می‌توان قابلیت تغییر سریع و مداوم اشیا اشاره کرد. همچنین برخلاف سیستم‌های قدیمی، در سلف فقط قسمتی از کد که تغییر کرده را بازسازی(rebuild) می‌کند.

کارایی 
VM(virtual machine) سلف سرعتی تقریباً برابر نصف C را دارد که به خاطر وجود تکنیک‌های just-in-time compilation حاصل شده‌است.

جمع‌آوری زباله 
Garbage collector در سلف از ویژگی generational garbage collection که اشیا را بر اساس سن جداسازی می‌کند. این تکنیک موجب افزایش کارایی می‌شود اگر چه موجب گرفتن وقت زیادی از سیستم می‌شود.

زبان سل 

سل  یک زبان شی گرا بر اساس نمونهٔ اولیه که بر پایه زبان سلف که خود آن تحت تأثیر اسمال‌تاک است. هدف از طراحی این زبان طراحی نسخه‌ای از زبان برنامه‌نویسی سلف بود که بودن وابستگی زیاد به Self GUI اجرا شود. برای مثال، ما بدون سر بار زیاد سیستم Self می‌توانیم برنامه‌ای تحت خط فرمان ایجاد کنیم.

دستور زبان آن بسیار شبیه زبان برنامه‌نویسی سلف است. اما، بعضی از الگوهای اولیه آن به صورت سمبل‌های هنری ASCII طراحی شده‌اند. لینکهای زیر مثال‌های ارائه شده‌است. همین‌طور، این زبان مبتنی بر تصویر نیست. این زبان فقط از منابع نوشتاری معمولی برای ایجاد برنامه استفاده می‌کرد. از سال ۲۰۰۲ به بعد دیگر از این زبان حمایت به عمل نیامد و توسعهٔ آن متوقف شد اما کدهای این زبان در دسترس و کارا هستند. این زبان بر روی سیستم عامل‌های ویندوز، فری بی اس دی و لینوکس کار می‌کند. این زبان توسط دورا نلسون در سال ۱۹۹۸ شروع به کار کرد.

منابع 

 ویکی‌پدیای انگلیسی. 

اشکال‌های برنامه‌نویسی
برنامه‌نویسی
زبان‌های برنامه‌نویسی بر پایه پیش‌نمونه
زبان‌های برنامه‌نویسی ساخته‌شده در ۱۹۸۷ (میلادی)
زبان‌های برنامه‌نویسی شیءگرا
زبان‌های برنامه‌نویسی ماشینی پویا
کامپایلرها و مفسرهای آزاد
نرم‌افزارهای آزاد چندسکویی
نرم‌افزارهای ۱۹۸۷ (میلادی)