فالکن

تاریخچه

یک پروژه کوچک به نام هست، که در سال ۲۰۰۲ به منظور ایجاد یک ماشین مجازی کوچک و سریع آغاز شد بعد از مدتی به طراحی و ساخت زبان برنامه‌نویسی فالکن منجر شد.
جیانکارلو نیکولی، طراح فالکن، در مصاحبه‌ای می‌گوید: «من از زبان‌های زیادی از جمله C++، C، جاوا، اسمبلی، لیسپ، پرولوگ، کلیپر، دلفی، SQL «...» استفاده کرده‌ام؛ و نیاز به یک ابزار قابل انعطاف که نیازهای روزانه مرا در بر بگیرد و امکان بروز ایده‌های جدید را فراهم کند، را احساس کردم. «...» استفاده از یک مدل برنامه‌نویسی برای ماشین سودمند است. «...» اما استفاده از یک مدل خالص برای ذهن و فکر مناسب نیست. زیرا که در دنیای واقع هنگامی که از تعداد زیادی روندها و مدل‌های حل بتوان استفاده کرد، راه حل‌های بهتری ایجاد می‌شوند. «...» بنابراین فالکن ایجاد شد با ایده دارا بودن امکانات یک زبان شئ‌گرا خالص در حالی که یک زبان شئ‌گرا نیست، دارا بودن امکانات یک زبان امری خالص در حالی که یک زبان امری نیست و دارا بودن ساختارهای تابعی در حالی که یک زبان تابعی نیست. «...» اگر یک چیز دربارهٔ این زبان باشد که من به آن افتخار کنم، این است که این زبان به دلایل خارجی ایجاد نشد. بلکه به منظور حل مشکل یکپارچه سازی و مدیریت دسترسی در برنامه‌های کاربردی بزرگ از یک سو و نیاز به حل مسئله‌های پیچیده منطقی و مسئله‌ها با تغییرپذیری بالا از سوی دیگر، ایجاد شد.»

ساختارهای انتخاب

ساختار if/elif/else: ساختار انتخاب if می‌تواند به صورت اختیاری دارای عبارت else یا تعدادی عبارت elif باشد. هر عبارت elif یک عبارت شرطی را ارزیابی می‌کند و در صورت درست بودن، دستورات زیر آن اجرا می‌شود در غیر این صورت عبارت elif بعدی در صورت وجود یا عبارت else بررسی می‌شود. یک عبارت if کامل مشابه زیر است:
if expresion
 statements...
elif expresion
 statements...
elif expresion
 statements...
 /*other elifs*/
else
 statements...
end
یک روش دیگر برای اجرای یک تکه برنامه بر اساس ساختار if، استفاده از عملوند fast-if است که ساختار آن چنین است:
<condition> ? <if true> [: <if false>]
if a == true ? printl("A is true")
ساختار switch: هنگامی که نیاز به بررسی یک مقدار در مقابل مقادیر زیادی داریم از این ساختار استفاده می‌شود. تنها محدودیت این ساختار این است که عبارتی که مقدار آن بررسی می‌شود، باید رشته یا عدد صحیح باشد. وجود عبارت default اختیاری است. یک عبارت switch کامل مشابه زیر است:
switch expresion
case item [, item,.. , item]
 statements...
case item [, item,.. , item]
 statements...
 /*other cases*/
default
 statements...
end
عناصری که برای انتخاب در مقابل caseها قرار می‌گیرند می‌توانند عددهای صحیح، رشته‌های حرفی، بازه‌های عددی، نام متغیر یا nil باشند.
ساختار select: مانند ساختار switch است با این تفاوت که تایپ متغیر را برای انتخاب بررسی می‌کند و نه مقدار آن را. یک عبارت select کامل مشابه زیر است:
select variable
case TypeSpec
 statements...
case TypeSpec1, TypeSpec2,... , TypeSpecN
 statements...
 /*other cases*/
default
 statements...
end

ساختارهای تکرار

ساختار while: مهم‌ترین ساختار تکرار این زبان است. شامل هیچ یا چند دستور است که می‌توانند بیش از یک بار اجرا شوند. break و continue دو دستور خاص هستند که در صورت قرار گرفت در بلوک ساختار while به ترتیب سبب پایان یافتن حلقه و انتقال بی درنگ کنترل برنامه به قسمت ارزیابی شرط حلقه می‌شوند؛ بنابراین با داشتن دستور break می‌توان حلقه بی‌نهایت نیز داشت. یک عبارت while مشابه زیر است:
while expresion
 statements...
   [break]
 statements...
   [continue]
 statements...
end
اگر بلوک while تنها شامل یک دستور باشد، می‌توان آن را با استفاده از یک علامت دونقطه خلاصه کرد. یک مثال می‌تواند چنین باشد:
while var <10: var = calc(var)
ساختار loop: مانند حلقه while است با این تفاوت که در آن شرطی وجود نداره وحلقه تا زمانی که با یک دستور در داخل آن از حلقه خارج نشود، تکرار می‌شود. خروج از حلقه علاوه بر دستور break می‌تواند با دستور return یا بروز یک خطا در حلقه انجام شود. یک عبارت loop مشابه زیر است:
count = ۰
loop
 if count> ۱۰۰
 break
 end
   // do something here
 count +=۱
end
ساختار for/in: این ساختار یک مجموعه از عناصر مانند عناصر آرایه، واژه‌نامه، لیست و.. را پیمایش می‌کند. با استفاده از دستور break می‌توان حلقه را در هر نقطه دلخواه پایان داد؛ و با دستور continue به عنصر بعدی در مجموعه رفت.
این ساختار را می‌توان روی رشته کاراکترها هم اعمال کرد که در این صورت روی کاراکترها از ابتدا تا انتها عمل می‌کند. علاوه بر بدنه اصلی بلاک for/in می‌تواند بلاک‌های خاصی با عناوین forfirst, forlast و formiddle داشته باشد که به ترتیب قبل از اولین عنصر، بعد از آخرین عنصر و بعد از هر عنصر به جز عنصر آخر اجرا می‌شوند.
for variable [, variable,...] in collection
 statements...
   [break | continue]
 statements...
 forfirst
 … first time only statements...
 end
 formiddle
 … statements executed between element processing...
 end
 forlast
 … last time only statements...
 end
end

توابع

توابع با کلمه کلیدی function مشخص می‌شوند. در ادامه نام تابع و دو پرانتز باز و بسته که می‌توانند شامل یک لیست از هیچ یا چند پارامتر باشند قرار می‌گیرد. پارامترها به صورت عادی انتقال با مقدار هستند اما می‌توان با عملگر ‘$’ که در ادامه توضیح داده می‌شود، انتقال با آدرس نیز انجام داد. توابع می‌توانند مقداری برگردانند. در واقع توابعی که به صورت صریح مقداری برنمی‌گردانند مقدار nil برای آن‌ها در نظر گرفته می‌شود. درون توابع می‌توان توابع دیگر و خود تابع را صدا زد. قطعه‌ای از از برنامه که درون هیچ تابعی نیست به عنوان «برنامه اصلی» در نظر گرفته می‌شود. توابع به صورت جداگانه از برنامه اصلی نگه‌داری می‌شوند؛ بنابراین می‌توان توابع و برنامه اصلی را در هم آمیخت و قبل از تعریف یک تابع آن را صدا زد. در زیر مثالی از تعریف تابع آورده شده‌است:
function square(x)
 y = x * x
 return y
end
مانند سایر دستورات توابع نیز می‌توانند با استفاده از نشانگر دو نقطه «:» مانند زیر خلاصه شوند:
function square(x): return x * x

دامنه متغیرها

دامنه متغیرها به صورت محلی و سراسری تعریف می‌شود. با تعریف تابع درون تابع می‌توان دامنه‌های درونی‌تری تعریف کرد که دامنه پدرشان را به ارث می‌برند.

متغیرهای محلی و سراسری

هنگامی که یک متغیر برای اولین بار درون یک تابع مشخص می‌شود، آن اسم به صورت محلی در نظر گرفته می‌شود. یعنی حتی در صورت وجود یک متغیر با همان نام در برنامه اصلی، نسخه محلی آن در تابع استفاده می‌شود. با این روش از تغییر یک متغیر که ممکن است در جای دیگری استفاده شود، جلوگیری می‌شود.
می‌توان به متغیرهای سراسری، درون توابع دسترسی داشت اما در حالت معمول نمی‌توان مقدار آن‌ها را تغییر داد. به عنوان مثال در قطعه برنامه زیر مقدار ۱٫۴۱ چاپ می‌شود اما تغییر مقدار sqr در خط بعد تنها توسط خود تابع، که آن را تغییر داده قابل رویت است و مقدار sqr استفاده شده در بیرون تابع هنوز همان ۱٫۴۱ است.
sqr = ۱٫۴۱
function sqr(x)
 printl (" sqr was: ", sqr)
 sqr = x * x
 return sqr
end
number = sqr(8) * sqr
در صورت نیاز به تغییر مقدار یک متغیر سراسری درون یک تابع، بدون دریافت آن به عنوان پارامتر، می‌توان آن را با استفاده از کلمه کلیدی "global" وارد تابع کرد.
زبان فالکن با استفاده از یک ساختار قدرتمند به نام "static initializer" این امکان را می‌دهد که متغیرهایی که درون این ساختار تعریف می‌شوند، تنها یک بار در اولین اجرای تابع مقدار دهی اولیه شوند و از آن پس در هر بار صدا زده شدن تابع، مقدار قبلی آن‌ها استفاده شود.

یک عنصر با بیش از یک نام

هر عنصری در زبان فالکن می‌تواند به صورت ایستا یا پویا، نام مستعار داشته باشد. نام مستعار در حالت ایستا (که به آن ارجاع هم گفته می‌شود) با استفاده از عملگر ‘$’ فراهم می‌شود. هر تغییری در اسم مستعار سبب تغییر د ر اسم اصلی نیز می‌شود. مانند مثال زیر:
 a = 0; b = $a
>b //is 0
a++;>b       //is 1
b++;>a       //is 2
با استفاده از اسم مستعار ایستا (ارجاع) می‌توان به توابع پارامترهای از نوع آدرس انتقال داد که در نتیجه مقدار آن‌ها درون تابع قابل تغییر خواهد کرد.
 function changer(data)
 data++
end
a = ۰
changer(a)   //pass by value
> a //it's still 0
changer($a)  //pass by reference
> a //it's 1
اسم مستعار پویا با استفاده از عملگر ‘#’ حاصل می‌شود. این عملگر می‌تواند بیش از یک بار در یک سطر اعمال شود. مثلاً در تکه برنامه زیر اولین استفاده روی متغیر “var” و دومین اعمال روی متغیرهای “var1”، “var2”، “var3” اعمال می‌شود.
v1 = "Hello"; v2 = ""; v3 = "world"
alias var
for i in [1:4]
 var = v + toString(i)
>##alias
end
printl()

مدل برنامه‌نویسی امری

برنامه‌نویسی امری از طریق تعریف و فراخوانی توابع به صورت کلاسیک، تأمین می‌شود. یک مجموعه از دستورات رویه‌ای مانند if, while, switch،... نیز پشتیبانی می‌شوند.

برنامه‌نویسی تابعی

زبان فالکن یک موتور سنجش به نام sigma-reductor دارد که به برنامه نویسان اجازه می‌دهد برنامه‌هایی با مدل تابعی خالص بنویسند که تفاوتی با آنچه در زبان لیسپ دیده شده‌است، ندارد. دنباله‌های تابعی با زبان آرایه‌ای استاندارد نمایش داده می‌شوند. یعنی این دنباله‌ها می‌توانند به صورت پویا به وسیله خود برنامه در ارزیابی‌های مختلف یا در طول ارزیابی sigma-recuction ایجاد و بررسی شوند یا تغییر کنند. اگر عنصر ابتدای یک آرایه خود قابل فراخوانی باشد، می‌توان آن آرایه را فراخوانی کرد.
شیوه استفاده از مدل‌های برنامه‌نویسی مختلف این اجازه را می‌دهد که از مدل‌های دیگری مانند مدل امری و شئ‌گرا در دنباله‌های تابعی استفاده شود؛ یا بالعکس یعنی در مدل امری از مدل تابعی نیز استفاده شود.
.

شئ‌گرا

فالکن مدل برنامه‌نویسی شئ‌گرا را با در نظر گرفتن کلاسها، وراثت چندگانه، مقداردهی صفات و ساخت نمونه از کلاس‌ها فراهم می‌کند. صورت عمومی تعریف یک کلاس به صورت زیر است:
class class_name[ (param_list) ] [from inh1 [,inh2,... , inhN]]
   [static block]
   [properties declaration]
   [init block]
   [method list]
end
ساختار نمونه‌های کلاس‌ها ثابت و غیرقابل تغییر است. اما با توجه به خصوصیت تابعی بودن فالکن، که به توابع به صورت یک گونه خاص از داده نگاه می‌کند، می‌توان به صفات یک نمونه از کلاس، یک داده ساده یا یک تابع مقداردهی کرد؛ بنابراین با مقداردهی یک صفت با دنباله تابعی، آن صفت یا خصوصیت برای آن نمونه، به صورت یک متد در می‌آید.
نمونه‌های کلاس‌ها می‌توانند در دنباله‌های تابعی ساخته شوند. از آنجا که ساخت یک نمونه از طریق صدا زدن نماد آن کلاس (نام کلاس) صورت می‌گیرد، اجرای یک دنباله تابعی که اولین عنصر آن نماد یک کلاس است، به ساخت یک نمونه از آن کلاس منجر می‌شود.
مدل شئ‌گرای فالکن امکان پیاده‌سازی چند مفهوم برای یک عملگر را می‌دهد. یعنی ایجاد کلاس‌هایی که عملگرهای منطقی و محاسباتی برای آن‌ها مفهوم خاصی دارند.

نمونه‌سازی اولیه بر پایه شئ‌گرایی

مدل برنامه‌نویسی نمونه‌سازی اولیه شئ‌گرا مانند مدل شئ‌گرا است با این تفاوت که از مفهوم کلاس استفاده نمی‌شود و نمونه‌ها متعلق به کلاس خاصی نیستند و ساختار نمونه‌ها به صورت پویا می‌باشد. یعنی ساخت یک نمونه به معنای ایجاد یک کپی از ساختار یک نمونه اولیه‌است اما این عمل به معنای محدود کردن ساختار نمونه جدید به ساختار اولیه، در تمام طول عمر آن نیست.
به عنوان مثال آرایه‌ها می‌توانند نمونه‌ای از استفاده از مدل نمونه‌سازی اولیه باشند. قطعه برنامه زیر نشان می‌دهد که با استفاده از Binding می‌توان برای یک نمونه از آرایه یک تابع خاص را ایجاد کرد. یعنی ساختار آن را به صورت پویا تغییر داد:
vect = ['alph', 'beta', 'gamma']
vect.dump = function()
 for n in [0:self.len()]
>@"$(n): ", slef[n]
 end
end
vect.dump()

پیغام‌گرا

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

جدولی

به برنامه‌نویسی مدل جدولی می‌توان به صورت گسترشی از مدل شئ‌گرا نگاه کرد؛ که در آن هر کلاس با یک جدول که ستون‌های آن خصوصیات و هر ریف یک نمونه از آن است نمایش داده می‌شود. علاوه بر در کنار هم آوردن همه نمونه‌ها، جهت کار با سایر نمونه‌های جدول، مدل جدولی این امکان را فراهم می‌آورد که هر تغییر در جدول اصلی، به صورت پویا در همه نمونه‌های آن جدول منعکس شود.
در واقع هر جدول از تعدادی سطر که آرایه هستند و یک عنوان که معنای هر ستون را مشخص می‌کند، تشکیل شده‌است و داده‌های هر سطر می‌توانند هر عنصر زبان مانند یک جدول دیگر باشند. این آرایه‌ها که در سطرها قرار دارند، دو خصوصیت مهم دارند. اولاً طول آن‌ها ثابت و برابر تعداد ستون‌های جدول است. البته تا زمانی که تغییری در آرایه سبب تغییر طول آن نشود، آن تغییر ممکن است. ثانیاً این آرایه‌ها ستون‌های جدول را به ارث می‌برند. یعنی این آرایه‌ها می‌دانن که قسمتی از یک جدول هستند. این آرایه‌ها می‌توانند از داده‌ها و منطق مشخص جدول استفاده کنند یا از طریق binding منطق خاص خود را داشته باشند. یعنی سطری از جدول که انتخاب شده‌است می‌تواند قابلیت‌های عملیاتی خاص خود را داشته باشد.

حالت استثنا

فالکن مدیریت حالت استثنا را با استفاده از عبارات raise, try و catch انجام می‌دهد. دستور raise می‌تواند هر نوع عنصر زبان مانند عدد، رشته، شئ و… را پرتاب کند. توابع کتابخانه‌ای معمولاً نمونه‌هایی از کلاس Error یا کلاس‌هایی که از آن مشتق شده‌اند را پرتاب می‌کنند.
دستور catch می‌تواند برای گرفتن هر عنصر زبان یا یک تایپ مشخص استفاده شود. کلاس‌هایی که توسط دستورات catch گرفته می‌شوند، معمولاً در یک سلسله مراتب قرار می‌گیرند. در نتیجه امکان اداره عمومی‌تر خطاها را فراهم می‌آورند. بلوک‌های try-catch می‌توانند درون سایر بلوک‌ها یا به صورت تو در تو نیز استفاده شوند. یک بلوک try-catch کامل می‌تواند به شکل زیر باشد (قسمت‌های درون کروشه اختیاری هستند):
try
   [try statements]
[catch [object_type] [in error_variable]]
   [catch statements]
[catch [object_type] [in error_variable]]
   [catch statements]
end

همروندی

همروندی با وجود مفهوم thread فراهم است. برای اشتراک داده‌ها بین آن‌ها نیز از روش‌های مختلف اشتراک استفاده می‌شود.
همچنین مفهوم coroutine که نوعی همروندی سبک‌تر نسبت به thread است، در این زبان پشتیبانی می‌شود.

گسترش صریح رشته

زبان فالکن دارای ‘@’ یک عملگر یگانی جهت گسترش صریح رشته‌های حرفی است. رشته‌های شامل کاراکتر ‘$’ که بعد از آن‌ها یک متغیر آمده‌است، با استفاده از این عمگر گسترش پیدا می‌کند. مانند مثال زیر:

 
a = ۱۲۳۴۵۶٫۷۸۹
v = "formatted as"
s = "a = $a, $v $(a:12rg3,.2), rounded hex as $(a:C)"
printl(@ s)
a = 123456.789, formatted as 123,456.79, rounded hex as 0x1E241
که مقداری که چاپ می‌کند برابر است با:
سه کاراکتر ‘$’ استفاده شده به ترتیب بیانگر مقدار، مقدار که با پهنای ۱۲ راست‌چین شده و در گروه‌های ۳تایی قرار گرفته و با ‘,’ جدا شده و با دقت دو عدد اعشاری گرد شده، مقدار که در مبنای ۱۶ با پسوند ‘0x’ نمایش داده شده هستند که با عملگر ‘@’ گسترش پیدا کرده‌اند.

بارگذاری پویای یک ماژول

فالکن امکان بارگذاری پویای ماژول‌ها را از دو طریق فراهم می‌کند. یکی استفاده از تابع include و دیگری استفاده از کلاس Reflexive Compiler.

پیاده‌سازی

ماشین مجازی مرکزی و تمام ماژول‌های استاندارد با زبان برنامه‌نویسی ++C پیاده‌سازی شده‌اند. اما بعضی از ماژول‌های بسیار سطح پایین و عناصر موتور متنی با استفاده از زبان C و زبان اسمبلی نوشته شده‌اند. در نتیجه در مجموع کارایی بالایی دارد.