תכנות פונקציונאלי ומערכות מידע ארגוניות

סתיו 2011 – המראה המסולסל חוזר לאופנה

(מי שלא הבין את הבדיחה צריך רק להודות על מזלו הטוב)

כמו בעולם האופנה, גם בתוכנה יש טרנדים שבאים והולכים רק כדי לחזור שוב. אחד הטרנדים האלה היום הוא החזרה לעיסוק בתכנות פונקציונאלי. כל גיק מתחיל יודע לדבר על היכולות של שפת erlang במערכות מקביליות, שפת clojure מימשה דיאלקט של lisp על גבי JVM וזוכה להייפ וסקאלה, עוד שפת JVM מעניינת, תומכת בשילוב עקרונות של תכנות מונחה עצמים ותכנות פונקציונאלי.

מעבר לרצון לחדש, יש משהו אמיתי מאחורי התופעה הזו. אחד המאפיינים של תכנות פונקציונאלי הוא שהערך של סמלים הוא קבוע, ולא ניתן לשינוי (לכן גם השתמשתי במילה סמלים ולא משתנים). הזרימה בתוכנית פונקציונאלית מוגדרת על ידי קריאה לפונקציות עם משתני וקלט, ןקבלת פלט חזרה. הפונקציות עצמן תלויות רק בפרמטרים שנשלחו אליהן ולא בשום מצב חיצוני כמו משתנים גלובליים או העברת מצביעים. העובדה הזו יוצרת מצב שהפונקציות הן יחידות חישוב עצמאיות, ולעולם יחזירו את אותה תוצאה בהינתן שהקלט זהה.

המאפיינים האלה גורמים למעשה לתוכנות פונקציונאליות להיות מבוזרות באופן טבעי. העובדה שהפונקציות הן יחידות חישוב עצמאיות שמתקשרות ביניהן על ידי העברת הודעות מאפשרת לתוכנה לרוץ בצורה זהה גם אם אחת הפונקציות פתאום תרוץ על ליבה אחרת של המעבד בלי צורך להוסיף קוד לסינכרון, שכן אף פונקציה לא "מפריעה" לפונקציה אחרת בכך שהיא מעדכנת משאב משותף. באופן דומה ניתן להריץ פונקציה מסוימת גם על מחשב אחר לחלוטין, כל מה שנדרש הוא תשתית להעברת ההודעות בין פונקציה לפונקציה.

אגב, אחד המנגנונים שבעזרתם שפת סקאלה תומכת בתכנות פונקציונאלי לצד תכנות מונחה עצמים היא היכולת לקבוע האם סמל מסוים הוא משתנה variable או ערך קבוע value.

"משבר" החישוב המבוזר

טענה די נפוצה היום היא שהאתגר הבא בעולם התוכנה הוא תמיכה בתכנות מבוזר ומקבילי. הסיבות לכך הן ההתפתחות של מעבדים מרובי ליבות, מגמה שצפויה להתעצם, וכן הצורך להריץ שרתים (או חישובים) על מספר מכונות במקביל ורצוי עם יכולת של הוספת והורדת שרתים באופן דינמי.

שפות כמו ג'אווה נותנות פתרון לתכנות מקבילי על ידי ריבוי חוטים (threads), אבל זה פתרון מוגבל ולא פשוט ליישום במערכות רציניות. המצב אומנם השתפר מגרסה 5 של השפה אבל עדיין שימוש במנגנונים האלה מערב לוגיקה תשתיתית לתוך הלוגיקה האפליקטיבית. זה עומד בניגוד למגמה הכללית של השפה שמחביאה מהמתכנת את השכבות הנמוכות ומאפשרת לו להתמקד בלוגיקה האפליקטיבית בלבד. בנוסף, המודל הזה תקף רק כשמדברים על ביזור על גבי אותה מכונה, או מכונה וירטואלית. ברגע שרוצים לבזר בין שרתים או תהליכים צריך לשנות את המודל.

בתכנות פונקציונאלי כמו שראינו הביזור אינו תוצאה של סנכרון או פתרון אחר בקוד, אלא הוא מאפיין טבעי של השפה, והביזור יכול להתבצע בצורה זהה בין אם מדובר על אותה מכונה או על שתי מכונות שונות (בהינתן שסביבת הריצה מספקת תמיכה בהעברת ההודעות).

"הפרד ומשול" ופונקצית הסיבוך

יתרון נוסף של תכנות פונקציונאלי הוא שהוא מכריח אותנו לחשוב על כל בעיה בתור קומפוזיציה של בעיות קטנות יותר, עד לרמה האטומית. זו פרקטיקה מאוד טובה בתכנות. אחת הסיבות לכך היא שכאשר מפרקים את הבעיות לתתי בעיות עצמאיות, המורכבות של המערכת דומה לסך המורכבויות של תתי הבעיות. לעומת זאת, אם יש תלות והשפעה הדדית בין תתי הבעיות (למשל על ידי כך שכולם משתמשים במשאב משותף כלשהוא) פונקצית המורכבות מתחילה להראות יותר כמו מכפלה של המורכבויות של תתי הבעיות השונות, ולכן המורכבות במערכת כזו צומחת באופן אקספוננציאלי לעומת צמיחה ליניארית בסוג הראשון (שימו לב, העניין הוא לא רק בחלוקה של הלוגיקה ליחידות קטנות יותר, אלא בכך שכל יחידה היא עצמאית עם נתונים משל עצמה). שפות פונקציונאליות מאפשרות להגדיר את אבני הבניין הבסיסיות ולאחר מכן מספקות דרכים רבות ומעניינות לחבר ביניהן.

אז מחליפים שפה?

כדאי מאוד להתנסות בתכנות פונקציונאלי כדי להפנים את המסר, אך אין חובה להשתמש בשפה כזו כדי ליישם את העקרונות האלה בחיים האמיתיים (וברוב המקומות השאלה הזו כלל לא על הפרק). הנה כמה מהדברים שאפשר לעשות בג'אווה למשל:

  1. Limit class mutability: העצה הזו היא אחד הנושאים בספר הקלאסי effective java. אמנם ברמת השפה כל הסמלים בג'אווה הם ברי שינוי, אבל ניתן לעצב את המחלקות בצורה כזו שמונעת שינוי של הערכים שלהם, ומרגע זה הם בטוחים לשימוש במערכת מקבילית. שימוש במנגנון הזה "ידחף" את המערכת לסגנון של יחידות עצמאיות שמתקשרות על ידי העברת הודעות, בין אם בצורה סנכרונית או א-סינכרונית (למשל על ידי שימוש בFuture).
  2. Actor: על רגל אחת, actor הוא תבנית שנותנת אבסטרקציה של תכנות מקבילי ומחליפה את ההתייחסות לרמה הפיזית (חוטים ותהליכים) עם רמה לוגית של "שחקנים" במערכת. משלב זה התשתית דואגת שכל שחקן יטפל באירועים שלו אחד אחרי השני ולכן ניתן לכתוב את הלוגיקה בלי להתחשב בשיקולי מקביליות.
  3. שימוש בפריימוורקים: בפלטפורמת ג'אווה קיימים פרויקטים כגון akka או gpars אשר מממשים את התבנית של actor ונותנים תשתית לפיתוח מערכות מקביליות על בסיס התבנית הזו. כרגיל בקוד פתוח, אני תמיד נפעם מהאיכות ומעושר התכונות של מה שמקבלים, ובוודאי ביחס לעלות

לסיכום, כמו שכתבתי בפוסט הראשון של הבלוג, כדאי להכיר שפות ופרדיגמות שונות ולו בכדי לשאוב מהן את המסר שהן מלמדות חזרה לעולם המוכר שלנו.

ומה הקשר לארכיטקטורה ארגונית?

אני זוכר שבאחד השיעורים בתואר שני דיברנו על מערכות ERP והמרצה הראה לנו שקפים של לפני ואחרי ישום ERP. בשקף של לפני היו מלא צריפים רעועים עם סבך של קשרים ודרכים ביניהם, ואילו בשקף של אחרי הוצגה טירה אלגנטית, מבוצרת ומוקפת חומה ותעלה. מעבר למניפולטיביות של ההצגה הגרפית (אף ארכיטקטורה לא מקדמת כאוס כפרקטיקה מומלצת) יש פה לדעתי בעיה גם במסר הבסיסי – ריכוז המאמץ הוא הגיוני כאשר רוצים לייעל התמודדות עם סדרה של בעיות דומות, אבל כאשר מקבצים יחד בעיות מתחומים שונים הוא עלול להפריע.

ההפרעה הזו מתבטאת בהרבה מישורים. הנדסית אנו יוצרים צימוד שכפי שראינו הופך את הסיבוכיות במערכות מפונקציה ליניארית לפונקציה אספוננציאלית. תפעולית הצימוד יוצר בעיות תאימות וניהול. אני בטוח שכל אחד יכול להיזכר במקרה דומה: מחלקת השיווק מחכה לתיקון דחוף של שגיאת כתיב קטנה ומביכה במיילים שנשלחים ללקוחות. התיקון הזה הוא חלק מגרסה שהייתה אמורה לצאת מזמן, אבל אז התברר שבגלל פיצ'ר קטן אחר צריך לשכתב את מודול הנהלת החשבונות…

הצימוד בעייתי גם ברמה הניהולית. איך אפשר לבטא רצון לקצץ או להשקיע בתחום מסויים כשהכל חלק ממערכת אחת ענקית? האם אלפון השלוחות הפנימיות צריך לקבל אותה רמת אבטחה כמו מערכת הסליקה (או חס וחלילה להיפך)? האם הזמינות של מערכת ההזמנות צריכה להיות זהה לזמינות של המגזין של החברה בפורטל הארגוני?

אינטגרציה באינטרנט היא דבר מאוד פשוט ברמת העיקרון (המימוש יכול להיות קצת טריקי, אבל זה לא לב העניין). אתר חושף ממשק לשירות כלשהוא, ומי שרוצה (ומורשה) יכול להשתמש. לא משנה עם זה REST, SOAP או סתם XML-RPC, הפתיחות הטכנולוגית משאירה לאנשים חופש לבצע אינטגרציה בלי ליצור תלות הדדית בין שירותים מעבר לעמידה בממשק.

כדי להבין כמה העניין הזה מוצלח, בואו נחשוב כדוגמא על עובד בארגון גדול, ששולח מייל לכמה גורמים כדי לקבוע ישיבה להתחלת פרויקט של אינטגרציה בין מערכות שונות בארגון לצורך מסוים. העובד הזה יכול לחזור הביתה, לפתח באופן פרטי אפליקציה לפייסבוק או טוויטר (שירותי ענק בהם הוא לא נפגש ולו עם אדם אחד), לממשק אותה בצורה עמוקה לשירות שלהם ולהעלות אותה לרשת עוד לפני שיקבל תשובות על המייל ששלח שמסבירות למה לא' לא נוח במועד שהוא מציע, שב' בפגישה אחרת, ג' חושב שאי אפשר להתחיל בלי להזמין גם את ד' וכו'.

הדוגמא הזאת באה להמחיש שארכיטקטורה שבנויה על שירותים מאפשרת אינטגרציות קלות ומהירות והסיבה שגורמת לכך היא שכאשר כל שירות מפותח באופן עצמאי וחושף ממשק, זה מעלה באופן דרמטי את רמת הביטחון ביכולת לשלב שירותים, שכן קשה למודול מסוים לפגוע בשירות שהוא משתמש בו או להשפיע על התנהגותו. בצורה הטהורה, שירות מסוים אפילו לא צריך לדעת מי משתמש בו ולא אכפת לו מכך.

ההבנה הזו היא לדעתי הבסיס שבגללה SOA זו מילה כל כך חשובה היום. SOA על רגל אחת היא התייחסות לכלל המערכת הארגונית בתור אוסף של שירותים עצמאיים. התייחסות כזו מאפשרת לפתח אותם בנפרד, לנהל אותם לפי מאפיינים שונים על פלטפורמות מתאימות, ולפרק את המורכבות בארגון לתתי בעיות קטנות יותר. זה יוצר גם מערכת יותר גמישה שמאפשרת מימוש של אינטגרציות חדשות ביתר קלות.

זה בדיוק המימוש ברמת המאקרו של העקרונות שדיברתי עליהם ברמת המיקרו כשעסקתי בתכנות פונקציונאלי: יחידות מודולריות ועצמאיות עם ממשק ביניהן מפשטות את הבעיה, מאפשרות גמישות והרכבת יחידות גדולות ממספר יחידות קטנות יותר ומתמודדות בצורה שקופה עם בעיות של ניידות בין סביבות שונות המתאימות להן.

במהלך כתיבת הפוסט גיליתי בלוג טכנולוגי נוסף ומעניין בעברית. מומלץ לקרוא את הבלוג, ובייחוד את הפוסט שעוסק בSOA להשלמת התמונה.

כתיבת תגובה

הזינו את פרטיכם בטופס, או לחצו על אחד מהאייקונים כדי להשתמש בחשבון קיים:

הלוגו של WordPress.com

אתה מגיב באמצעות חשבון WordPress.com שלך. לצאת מהמערכת / לשנות )

תמונת Twitter

אתה מגיב באמצעות חשבון Twitter שלך. לצאת מהמערכת / לשנות )

תמונת Facebook

אתה מגיב באמצעות חשבון Facebook שלך. לצאת מהמערכת / לשנות )

תמונת גוגל פלוס

אתה מגיב באמצעות חשבון Google+ שלך. לצאת מהמערכת / לשנות )

מתחבר ל-%s