پرش به محتوای اصلی
پرش به محتوای مقاله

«تعامل مستقیم مقادیر»؛ راهکار Glojure برای پیوند دو زبان

·۲۸ خرداد ۱۴۰۵۶ دقیقه مطالعه
مفسر کلوژر مبتنی بر گو با پشتیبانی از هم‌کنش‌پذیری توسعه‌پذیر
مفسر کلوژر مبتنی بر گو با پشتیبانی از هم‌کنش‌پذیری توسعه‌پذیر
اشتراک‌گذاری
واقعاً چه چیز جدید است؟

جایگزینی شبیه‌سازی‌های سطحی با یک سیستم «میزبانی» (Hosted) که مقادیر Clojure را مستقیماً به تایپ‌های Go نگاشت می‌کند تا تعامل دو زبان بدون لایه‌های اضافی صورت گیرد.

اگر برنامه‌نویس Go هستید و می‌خواهید بدون ترک این اکوسیستم، از انعطاف‌پذیری زبان‌های Lisp بهره ببرید، Glojure پلی مستقیم برای شماست. این ابزار که به‌صورت متن‌باز در گیت‌هاب منتشر شده، به شما اجازه می‌دهد یک مفسر Clojure را مستقیماً در فایل‌های باینری Go جای‌گذاری کنید یا از آن به عنوان یک ابزار خط فرمان مستقل استفاده کنید.

مفسر کلوژر مبتنی بر گو با پشتیبانی از هم‌کنش‌پذیری توسعه‌پذیر

سال‌ها بود که Clojure با ماشین مجازی جاوا (JVM) گره خورده بود تا از کتابخانه‌های عظیم آن استفاده کند. Glojure تلاش می‌کند همین رابطهٔ «میزبانی» را بازسازی کند، اما جای جاوا را با Go عوض کرده است. در زمینه Clojure روی JVM، «میزبان بودن» به این معناست که تمام مقادیر جاوا، مقادیر Clojure هستند و بالعکس. Glojure تلاش می‌کند دقیقاً همین رابطه را با Go حفظ کند؛ به این معنا که تمام مقادیر Go می‌توانند به عنوان مقادیر Glojure استفاده شوند و برعکس.

این یعنی به جای هدف قرار دادن JVM، کد Clojure شما روی یک محیط زمان اجرای مبتنی بر Go اجرا می‌شود. این امر باعث می‌شود کتابخانه‌های Go به همان اندازه برای Clojure در دسترس باشند که چارچوب‌های جاوا برای Clojure استاندارد در دسترس هستند. طبق مستندات پروژه، این سیستم در مراحل اولیه توسعه است. کاربران باید منتظر باگ‌ها، نبود برخی ویژگی‌ها و عملکرد محدود باشند، زیرا سازگاری با نسخه‌های قدیمی تا انتشار نسخه ۱ تضمین نشده است. با این حال، این ابزار در حال حاضر بخش بزرگی از کتابخانه هسته Clojure (در حالت تغییریافته) را اجرا می‌کند و در چندین پروژه تفننی با موفقیت به کار گرفته شده است.

پیاده‌سازی فنی و نصب

برای شروع، شما به نسخه ۱.۱۹ یا بالاتر Go نیاز دارید، هرچند پروژه به‌طور مشخص برای نصب، Go ۱.۲۴ را می‌طلبد. می‌توانید ابزار را از طریق خط فرمان نصب کنید:

$ go install github.com/glojurelang/glojure/cmd/glj@latest

پس از نصب، دستور glj یک تجربه کامل REPL (Read-Eval-Print-Loop) را فراهم می‌کند. شما می‌توانید نشست را با اجرای ساده $ glj آغاز کنید. برای مثال، وارد کردن (println "Hello, world!") رشته را چاپ کرده و مقدار nil را برمی‌گرداند.

این نشست تعاملی شامل چندین ویژگی توسعه حرفه‌ای است:

  • حالت‌های ویرایش: پشتیبانی از هر دو حالت Vi (پیش‌فرض) و Emacs؛ پیکربندی از طریق فایل ~/.inputrc انجام می‌شود.
  • ورودی هوشمند: ویرایش چندخطی که در آن عبارات ناقص در خط بعدی با تورفتگی خودکار ادامه می‌یابند. تکمیل خودکار (Tab completion) برای نمادها، فضای نام‌ها و نام‌های مستعار با برچسب‌های توصیفی در دسترس است.
  • مدیریت نشست: تاریخچه پایدار که در فایل ~/.glj_history در طول نشست‌ها ذخیره می‌شود و قابلیت Bracketed Paste برای درج فوری بلوک‌های کد.
  • کنترل: کنترل شغلی استاندارد از طریق Ctrl+Z برای تعلیق و fg برای بازگشت. وقفه‌ها از طریق Ctrl+C برای لغو ورودی یا متوقف کردن ارزیابی مدیریت می‌شوند.

استفاده از خط فرمان و اسکریپت‌نویسی

ابزار glj فراتر از یک REPL تعاملی، تطبیق‌پذیر است. شما می‌توانید نسخه را با دستور $ glj --version بررسی کنید که در حال حاضر را به عنوان glojure v0.3.0 شناسایی می‌کند. در داخل REPL، نسخه از طریق نماد *glojure-version* قابل بررسی است که یک نقشه (Map) برمی‌گرداند: {:major 0, :minor 3, :incremental 0, :qualifier nil}.

برنامه‌نویسان می‌توانند عبارات را مستقیماً از شل با استفاده از پرچم -e ارزیابی کنند. برای مثال:

  • $ glj -e '(println "Hello, World!")'
  • $ glj -e '(apply + (range 3 10))' (نتیجه ۴۲ را برمی‌گرداند)
  • $ glj -e ' (defn factorial [n] (if (<= n 1) 1 (* n (factorial (dec n))))) (factorial 5)' (نتیجه ۱۲۰ را برمی‌گرداند)

علاوه بر این، شما می‌توانید اسکریپت‌های Clojure را اجرا کنید. فایلی به نام hello.glj حاوی کد (println "Hello," (first *command-line-args*)) را می‌توان با دستور $ glj hello.glj World اجرا کرد که منجر به چاپ "Hello, World" می‌شود. شما حتی می‌توانید برنامه‌های اجرایی بسازید، مانند یک سرور که از net:http.ListenAndServe ":8080" nil برای مدیریت درخواست‌ها از طریق یک echo-handler تعریف شده استفاده می‌کند.

جای‌گذاری و تعامل دوطرفه

یکی از قدرتمندترین جنبه‌های Glojure، قابلیت جای‌گذاری (Embedding) در برنامه‌های Go با استفاده از بسته github.com/glojurelang/glojure/pkg/runtime است. این قابلیت برای افزودن پیکربندی‌های اسکریپتی، اجازه دادن به کاربران برای گسترش برنامه‌ها با پلاگین‌های Clojure، ترکیب عملکرد Go با بیان‌پذیری Clojure، یا کنترل محیط اجرا از طریق Sandbox و ورودی/خروجی سفارشی مفید است.

تعامل در این سیستم دوطرفه است. شما می‌توانید یک تابع Go تعریف کنید و آن را با استفاده از glj.Var در دسترس زمان اجرای Clojure قرار دهید. برای مثال، یک تابع Go مانند greet(name string) می‌تواند از طریق greetVar.SetRoot(greet) به یک متغیر Clojure به نام greet-from-go نگاشت شود. برعکس، شما می‌توانید یک تابع Clojure مانند (defn add [x y] (+ x y)) تعریف کنید و آن را از داخل Go با استفاده از متد .Invoke(10, 32) فراخوانی کنید که مقدار ۴۲ را برمی‌گرداند.

ادغام با بسته‌های Go

Glojure دارای تعامل داخلی برای بسیاری از بسته‌های کتابخانه استاندارد است. برای جلوگیری از تداخل نام با استفاده Clojure از اسلش (/) برای فضای نام‌ها، نام بسته‌های Go «تغییر شکل» (munged) داده می‌شوند؛ به این صورت که موارد / در نام بسته‌ها با دو‌نقطه (:) جایگزین می‌شوند.

بسته‌های پشتیبانی‌شده پیش‌فرض عبارت‌اند از:

  • ابزارهای هسته: fmt, io, os, errors, flag, reflect, strconv, unicode.
  • سیستم فایل و شبکه: io/fs, io/ioutil, net/http.
  • ریاضیات و منطق: math, math/big, math/rand, regexp, sort.
  • هم‌زمانی و زمان: sync, sync/atomic, time, context, os/exec, os/signal.

برای بسته‌های سفارشی، توسعه‌دهندگان می‌توانند از ابزار gen-import-interop استفاده کنند. این ابزار یک فایل .go تولید می‌کند که تابعی را برای افزودن بسته به نقشه بسته‌های Glojure صادر می‌کند. دستور طبق این الگو است:
$ go run github.com/glojurelang/glojure/cmd/gen-import-interop -packages=:comma-separated-package-list: > your/package/gljimports/my_package_map.go

مقایسه با سایر نسخه‌های Go

برخلاف دیگر پیاده‌سازی‌های Clojure مبتنی بر Go، Glojure خود را از طریق ماهیت «میزبانی» متمایز می‌کند. جدول زیر آن را با Joker و let-go مقایسه می‌کند:

ویژگی Glojure Joker let-go
میزبان (Hosted) بله خیر خیر
تعامل گسترده با Go بله خیر خیر
هم‌زمانی بله بله (با GIL) بله
ابزارهای Clojure خیر بله خیر
نحوه اجرا مفسر Tree-walk مفسر Tree-walk مفسر Bytecode

در حالی که سایر پورت‌ها ممکن است به عنوان کامپایلرهای مستقل عمل کنند، Glojure تضمین می‌کند که تمام مقادیر Go اساساً مقادیر Clojure باشند. این ابزار از یک مفسر Tree-walk استفاده می‌کند و در این مرحله، انعطاف‌پذیری مدل میزبان را بر سرعت اجرای خام ترجیح داده است.

نگاشت انواع داده

برای حفظ سازگاری، Glojure انواع داده Clojure را به صورت زیر به انواع Go نگاشت می‌کند:

  • Long: int64
  • Double: float64
  • Float: float32
  • Byte: byte (توجه: بایت‌های Go بدون علامت هستند، برخلاف بایت‌های علامت‌دار JVM)
  • Int: int (توجه: Intهای JVM ۳۲ بیتی هستند، در حالی که Intهای Go بسته به پلتفرم ۳۲ یا ۶۴ بیتی‌اند)
  • Char: نوع rune علامت‌گذاری شده type Char rune (۳۲ بیتی در Go در مقابل ۱۶ بیتی در JVM)
  • BigInt: پوشش داده شده توسط *big.Int
  • BigDecimal: پوشش داده شده توسط *big.Float
  • Ratio: پوشش داده شده توسط *big.Rat

این نگاشت تضمین می‌کند که محاسبات با دقت بالا و اعداد بزرگ در مرز بین دو زبان کاربردی باقی بمانند.

این پروژه این فرض را که Clojure حتماً باید به یک ماشین مجازی سنگین وابسته باشد، تغییر می‌دهد. با آوردن انعطاف‌پذیری Lisp به هم‌زمانی بهینه و توزیع باینری Go، توسعه‌دهندگان می‌توانند سیستم‌های بسیار پویا بسازند که همچنان به یک فایل اجرایی واحد و قابل حمل تبدیل می‌شوند. در واقع، Go به میزبان یک موتور اسکریپتینگ قدرتمند تبدیل شده است.

اگر می‌خواهید با این گردش کار آزمایش کنید، سعی کنید یک سرور کوچک Go بسازید و از Glojure برای مدیریت پویا منطق مسیریابی (Routing) بدون نیاز به ری‌استارت کردن پروسه استفاده کنید.

گام بعدی شما

  • اگر از Go استفاده می‌کنید، سعی کنید یک سرور کوچک بسازید و از Glojure برای مدیریت پویا و لحظه‌ای مسیرهای (Routing) سرور بدون نیاز به ری‌استارت استفاده کنید.
  • مستندات gen-import-interop را برای متصل کردن کتابخانه‌های شخصی خود به محیط Clojure بررسی کنید.
  • عملکرد مفسر Tree-walk را در مقایسه با اجرای مستقیم Go در پروژه‌های کوچک تست کنید تا نقاط گلوگاه را بشناسید.

اما تأثیر این رویکرد بر مدیریت حافظه در مقیاس بالا موضوع دیگری است؛ برای درک بهتر مدیریت حافظه در زبان‌های مدرن، تحلیل ما درباره‌ی Go و Rust را بخوانید.

چرا این موضوع مهم است؟

این ابزار با تکیه بر اعتبار معماری Go و انعطاف Clojure، سرعت توسعه در سیستم‌های پیچیده را بالا می‌برد. توسعه‌دهندگان اکنون می‌توانند بدون ریسکِ کامپایل مجدد، منطق کسب‌وکار را به‌صورت دینامیک تغییر دهند.

تأثیر برای ایران

برنامه‌نویسان Go در ایران که در توسعه سیستم‌های توزیع‌شده یا ابزارهای زیرساختی فعالیت می‌کنند، می‌توانند از این ابزار برای پیاده‌سازی پلاگین‌های دینامیک بدون نیاز به تغییر هسته‌ی برنامه استفاده کنند.

·نگاه ما
تحریریه دات‌هوش

تلاش برای bringing Lisp to Go نشان‌دهنده بازگشت به پارادایم «زبان‌های میزبان» برای حل مشکل صلب بودن زبان‌های کامپایله است. Glojure به جای شبیه‌سازی، روی ادغام عمیق تایپ‌ها تمرکز کرده که این یعنی کاهش چشمگیر سربار (overhead) در تعاملات بین‌زبانی، چیزی که معمولاً نقطه ضعف پل‌های ارتباطی بین زبان‌های دینامیک و استاتیک است.

منابع

این گزارش با خط‌لولهٔ خودکار دات‌هوش از منابع معتبر جهانی تدوین و زیر نظر تحریریه منتشر شده است. روش کار ما

گفتگو

پنج‌شنبه‌های هوش‌محور

بسته‌ی هفتگی دات‌هوش

۵ خبر، ۲ ابزار، ۱ پرامپت در هر شماره. به‌زودی راه‌اندازی می‌شود — هر پنج‌شنبه صبح.

خبر کلیدی
ابزار کاربردی
پرامپت حرفه‌ای
تحلیل پژوهش
به‌زودی
زاویه‌ی ایرانی
به‌زودی
تمرین این هفته
به‌زودی

راهنماهای دات‌هوش

راهنماهای کاربردیِ دات‌هوش برای کار با هوش مصنوعی — از همین‌جا شروع کنید:

دات‌هوش

راهنمای فارسی هوش مصنوعی — با نگاه به ایران

اخبار روزانه، معرفی ابزارها و مدل‌ها، و آموزشِ کار با هوش مصنوعی؛ همیشه با این پرسش که از ایران چه چیزی کار می‌کند و چه چیزی نه.