rahnema1
چهارشنبه 29 اردیبهشت 1395, 17:57 عصر
الکس استپانوف Alex Stepanov دانشمند روسی الاصل مقیم آمریکا خالق کتابخانه STL و در عرصه ++C و برنامه نویسی یکی از چهره های مهم به حساب می آید.متن زیر مصاحبه ای است که در سال 1995 توسط مجله drdobbs با وی ترتیب داده شده اگرچه تاریخ مصاحبه قدیمیه و حتی این مجله در سال 2014 تعطیل شده و خود استپانوف هم ژانویه امسال 2016 میلادی از شرکت A9 بازنشسته شده اما هنوز این مصاحبه و مطالب اون تازگی داره هم از جهت نوستالژیک و هم آشنایی با دیدگاههای ایشان (برنامه نویسی عام ، STL ، شیء گرایی و اهمیت زبان ++C/C و ...). سعی کردم مصاحبه را به صورت روان ترجمه کنم امیدوارم بتونم مصاحبه ها یا مطالب بیشتری از ایشان یا افراد مهم دیگه اینجا ارائه بدم. هر کسی هم که مایله اصل گفتگو را مطالعه کنه در اینجا (http://www.drdobbs.com/cpp/standards-stepanov-stroustrup-and-steven/228700420) قرار داره
-سوال:در مورد علاقه طولانی مدتتان به برنامه نویسی عام (generic programming) صحبت بفرمایید.
-جواب:در اواخر دهه ۷۰ میلادی شروع به فکر کردن در مورد برنامه نویسی عام کردم در آن زمان این مطلب را دریافته بودم که بعضی الگوریتم ها به پیاده سازی ساختمان داده بستگی ندارند بلکه به تعدادی خصوصیات مفهومی اساسی آن ساختمان بستگی دارند. شروع کردم به بررسی الگوریتمهای مختلف و فهمیدم که بیشتر الگوریتم ها را می توان بدون وابستگی به پیاده سازی به مرحله ای از انتزاع (abstraction) رساند که کارایی الگوریتم پایین نیاید. کارایی، از دغدغه های اساسی من است. این احمقانه است که یک الگوریتم را به صورتی به انتزاع برسانیم که وقتی آن را از حالت انتزاع به نمونه اصلی برگردانیم کارایی اون از بین برود.
در آن زمان من فکر می کردم راه درست برای انجام این نوع مطالعه این است که یه زبان برنامه نویسی را ایجاد کنیم. در نتیجه با دو نفر از دوستانم به نام Deepak Kapur که در حال حاضر پروفسور در دانشگاه ایالتی نیویورک است و David Musser که پروفسور موسسه پلی تکنیک Rensselaer است این کار را شروع کردیم. در آن زمان هر سه در مرکز تحقیقاتی جنرال الکتریک کار می کردیم. ما شروع به کار روی یک زبان به نام Tecton کردیم که این زبان به افراد اجازه می داد که الگوریتم را به وسیله چیزی به نام ساختمانهای عام (generic structure) بیان کند. این ساختمانهای عام در واقع مجموعه ای از «نوع ها» و خصوصیات آنها بود. در واقع یه سری مطالب به زبان ریاضی. ما دریافتیم که می شود یک جبر را تعریف کنیم که موضوع آن عملیات روی این ساختمانها باشد در نتیجه می شود آنها را اصلاح کرد می شود چیزهایی به آن اضافه کرد و کارهای مختلفی را روی آنها انجام داد.
ایده های جالبی داشتیم اما این تحقیق ما به نتایج عملی منجر نشد زیرا Tecton یک زبان functional بود. ما به ایده های Backus معتقد بودیم که باید از سبک برنامه نویسی وان نیومن رها بشویم و نمی خواستیم که در زبان ما اثرات جانبی (side effect) وجود داشته باشه. که این محدودیتی برای ما ایجاد کرد که نتوانیم با الگوریتمهای زیادی که در آنها مفهوم «حالت» (state) و «اثرات جانبی» وجود داشت کار کنیم.
در خصوص Tecton در اواخر دهه ۷۰ به نکته جالبی پی بردم این نکته این بود که مفهومی که از نوع داده انتزاعی (abstract data type)در آن زمان پذیرفته شده بود دارای یک محدودیت اساسی است. ما معمولا به این صورت در نظر می گیریم که نوع داده انتزاعی به ما درمورد رفتار اشیاء می گوید اما در مورد پیاده سازی صحبتی نمی کند. عموما این گونه پذیرفته شده که پیچیدگی یک عملیات (operation)، بخشی از پیاده سازی است و انتزاع، در مورد پیچیدگی صحبتی نمی کند. یکی از مواردی که از نکات مرکزی برنامه نویسی عام است، این است که پیچیدگی یا حداقل مفهومی کلی از پیچیدگی را باید با یک عملیات مربوط کرد.
یک مثال می زنم. شما stack را به عنوان یک نوع داده انتزاعی در نظر بگیرید. خب اینکه در مورد stack دو عملیات push و pop را در داشته باشیم که مثلا با push یک چیزی وارد stack بشود سپس با استفاده از pop همون چیز را بازیابی کنید این به تنهایی کافی نیست بلکه نکته مهم دیگری که باید توجه بشود این است که عملیات push بدون توجه به اندازه stack باید در یک زمان ثابت انجام بشود. اگر من stack را طوری پیاده سازی کنم که با هر push که انجام می دهم کند تر و کندتر بشود دیگه کسی علاقه ای به استفاده از این stack نخواهد داشت.
درست است که لازمه که پیاده سازی را از رابط (interface) جدا کنیم اما این نباید به قیمت صرفنظر کردن از پیچیدگی باشد. پیچیدگی باید به عنوان بخشی از قرارداد نانوشته بین یک ماژول و کاربر آن باشد. علت معرفی مفهوم نوع داده انتزاعی، امکان پذیر کردن ماژولهای نرم افزاری قابل تعویض بود.در صورتی ماژولها، قابل تعویض خواهند بود که از نظر پیچیدگی رفتار مشابهی داشته باشند.اگر من یک ماژول را با ماژول دیگر جایگزین کنم که از نظر کاری که انجام می دهند یکسان باشند اما هزینه پیچیدگی متفاوتی داشته باشند، کاربر این کد به طرز ناخوشایندی غافلگیر می شود. من هر چه قدر که بخواهم می توانم با او در مورد انتزاع داده ها صحبت کنم اما نهایتا از کد من استفاده نخواهد کرد.بخشی از رابط، باید شامل بیان پیچیدگی باشد.
حدود سال 1983 من بعد از جنرال الکتریک به دانشگاه پلی تکنیک رفتم و شروع به کار در زمینه الگوریتمهای گراف کردم. یاور اصلی من Aaron Kershenbaum بود که الان در IBM هست. او یک متخصص در زمینه الگوریتمهای گراف و شبکه بود و من او را متقاعد کردم بعضی ایده های برنامه نویسی مرتبه بالا (high order programming) و عام، قابل اعمال روی الگوریتم های گراف است. بودجه هایی در دست او بود و از من حمایت کرد تا با او این ایده ها را روی الگوریتمهای شبکه های واقعی اعمال کنیم. او علاقه داشت که یک تولباکس شامل کامپوننتهای مرتبه بالا و عام ایجاد کند تا در آن برخی از این الگوریتمها را بتوان پیاده سازی کرد. چون بعضی الگوریتمهای شبکه آنقدر پیچیده هستند که علیرغم اینکه به صورت نظری تحلیل شده اند اما اصلا پیاده سازی نشده اند. من تصمیم گرفتم از یکی از گونه های زبان LISP به نام Scheme جهت ساخت این تولباکس استفاده کنم. Aaron و من یک کتابخانه بزرگ از کامپوننتها را به زبان Scheme توسعه دادیم که در آن از انواع مختلف تکنیکهای برنامه نویسی بهره برده بودیم. هدف اولیه، الگوریتمهای شبکه بود. بعدا Dave Musser که هنوز در جنرال الکتریک بود به ما پیوست که ما کمپوننتهای بیشتری ایجاد کردیم که یک کتابخانه نسبتا بزرگ شد. آن کتابخانه در دانشگاه توسط دانشجویان تحصیلات تکمیلی مورد استفاده قرار گرفت اما استفاده تجاری از آن نشد. در طول این فعالیت متوجه شدم که «اثرات جانبی» اهمیت دارند چون عملیات روی گراف را نمی توان بدون اثرات جانبی انجام داد. وقتی بخواهیم فقط یک راس گراف را اصلاح کنیم نمیتونیم (نباید) کل گراف را کپی کنیم. نتیجه ای که از این مطلب گرفتم این بود که در ساخت الگوریتمهای عام می شود تکنیکهای مرتبه بالا را با استفاده کنترل شده اثرات جانبی ترکیب کرد. اثرات جانبی لزوما بد نیستند .آنها در صورتی بد هستند که استفاده درستی از آنها نشود.
در تابستان 1985 دوباره به موسسه تحقیقاتی جنرال الکتریک دعوت شدم تا برنامه نویسی مرتبه بالا را تدریس کنم. که در آنجا نحوه ایجاد الگوریتمهای پیچیده را با استفاده از این تکنیک ارائه می دادم. یکی از افرادی که در آنجا حضور داشت Art Chen مدیر Information Systems Laboratory بود. به حدی تحت تاثیر قرار گرفته بود که از من خواست که آیا می توانم کتابخانه ای جهت کارهای صنعتی را با استفاده از این تکنیکها با زبان Ada بنویسم که البته با تامین و پشتیبانی وی همراه خواهد بود. به علت اینکه یک استادیار بی بضاعت بودم پیشنهاد را قبول کردم در حالیکه در آن زمان اصلا هیچ چیز در مورد زبان Ada بلد نبودم. در ساخت کتابخانه Ada با Dave Musser همکار شدم. پروژه مهمی بود چون با تغییر مسیر از یک زبان با نوع پویا مثل Scheme به یک زبان با نوع ایستا مثل Ada باعث شد اهمیت سیستم نوع قوی (strong typing) را متوجه بشوم. هر کسی می داند سیستم نوع قوی کمک به یافتن خطاها می کند. من فهمیدم که سیستم نوع قوی در برنامه نویسی عام در زبان Ada وسیله ای جهت طراحی هست. این خصوصیت نه تنها وسیله ای جهت گرفتن باگها بلکه وسیله ای جهت فکر کردن بود. این مساله منجر به ایده تجزیه متعامد فضای کامپوننت شد. من دریافتم که کامپوننتهای نرم افزاری به گروههای مختلفی تعلق دارند. هنرمندان عرصه برنامه نویسی شیء گرا فکر می کنند هر چیزی Object است. هنگامی که روی کتابخانه عام Ada کار می کردم فهمیدم که این گونه نیست. چیزهایی هستند که Object هستند در واقع چیزهایی که حالت دارند و حالت آنها تغییر می کند Object هستند. همچنین چیزهایی هستند که Object نیستند. یک جستجوی دودویی Object نیست. در واقع یک الگوریتم است. علاوه بر این در آن زمان دریافتم که با تجزیه فضای کامپوننت به چندین بعد متعامد ما می توانیم تعداد کامپوننتها را کم کنیم و مهم تر از آن ما می توانیم یک چارچوب مفهومی را برای نحوه طراحی فراهم کنیم.
سپس جهت کار در گروهی که روی کتابخانه های ++c کار می کرد به من شغلی پیشنهاد شد. از من سوال کردند که آیا می توانم (ایده هایم) را در ++c پیاده کنم. خب من ++C را بلد نبودم ولی به آنها جواب مثبت دادم. اما من ایده هایم را نتوانستم در ++C پیاده کنم چون در سال ۱۹۸۷ هنوز template در ++C وجود نداشت که برای این سبک برنامه نویسی لازم است. تنها سازوکار جهت رسیدن به عامیت، وراثت بود که ناکافی بود.
حتی اکنون هم وراثت در ++C کاربرد چندانی جهت برنامه نویسی عام ندارد. بگذارید توضیح بدهم. خیلی ها تلاش کرده اند تا از وراثت جهت پیاده سازی ساختمان های داده و کلاسهای محتوی (container class) استفاده ببرند. اما اکنون می دانیم این تلاشها موفقیت آمیز نبوده. وراثت در ++C و سبک برنامه نویسی مرتبط با آن به شدت محدود هست. امکان ندارد که یک طراحی ـ که مثلا شامل یک عملگر ساده مثل تساوی باشد ـ را پیاده سازی کرد که بتوانیم درست ازش استفاده کنیم. مثلا در راس سلسله مراتب کلاس شما کلاس پایه ای به نام X باشد و یک عملگر تساوی مجازی (virtual) برای کلاس X تعریف کنید که آرگومانی از نوع X بگیرد، سپس کلاسی به نام Y را از کلاس X مشتق کنید. نتیجه تساوی چه می شود؟ در این تساوی Y با X مقایسه می شود!
به عنوان یک مثال دیگه از حیوانات استفاده می کنیم (طرفداران شیء گرایی به حیوانات علاقه دارند.) کلاس پستانداران را تعریف کنید و کلاس زرافه را از آن مشتق کنید. یک تابع عضو به نام جفتگیری برای پستاندار تعریف کنید که پستاندار با پستاندار جفتگیری می کند و یک پستاندار را بر می گرداند. وقتی زرافه از پستاندار مشتق می شود دارای تابعی به نام جفت گیری می شود که در آن زرافه با پستاندار جفت گیری می کند و یک پستاندار را بر می گرداند. و قطعا این چیزی نیست که شما می خواهید. خب جفت گیری اهمیت چندانی برای برنامه نویسان ندارد اما «تساوی» اهمیت دارد. من حتی یک الگوریتم را ندیدم که نوعی از تساوی در آن استفاده نشده باشد.
برای چنین مسائلی نیاز به template می باشد. اینجا شما یک کلاس template برای پستانداران دارید که یک تابع عضو به نام جفت گیری دارد که پستاندار را می گیرد و پستاندار را بر می گرداند. وقتی این template را با زرافه نمونه سازی کنید تابع جفت گیری آن گونه که باید باشد می شود. بنابراین template از این نظر سازوکار قدرتمندتری هست.
در هر صورت من قادر بودم که کتابخانه ای بزرگ از الگوریتم ها را ایجاد کنم که بعدا بخشی از کتابخانه Unix System Laboratory Standard Component شد. من در آزمایشگاههای Bell در صحبت با افرادی نظیر Andy Koenig و Bjarne Stroustrup در مورد برنامه نویسی چیزهای زیادی یاد گرفتم. و متوجه شدم که ++C/C یک زبان برنامه نویسی مهم با چارچوبهایی اساسی است که نمی شود آنها را نادیده گرفت. مشخصا یادگرفتم که اشاره گر ها خیلی خوب هستند. البته منظورم اشاره گرهای معلق (dangling pointer) نیست. منظورم اشاره گر به stack نیست. اما منظورم این است که مفهوم کلی اشاره گر یک ابزار قدرتمند می باشد. که مفهوم آدرس به صورت فراگیر استفاده می شود. به صورت غلط تصور می شود که اشاره گر ها تفکر ما را به صورت متوالی (sequential) در می آورد. نه این طور نیست. بدون استفاده از مغهوم اشاره گر نمی توانیم بعضی الگوریتم های موازی را بیان کنیم. مثلا اگر بخواهیم مجموع n عدد را به صورت موازی بیان کنیم، ما نمی توانیم این کار را انجام بدهیم مگر اینکه بگوییم که اولین عدد با دومین عدد جمع می شود و سومین عدد با چهارمین عدد جمع می شود. یعنی به نوعی اندیس گذاری نیاز داریم. به نوعی از آدرس نیاز داریم تا هر الگوریتم را بیان کنیم، چه متوالی چه موازی. مفهوم آدرس یا موقعیت مفهومی اساسی در شکل دادن فرایندهای محاسباتی (الگوریتم ها) هست.
اکنون بگذارید به این بپردازیم که چرا C زبان بزرگی است. معمولا تصور می شود که C که یک ترفند (hack) هست، به این علت موفق بوده که Unix را با آن نوشتند.من موافق با این نظر نیستم. در مدت زمانی طولانی معماری کامپوتر متحول شده، نه به این علت که افرادی باهوش در فکر این بوده اند که چگونه معماری ها را متحول کنند ـ البته در این مدت افراد باهوش، معماریهای برچسب دار (tagged) را معرفی کردندـ بلکه به علت نیاز برنامه نویسان مختلف جهت حل مسائل واقعی بوده است. کامپیوترهایی که فقط قادر بودند با اعداد کار کنند به کامپیوترهایی با حافظه قابل آدرس دهی با بایت ،قابل آدرس دهی با فضای آدرس تخت و قابل آدرس دهی با اشاره گر تبدیل شدند. این تحولی طبیعی بود که بازتاب مسائل در حال رشدی بود که افراد می خواستند حل کنند. C که هوش Dennis Ritchie را نشان می دهد یک مدل حداقلی را از کامپیوتری که در طی ۳۰ سال متحول شده بود ارائه کرد. C یک ترفند یک شبه نبوده. همان طور که کامپیوترها جهت حل مسائل مختلف متحول شدند و C هم مدل حداقلی این گونه کامپیوتر شد و در نتیجه تبدیل به یک زبان بسیار قدرتمند جهت حل مسائل مختلف در حوزه های مختلف و در عین حال با کارایی بسیار بالا شد. ضمن اینکه مدل ماشین پشت زبان C را می شود فهمید. یعنی برای یک مهندس با سطح متوسط، فهم مدل ماشین C راحت تر از فهمیدن مدل ماشین Ada یا حتی Scheme است. C موفق شد چونکه کار درست را انجام می داد نه به خاطر اینکه AT&T آن را ترویج می کرد یا اینکه Unix را با آن نوشته اند.
زبان ++C موفق بوده چون Bjarne به جای درگیر شدن عمیق در ابداع یک مدل ماشین جدید، با C شروع کرد و سعی کرد با وارد کرد تکنیکهای برنامه نویسی کلی تر اما در چارچوب این مدل ماشین، زبان C را تکامل بیشتری بدهد. مدل ماشین C بسیار ساده است. ما حافظه را داریم که در آن چیزهای مختلف قرار می گیرند. ما اشاره گر به عناصر متوالی حافظه داریم. که به راحتی قابل فهم است. ++C این مدل را می گیرد اماچیزهایی که در حافظه قرار می گیرند را گسترده تر از ماشین C می کند، زیرا C تعداد محدودی نوع داده دارد. البته ساختارها (structure) در C وجود دارند که سیستم نوع قابل توسعه ای را امکان پذیر می کند اما در این سیستم امکان تعریف عملیات برای ساختار وجود ندارد که باعث محدودیت در توسعه پذیری سیستم نوع می شود. ++C مدل ماشین C را به سمت یک سیستم نوع «حقیقتا» قابل توسعه برد.
در سال 1988 به آزمایشگاههای HP رفتم که در آنجا جهت کار بر روی کتابخانه های عام به کار گرفته شدم. اما برای چندین سال به جای کار بر روی کتابخانه، روی درایور دیسک کار می کردم که هیجان انگیز بود اما در تضاد با آن موضوع تحقیقاتی بود. در سال 1992، Bill Worley که رئیس آزمایشگاه من بود یک پروژه الگوریتم ترتیب داد که من مدیر آن شدم در نتیجه دوباره برگشتم سراغ توسعه کتابخانه عام. در آن زمان template در ++C وجود داشت. دیدم که Bjarne با طراحی template کار حیرت آوری انجام داده. من قبلا در آزمایشگاه Bell در بحثهای اولیه در خصوص طراحی template مشارکت کرده بودم و با جزمیت به Bjarne گفته بودم که او باید template های ++C را تا جایی که امکان دارد مشابه بخشهای عام زبان Ada ایجاد کند. فکر می کنم آنگونه با جزمیت با Bjarne بحث کردم که او تصمیمی مخالف آن گرفت. من برخلاف بعضی که فقط بر اهمیت ایجاد template های کلاس باور داشتند به ایجاد template های تابع هم فکر می کردم. اما در عین حال فکر می کردم تابعهای template باید شبیه بخشهای عام Ada کار کنند یعنی به طور صریح باید نمونه سازی (instantiation) شوند. Bjarne به حرف من گوش نداد و سازوکاری برای تابع template طراحی کرد که تابع template به صورت ضمنی با استفاده از سازوکار سربارگذاری، نمونه سازی می شد. این تکنیک خاص برای کار من نقش حیاتی پیدا کرد چون فهمیدم که این امکان را به من می دهد که بسیاری کارها را که در Ada امکان پذیر نبود انجام بدهم. بنابراین من این طراحی خاص که Bjarne انجام داد را به عنوان کاری حیرت آور می دانم و خیلی خوشحالم که توصیه من را گوش نکرد.
-سوال:در مورد علاقه طولانی مدتتان به برنامه نویسی عام (generic programming) صحبت بفرمایید.
-جواب:در اواخر دهه ۷۰ میلادی شروع به فکر کردن در مورد برنامه نویسی عام کردم در آن زمان این مطلب را دریافته بودم که بعضی الگوریتم ها به پیاده سازی ساختمان داده بستگی ندارند بلکه به تعدادی خصوصیات مفهومی اساسی آن ساختمان بستگی دارند. شروع کردم به بررسی الگوریتمهای مختلف و فهمیدم که بیشتر الگوریتم ها را می توان بدون وابستگی به پیاده سازی به مرحله ای از انتزاع (abstraction) رساند که کارایی الگوریتم پایین نیاید. کارایی، از دغدغه های اساسی من است. این احمقانه است که یک الگوریتم را به صورتی به انتزاع برسانیم که وقتی آن را از حالت انتزاع به نمونه اصلی برگردانیم کارایی اون از بین برود.
در آن زمان من فکر می کردم راه درست برای انجام این نوع مطالعه این است که یه زبان برنامه نویسی را ایجاد کنیم. در نتیجه با دو نفر از دوستانم به نام Deepak Kapur که در حال حاضر پروفسور در دانشگاه ایالتی نیویورک است و David Musser که پروفسور موسسه پلی تکنیک Rensselaer است این کار را شروع کردیم. در آن زمان هر سه در مرکز تحقیقاتی جنرال الکتریک کار می کردیم. ما شروع به کار روی یک زبان به نام Tecton کردیم که این زبان به افراد اجازه می داد که الگوریتم را به وسیله چیزی به نام ساختمانهای عام (generic structure) بیان کند. این ساختمانهای عام در واقع مجموعه ای از «نوع ها» و خصوصیات آنها بود. در واقع یه سری مطالب به زبان ریاضی. ما دریافتیم که می شود یک جبر را تعریف کنیم که موضوع آن عملیات روی این ساختمانها باشد در نتیجه می شود آنها را اصلاح کرد می شود چیزهایی به آن اضافه کرد و کارهای مختلفی را روی آنها انجام داد.
ایده های جالبی داشتیم اما این تحقیق ما به نتایج عملی منجر نشد زیرا Tecton یک زبان functional بود. ما به ایده های Backus معتقد بودیم که باید از سبک برنامه نویسی وان نیومن رها بشویم و نمی خواستیم که در زبان ما اثرات جانبی (side effect) وجود داشته باشه. که این محدودیتی برای ما ایجاد کرد که نتوانیم با الگوریتمهای زیادی که در آنها مفهوم «حالت» (state) و «اثرات جانبی» وجود داشت کار کنیم.
در خصوص Tecton در اواخر دهه ۷۰ به نکته جالبی پی بردم این نکته این بود که مفهومی که از نوع داده انتزاعی (abstract data type)در آن زمان پذیرفته شده بود دارای یک محدودیت اساسی است. ما معمولا به این صورت در نظر می گیریم که نوع داده انتزاعی به ما درمورد رفتار اشیاء می گوید اما در مورد پیاده سازی صحبتی نمی کند. عموما این گونه پذیرفته شده که پیچیدگی یک عملیات (operation)، بخشی از پیاده سازی است و انتزاع، در مورد پیچیدگی صحبتی نمی کند. یکی از مواردی که از نکات مرکزی برنامه نویسی عام است، این است که پیچیدگی یا حداقل مفهومی کلی از پیچیدگی را باید با یک عملیات مربوط کرد.
یک مثال می زنم. شما stack را به عنوان یک نوع داده انتزاعی در نظر بگیرید. خب اینکه در مورد stack دو عملیات push و pop را در داشته باشیم که مثلا با push یک چیزی وارد stack بشود سپس با استفاده از pop همون چیز را بازیابی کنید این به تنهایی کافی نیست بلکه نکته مهم دیگری که باید توجه بشود این است که عملیات push بدون توجه به اندازه stack باید در یک زمان ثابت انجام بشود. اگر من stack را طوری پیاده سازی کنم که با هر push که انجام می دهم کند تر و کندتر بشود دیگه کسی علاقه ای به استفاده از این stack نخواهد داشت.
درست است که لازمه که پیاده سازی را از رابط (interface) جدا کنیم اما این نباید به قیمت صرفنظر کردن از پیچیدگی باشد. پیچیدگی باید به عنوان بخشی از قرارداد نانوشته بین یک ماژول و کاربر آن باشد. علت معرفی مفهوم نوع داده انتزاعی، امکان پذیر کردن ماژولهای نرم افزاری قابل تعویض بود.در صورتی ماژولها، قابل تعویض خواهند بود که از نظر پیچیدگی رفتار مشابهی داشته باشند.اگر من یک ماژول را با ماژول دیگر جایگزین کنم که از نظر کاری که انجام می دهند یکسان باشند اما هزینه پیچیدگی متفاوتی داشته باشند، کاربر این کد به طرز ناخوشایندی غافلگیر می شود. من هر چه قدر که بخواهم می توانم با او در مورد انتزاع داده ها صحبت کنم اما نهایتا از کد من استفاده نخواهد کرد.بخشی از رابط، باید شامل بیان پیچیدگی باشد.
حدود سال 1983 من بعد از جنرال الکتریک به دانشگاه پلی تکنیک رفتم و شروع به کار در زمینه الگوریتمهای گراف کردم. یاور اصلی من Aaron Kershenbaum بود که الان در IBM هست. او یک متخصص در زمینه الگوریتمهای گراف و شبکه بود و من او را متقاعد کردم بعضی ایده های برنامه نویسی مرتبه بالا (high order programming) و عام، قابل اعمال روی الگوریتم های گراف است. بودجه هایی در دست او بود و از من حمایت کرد تا با او این ایده ها را روی الگوریتمهای شبکه های واقعی اعمال کنیم. او علاقه داشت که یک تولباکس شامل کامپوننتهای مرتبه بالا و عام ایجاد کند تا در آن برخی از این الگوریتمها را بتوان پیاده سازی کرد. چون بعضی الگوریتمهای شبکه آنقدر پیچیده هستند که علیرغم اینکه به صورت نظری تحلیل شده اند اما اصلا پیاده سازی نشده اند. من تصمیم گرفتم از یکی از گونه های زبان LISP به نام Scheme جهت ساخت این تولباکس استفاده کنم. Aaron و من یک کتابخانه بزرگ از کامپوننتها را به زبان Scheme توسعه دادیم که در آن از انواع مختلف تکنیکهای برنامه نویسی بهره برده بودیم. هدف اولیه، الگوریتمهای شبکه بود. بعدا Dave Musser که هنوز در جنرال الکتریک بود به ما پیوست که ما کمپوننتهای بیشتری ایجاد کردیم که یک کتابخانه نسبتا بزرگ شد. آن کتابخانه در دانشگاه توسط دانشجویان تحصیلات تکمیلی مورد استفاده قرار گرفت اما استفاده تجاری از آن نشد. در طول این فعالیت متوجه شدم که «اثرات جانبی» اهمیت دارند چون عملیات روی گراف را نمی توان بدون اثرات جانبی انجام داد. وقتی بخواهیم فقط یک راس گراف را اصلاح کنیم نمیتونیم (نباید) کل گراف را کپی کنیم. نتیجه ای که از این مطلب گرفتم این بود که در ساخت الگوریتمهای عام می شود تکنیکهای مرتبه بالا را با استفاده کنترل شده اثرات جانبی ترکیب کرد. اثرات جانبی لزوما بد نیستند .آنها در صورتی بد هستند که استفاده درستی از آنها نشود.
در تابستان 1985 دوباره به موسسه تحقیقاتی جنرال الکتریک دعوت شدم تا برنامه نویسی مرتبه بالا را تدریس کنم. که در آنجا نحوه ایجاد الگوریتمهای پیچیده را با استفاده از این تکنیک ارائه می دادم. یکی از افرادی که در آنجا حضور داشت Art Chen مدیر Information Systems Laboratory بود. به حدی تحت تاثیر قرار گرفته بود که از من خواست که آیا می توانم کتابخانه ای جهت کارهای صنعتی را با استفاده از این تکنیکها با زبان Ada بنویسم که البته با تامین و پشتیبانی وی همراه خواهد بود. به علت اینکه یک استادیار بی بضاعت بودم پیشنهاد را قبول کردم در حالیکه در آن زمان اصلا هیچ چیز در مورد زبان Ada بلد نبودم. در ساخت کتابخانه Ada با Dave Musser همکار شدم. پروژه مهمی بود چون با تغییر مسیر از یک زبان با نوع پویا مثل Scheme به یک زبان با نوع ایستا مثل Ada باعث شد اهمیت سیستم نوع قوی (strong typing) را متوجه بشوم. هر کسی می داند سیستم نوع قوی کمک به یافتن خطاها می کند. من فهمیدم که سیستم نوع قوی در برنامه نویسی عام در زبان Ada وسیله ای جهت طراحی هست. این خصوصیت نه تنها وسیله ای جهت گرفتن باگها بلکه وسیله ای جهت فکر کردن بود. این مساله منجر به ایده تجزیه متعامد فضای کامپوننت شد. من دریافتم که کامپوننتهای نرم افزاری به گروههای مختلفی تعلق دارند. هنرمندان عرصه برنامه نویسی شیء گرا فکر می کنند هر چیزی Object است. هنگامی که روی کتابخانه عام Ada کار می کردم فهمیدم که این گونه نیست. چیزهایی هستند که Object هستند در واقع چیزهایی که حالت دارند و حالت آنها تغییر می کند Object هستند. همچنین چیزهایی هستند که Object نیستند. یک جستجوی دودویی Object نیست. در واقع یک الگوریتم است. علاوه بر این در آن زمان دریافتم که با تجزیه فضای کامپوننت به چندین بعد متعامد ما می توانیم تعداد کامپوننتها را کم کنیم و مهم تر از آن ما می توانیم یک چارچوب مفهومی را برای نحوه طراحی فراهم کنیم.
سپس جهت کار در گروهی که روی کتابخانه های ++c کار می کرد به من شغلی پیشنهاد شد. از من سوال کردند که آیا می توانم (ایده هایم) را در ++c پیاده کنم. خب من ++C را بلد نبودم ولی به آنها جواب مثبت دادم. اما من ایده هایم را نتوانستم در ++C پیاده کنم چون در سال ۱۹۸۷ هنوز template در ++C وجود نداشت که برای این سبک برنامه نویسی لازم است. تنها سازوکار جهت رسیدن به عامیت، وراثت بود که ناکافی بود.
حتی اکنون هم وراثت در ++C کاربرد چندانی جهت برنامه نویسی عام ندارد. بگذارید توضیح بدهم. خیلی ها تلاش کرده اند تا از وراثت جهت پیاده سازی ساختمان های داده و کلاسهای محتوی (container class) استفاده ببرند. اما اکنون می دانیم این تلاشها موفقیت آمیز نبوده. وراثت در ++C و سبک برنامه نویسی مرتبط با آن به شدت محدود هست. امکان ندارد که یک طراحی ـ که مثلا شامل یک عملگر ساده مثل تساوی باشد ـ را پیاده سازی کرد که بتوانیم درست ازش استفاده کنیم. مثلا در راس سلسله مراتب کلاس شما کلاس پایه ای به نام X باشد و یک عملگر تساوی مجازی (virtual) برای کلاس X تعریف کنید که آرگومانی از نوع X بگیرد، سپس کلاسی به نام Y را از کلاس X مشتق کنید. نتیجه تساوی چه می شود؟ در این تساوی Y با X مقایسه می شود!
به عنوان یک مثال دیگه از حیوانات استفاده می کنیم (طرفداران شیء گرایی به حیوانات علاقه دارند.) کلاس پستانداران را تعریف کنید و کلاس زرافه را از آن مشتق کنید. یک تابع عضو به نام جفتگیری برای پستاندار تعریف کنید که پستاندار با پستاندار جفتگیری می کند و یک پستاندار را بر می گرداند. وقتی زرافه از پستاندار مشتق می شود دارای تابعی به نام جفت گیری می شود که در آن زرافه با پستاندار جفت گیری می کند و یک پستاندار را بر می گرداند. و قطعا این چیزی نیست که شما می خواهید. خب جفت گیری اهمیت چندانی برای برنامه نویسان ندارد اما «تساوی» اهمیت دارد. من حتی یک الگوریتم را ندیدم که نوعی از تساوی در آن استفاده نشده باشد.
برای چنین مسائلی نیاز به template می باشد. اینجا شما یک کلاس template برای پستانداران دارید که یک تابع عضو به نام جفت گیری دارد که پستاندار را می گیرد و پستاندار را بر می گرداند. وقتی این template را با زرافه نمونه سازی کنید تابع جفت گیری آن گونه که باید باشد می شود. بنابراین template از این نظر سازوکار قدرتمندتری هست.
در هر صورت من قادر بودم که کتابخانه ای بزرگ از الگوریتم ها را ایجاد کنم که بعدا بخشی از کتابخانه Unix System Laboratory Standard Component شد. من در آزمایشگاههای Bell در صحبت با افرادی نظیر Andy Koenig و Bjarne Stroustrup در مورد برنامه نویسی چیزهای زیادی یاد گرفتم. و متوجه شدم که ++C/C یک زبان برنامه نویسی مهم با چارچوبهایی اساسی است که نمی شود آنها را نادیده گرفت. مشخصا یادگرفتم که اشاره گر ها خیلی خوب هستند. البته منظورم اشاره گرهای معلق (dangling pointer) نیست. منظورم اشاره گر به stack نیست. اما منظورم این است که مفهوم کلی اشاره گر یک ابزار قدرتمند می باشد. که مفهوم آدرس به صورت فراگیر استفاده می شود. به صورت غلط تصور می شود که اشاره گر ها تفکر ما را به صورت متوالی (sequential) در می آورد. نه این طور نیست. بدون استفاده از مغهوم اشاره گر نمی توانیم بعضی الگوریتم های موازی را بیان کنیم. مثلا اگر بخواهیم مجموع n عدد را به صورت موازی بیان کنیم، ما نمی توانیم این کار را انجام بدهیم مگر اینکه بگوییم که اولین عدد با دومین عدد جمع می شود و سومین عدد با چهارمین عدد جمع می شود. یعنی به نوعی اندیس گذاری نیاز داریم. به نوعی از آدرس نیاز داریم تا هر الگوریتم را بیان کنیم، چه متوالی چه موازی. مفهوم آدرس یا موقعیت مفهومی اساسی در شکل دادن فرایندهای محاسباتی (الگوریتم ها) هست.
اکنون بگذارید به این بپردازیم که چرا C زبان بزرگی است. معمولا تصور می شود که C که یک ترفند (hack) هست، به این علت موفق بوده که Unix را با آن نوشتند.من موافق با این نظر نیستم. در مدت زمانی طولانی معماری کامپوتر متحول شده، نه به این علت که افرادی باهوش در فکر این بوده اند که چگونه معماری ها را متحول کنند ـ البته در این مدت افراد باهوش، معماریهای برچسب دار (tagged) را معرفی کردندـ بلکه به علت نیاز برنامه نویسان مختلف جهت حل مسائل واقعی بوده است. کامپیوترهایی که فقط قادر بودند با اعداد کار کنند به کامپیوترهایی با حافظه قابل آدرس دهی با بایت ،قابل آدرس دهی با فضای آدرس تخت و قابل آدرس دهی با اشاره گر تبدیل شدند. این تحولی طبیعی بود که بازتاب مسائل در حال رشدی بود که افراد می خواستند حل کنند. C که هوش Dennis Ritchie را نشان می دهد یک مدل حداقلی را از کامپیوتری که در طی ۳۰ سال متحول شده بود ارائه کرد. C یک ترفند یک شبه نبوده. همان طور که کامپیوترها جهت حل مسائل مختلف متحول شدند و C هم مدل حداقلی این گونه کامپیوتر شد و در نتیجه تبدیل به یک زبان بسیار قدرتمند جهت حل مسائل مختلف در حوزه های مختلف و در عین حال با کارایی بسیار بالا شد. ضمن اینکه مدل ماشین پشت زبان C را می شود فهمید. یعنی برای یک مهندس با سطح متوسط، فهم مدل ماشین C راحت تر از فهمیدن مدل ماشین Ada یا حتی Scheme است. C موفق شد چونکه کار درست را انجام می داد نه به خاطر اینکه AT&T آن را ترویج می کرد یا اینکه Unix را با آن نوشته اند.
زبان ++C موفق بوده چون Bjarne به جای درگیر شدن عمیق در ابداع یک مدل ماشین جدید، با C شروع کرد و سعی کرد با وارد کرد تکنیکهای برنامه نویسی کلی تر اما در چارچوب این مدل ماشین، زبان C را تکامل بیشتری بدهد. مدل ماشین C بسیار ساده است. ما حافظه را داریم که در آن چیزهای مختلف قرار می گیرند. ما اشاره گر به عناصر متوالی حافظه داریم. که به راحتی قابل فهم است. ++C این مدل را می گیرد اماچیزهایی که در حافظه قرار می گیرند را گسترده تر از ماشین C می کند، زیرا C تعداد محدودی نوع داده دارد. البته ساختارها (structure) در C وجود دارند که سیستم نوع قابل توسعه ای را امکان پذیر می کند اما در این سیستم امکان تعریف عملیات برای ساختار وجود ندارد که باعث محدودیت در توسعه پذیری سیستم نوع می شود. ++C مدل ماشین C را به سمت یک سیستم نوع «حقیقتا» قابل توسعه برد.
در سال 1988 به آزمایشگاههای HP رفتم که در آنجا جهت کار بر روی کتابخانه های عام به کار گرفته شدم. اما برای چندین سال به جای کار بر روی کتابخانه، روی درایور دیسک کار می کردم که هیجان انگیز بود اما در تضاد با آن موضوع تحقیقاتی بود. در سال 1992، Bill Worley که رئیس آزمایشگاه من بود یک پروژه الگوریتم ترتیب داد که من مدیر آن شدم در نتیجه دوباره برگشتم سراغ توسعه کتابخانه عام. در آن زمان template در ++C وجود داشت. دیدم که Bjarne با طراحی template کار حیرت آوری انجام داده. من قبلا در آزمایشگاه Bell در بحثهای اولیه در خصوص طراحی template مشارکت کرده بودم و با جزمیت به Bjarne گفته بودم که او باید template های ++C را تا جایی که امکان دارد مشابه بخشهای عام زبان Ada ایجاد کند. فکر می کنم آنگونه با جزمیت با Bjarne بحث کردم که او تصمیمی مخالف آن گرفت. من برخلاف بعضی که فقط بر اهمیت ایجاد template های کلاس باور داشتند به ایجاد template های تابع هم فکر می کردم. اما در عین حال فکر می کردم تابعهای template باید شبیه بخشهای عام Ada کار کنند یعنی به طور صریح باید نمونه سازی (instantiation) شوند. Bjarne به حرف من گوش نداد و سازوکاری برای تابع template طراحی کرد که تابع template به صورت ضمنی با استفاده از سازوکار سربارگذاری، نمونه سازی می شد. این تکنیک خاص برای کار من نقش حیاتی پیدا کرد چون فهمیدم که این امکان را به من می دهد که بسیاری کارها را که در Ada امکان پذیر نبود انجام بدهم. بنابراین من این طراحی خاص که Bjarne انجام داد را به عنوان کاری حیرت آور می دانم و خیلی خوشحالم که توصیه من را گوش نکرد.