פרדיגמת התכנות פונקציונלי
פרדיגמת התכנות הפונקציונלי (Functional Programming, בקיצור: FP) קדמה דווקא לפרדיגמה מונחית-העצמים, אך מכיוון שהפופולריות שלה נסקה דווקא בעשור האחרון - רבים נוטים לחשוב שהיא חדשה.
פרדיגמת ה FP, בצורות כאלו ואחרות קיימת משנת 1957. בשנות ה-60, בשל כך שצרכה יותר זיכרון ומשאבים (שהיו אז מאוד מוגבלים) - היא הייתה בגדר רעיון מעניין, אך לא מעשי. בשנות ה-80 היא כמעט הפכה למיינסטרים - אך שפות כמו Basic ו C, שהיו מבוססות על תכנות פרוצדורלי, הפכו פופולאריות וקצת השכיחו אותה.
מה שהחזיר אותה לזירה לפני עשור בערך, היא הדאגה מריבוי ה cores במעבדים. רבים חזו שיידרש שינוי פרדיגמות משמעותי בעולם התוכנה - הרי עשרות שנים היו רגילים לחשוב בעיקר על Execution Thread יחיד, ופתאום אנו עוברים לעולם מרובה-cores (תכנות HPC, ו UI - כן חשבו על כמה threads, כבר לאורך שנים רבות).
אני זוכר את הדיבורים, לפני עשור או עשור וחצי, על כך שאוטוטו - לא נוכל לכתוב קוד יותר כפי שכתבתנו. שכל מתכנת יצטרך להתמחות ב Fork-Join או Patterns דומים של מקביליות, אחרת הקוד לא יהיה יעיל. אחד התוצרים של הדאגה הזו הייתה להחזיר את התכנות הפונקציונלי לשיח בתעשייה: כאשר עובדים עם מבני-נתונים שהם Immutable - קל הרבה יותר לכתוב קוד שירוץ בצורה מקבילית.
עשור עבר, מספר ה cores במעבדים אכן גדל, (אם כי בקצב מתון מהמדובר) - ובעצם עבור רוב המתכנתים מעט מאוד השתנה. ישנם מעבדים עם 20+ ליבות, אך הם משרתים בעיקר בצד השרת (ולא כ Desktop App) - ושם ריבוי cores משמש לשירות הרבה בקשות במקביל - כל אחד על thread. זה המצב ברוב הגדול של המקרים.
פרדיגמת ה FP אמנם חזרה לדיון, אבל תרומתה העיקרית היא דווקא במלחמה בסיבוכיות - ובכתיבת קוד פושט יותר.
לפעמים אנו נוטים להסתכל על FP ו OO כבלעדיים (mutual exclusive) - כאילו עלינו לבחור רק בפרדיגמה אחת מהשתיים. יתרה מכך: לפעמים אפילו מתחיל דיון ״מי יותר טוב?״ דיונים שהרבה פעמים כוללים אי-דיוקים משמעותיים, לכל הפחות. זו גישה נפסדת ומטופשת: יש לנו כאן שני רעיונות טובים שמשתלבים היטב - ולנסות להפריד אותם, ולהוקיע אחד מהם - היא דרך טובה להיות פחות מקצועיים ויותר ילדותיים. די כבר עם ״תכנות פונקציונלי יותר חכם ומתקדם מ OO״ או ״תכנות FP הוא אקדמי ולא מעשי״. זה מטופש!
ניתן לראות ששפות OO פופולריות רבות - הולכות ומאמצות רעיונות FP. אפשר לציין את סקאלה, קוטלין, או ג׳אווה 8 - שם רעיונות ה FP זוכים לתהודה ותשומת-לב, אבל שימו לב שרעיונות דומים אומצו גם בשפות כמו רובי, פייטון, וג׳אווהסקריפט כבר לפני עשור או שניים - והשתלבו היטב עם רעיונות OO.
גם בשפות ״פונקציונליות טהורות״ (כלומר: שפות בהן ניתן דגש רב על רעיונות של FP) - יש אלמנטים של OO. למשל, ב Haskell יש הכמסה ברמת המודול (מודול לרוב הוא בגודל של קובץ בודד, והוא מקביל במידה מסוימת למחלקה בשפות OO) - וזו תוספת חשובה מאוד לשפה. ב Haskell יש גם ריבוי-צורות (Polymorphism), מן הסתם ברמת הפונקציה - שזה המבנה העיקרי בשפה.
הכמסה + ריבוי-צורות, ובלי הורשה (השנויה במחלוקת) - אפשר לרגע להתבלבל ולחשוב ש Haskell היא בכלל שפת OO מודרנית?
בקיצור: אין סיבה שלא נאמץ גם ספרות וגם שירה - הם לא מתחרים זה בזה, ולא צריך באמת לבחור. כנ"ל לגבי OO ו FP.
אז מה שפות FP חידשו לנו?
Functional Style
לפעמים אוהבים להתייחס ל FP כענף של התכנות הדקלרטיבי. האם זה נכון?
ובכן, קשה להשוות FP לשפות דקלרטיביות כמו SQL. ב SQL אנו מתארים כוונה עם מעט מאוד הַכוונה - ו"מישהו" דואג שהיא תתבצע. בשפות פונקציונלית מתארים כוונה עם הרבה מאוד הַכוונה - וברור מאוד כיצד הדברים עומדים להתבצע. אני חושב שיש אכן טעם לעשות הפרדה בין Imperative Style לבין Declarative Style לבין Functional Style - שהוא משהו באמצע.
אפשר לומר שמקום לומר למעבד מה לעשות צעד אחרי צעד (אימפרטיבי) - ב FP אנחנו אומרים לו מה לעשות שלב אחרי שלב 😀
ב Functional Style מחליפים (בגדול) משפטי if במשפטי filter, לולאות במשפטי map או רקורסיות, ובמקום להשתמש במשתנה result שיצבור את התשובה של הפונקציה - משתמשים ב fold.
הרבה פעמים קוד שנכתב ב Functional Style הוא פשוט וקריא יותר - מהמקבילה האימפרטיבית (ולכן כ"כ הרבה שפות אמצו את הסגנון הזה, כסגנון חלופי). בפעמים אחרות, דווקא הסגנון האימפרטיבי יהיה קצר / פשוט / מובן יותר - ולכן רק מעט שפות אמצו את ה Functional Style - כסגנון יחיד.
כשתתחילו לעבוד עם Functional Style - ייתכן ותראו שרוב הפונקציות קריאות ופשוטות יותר בסגנון הזה, אבל חבל ״לשבור את השיניים״ ולהסתבך בכל מחיר. הרשו לעצמכם עדיין להשתמש ב Imperative Style כאשר הקוד פשוט יותר.
לפעמים עולה אמירה ש״סגנון פונקציונלי הוא למתכנתים מנוסים יותר/בלבד״ - זו לרוב אינדיקציה שהשתמשנו ב Functional Style בצורה מתוחכמת ולא פשוטה, וזה בדיוק הזמן לפשט בעזרת סגנון אימפרטיבי (או כל אופן אחר שהקוד יהיה פשוט וקל יהיה להבין אותו). מתכנתים מנוסים וחכמים כותבים קוד פשוט - לא קוד מורכב.
שרבטתי דוגמה לפונקציה, שכנראה תהיה פשוטה יותר לכתיבה ב Imperative Style.
אפשר (כמובן) לכתוב אותה גם ב Functional Style - אבל בסיכוי גדול הקוד יהיה מסובך (״מתוחכם״) וקשה יותר להבנה:
הוספתי הערות מטא, שמציגות אלו אלמנטים בפונקציה יהיה מורכב יותר לבטא ב Functional Style. אני מקווה שההערות לא מקשות מדי על קריאת הקוד... |
Immutability
כשתוכנה הופכת למורכבת, אנו רוצים לחלק אותה לחלקים קטנים יותר, עם מינימום קשרים ביניהם. דרך אחת לצמצם קשרים היא הכמסה, דרך נוספת - היא Immutability. כאשר אין אפשרות לשנות מבני-נתונים - יש הרבה פחות עניין לייצר אליהם קשרים.
Immutability משמע שהערכים ומבני הנתונים שקבענו לא ישתנו מרגע שנוצרו. מנקודת מבט של שפות אימפרטיביות, זה אומר שכל הערכים / השדות בשפה הם final, const, או איך שזה נקרא בשפה הספציפית.
ברור שתוכנה צריכה לבצע שינויים, אך כל שינוי מתבטא ביצירת עותק חדש של מבני-הנתונים ותוך כדי ההעתקה - מבצעים שינויים. מכיוון שהפעולה הזו נפוצה - מוסיפים לעתים כלי העתקה נוח ברמת השפה.
שפות פונקציונליות מספקות לרוב סט של מבני-נתונים שהם Immutable, והגדרת משתנים (ערכים / שדות שיכולים להשתנות) - הם היוצאים מהכלל.
לוקח קצת להתרגל ולעבוד בטבעיות עם קוד שהוא Immutable, זה שינוי בתפיסה - אבל הוא בהחלט אפשרי ויכול להפוך לטבעי. היתרונות הם בצמצום התלויות בקוד, ובעיקר בשילוב עם פונקציות טהורות - שנדבר עליהן מיד.
Immutability גורר שכל שינוי state כולל העתקה - ויש לכך מחיר בבצועים, ולפעמים מחיר ב boilerplate code - תלוי בשפה, אך בעיקר כאשר עושים שינויים עמוקים במבני-נתונים מורכבים ומקוננים.
משחק מחשב שהוא Realtime, היכן שהרבה state משתנה כל הזמן ויש עניין של ביצועים ותגובתיות - הוא לא מקום טוב להחיל בו Immutability.
.
עוד וריאציה חשובה של Immutability היא הדפוס של Event Sourcing - שמירת רשימת השינויים באובייקט, מבלי לשנות את האובייקט עצמו, והרכבה מחדש שלהם בכל פעם שצריך לגשת לאובייקט. כיסיתי את הדפוס הזה בפוסט עבר.
No Side Effects
רעיון מרכזי ב FP הוא רעיון ה "Pure Functions", פונקציות טהורות - עם אפס השפעה על הסביבה.
הרעיון אומר כך: כאשר אני מפעיל פונקציה, בתכנות בכלל, כל מה שידוע לי על הפונקציה הוא השם שלה והחתימה שלה (פרמטרים / ערך החזרה). נכון, אני יכול להיכנס ולקרוא את הקוד - אבל אין לי זמן לקורא רקרוסיבית את כל הקוד שירוץ בעקבות הפעלה של כל פונקציה. יש כאן עניין של אמון / תיאום ציפיות: שם הפונקציה והחתימה נותנים לי מושג מה הפונקציה תעשה - ואני מצפה שהאמון הזה לא יישבר.
הרבה פעמים, האמון הזה נשבר. אם הפונקציה לא בנתה את הציפיות הנכונות ואני מגלה שדברים הם אחרת - זו הפתעה, ולרוב לא טובה. אם הפונקציה הפתיעה בכך שהחזירה רשימה ממוינת בלי שזה צוין - אין פה ממש נזק, כי לא בניתי ציפיה לסדר. אם הפונקציה עשתה משהו משמעותי שלא ציפיתי, שינתה state חשוב במערכת - זה עלול להיות סוג של Ohh fuck moment - וזו בעיה. הפתעות מהקוד הן מקור ברור לבאגים, הרי באג = המחשב עושה מה שנאמר לו, ולא מה שהמתכנת התכוון.
עד כאן - תכנות / הנדסת תוכנה. הצורך בפונקציות צפויות הוא צורך ליבה, ואנשי-תוכנה מנוסים מקפידים מאוד לברור את שמות הפונקציות - כדי לתאם ציפיות בצורה הטובה ביותר האפשרית (POLA). למשל: תחילית try לשם פונקציה שעשויה לא לבצע את הפעולה.
חלק משמעותי מההפתעות הגדולות שקורות בהפעלת פונקציה נובעות משינוי state של אובייקט אחר (קריאה לגיטימית לכאורה, לממשק הציבורי של האובייקט), שליחת מייל או הפעלת תהליך אחר לא צפוי. אלו בעצם side effects, ו side effects משמעותיים.
בפרדיגמת ה OO כלי משמעותי למניעת הפתעות ממש מאותו הסוג הוא Encapsulation: את ה state הפנימי של האובייקט ניתן לשנות רק במסלולים ״המקובלים״, מה שמצמצם משמעותית הפתעות. זה חשוב מאוד - אבל לא תמיד מספיק. הפתרון של FP הוא לחתור לכך שכמעט כל הפונקציות במערכת יהיו pure functions - פונקציות שבשום אופן לא משנות משהו מחוץ ל scope הפנימי שלהן, ולא מסתמכות על שום דבר מעבר לארגומנטים שנשלחו להן - וכך אנו מגבירים מאוד את הסיכוי שהן יהיו צפויות. הפעלה של Pure Function, מספר פעמים ובזמנים שונים - תמיד תציג אותה תוצאה, כאשר נשלחים לה אותם הארגומנטים.
איך בכלל אפשר ש"רוב הפונקציות במערכת לא ישנו state כלשהו?" הפתרון הוא ב Immutability: הפונקציה מקבלת מבנה-נתונים, אך לא מבצעת עליו שינוי. היא יוצרת עותק שלו - ומחזירה עותק שיש עליו שינוי.
- הפונקציה התבססה רק על הקלט שקיבלה (מבנה-נתונים) - וכך ברור לנו על מה בדיוק היא מתבססת.
- ההשפעה היחידה שלה על כלל המערכת - הוא החזרה של מבנה נתונים אחר (עותק של הראשון, בד"כ). Scope השינוי - גם הוא ברור.
האם זה לא Functional Decomposition? אותו דפוס שלילי שדיברנו עליו בפוסט הקודם?
ישנם הבדלים: Functional Decomposition קלאסי פעל על State גלובלאי בו ביצעו כל הזמן שינויים - וזה מאוד לא צפוי.
כן יש פה עניין של חלוקת אחריות מפוזרת ולא ברורה בפוטנציה, ולכן תראו הרבה פעמים שמערכות המושפעות מ FP עדיין משתמשות בחלוקה למודולים (או מחלקות, בשפות OO+FP) והקפדה על סידור הגיוני של המערכת.
אם אני לא יכול למצוא בקלות קוד שעושה משהו, סביר שאכתוב אותו מחדש => כפילות קוד.
בקיצור: בהחלט יש מה להיזהר כאשר מאמצים למערכת "ארכיטקטורה מבוססת pure functions ומבני-נתנים", ולנסות לא להיגרר ל Functional Decomposition. את זה עושים בעיקר ע״י ארגון של הקוד ע״פ נושאים ותחומי אחריות - היכן שעקרונות ה OO יכולים בהחלט לעזור.
יש עוד כמה יתרונות חשובים לפונקציות טהורות:
- מאוד טבעי לכתוב להן בדיקות יחידה. כלל חשוב שהתווסף ב TDD עם השנים הוא לחלץ ״לוגיקה עסקית טהורה״ לפונקציות / מחלקות נפרדות - כדי לכתוב בדיקות-יחידה סופר-יעילות, וללא Mocks. זה בדיוק מה שקורה ב Pure Functions.
- פשטות במקביליות. כאשר כותבים קוד מקבילי, state משותף הוא נקודת כאב מרכזית: אנחנו צריכים לנהל אותו, לנעול אותו, וגם להימנע מהשלכות שליליות של locks כגון deadlocks ופגיעה במקביליות - וזה לא קל. דווקא קל מאוד למקבל פונקציות טהורות - אם כי לא תמיד זה מוביל לביצועים הטובים ביותר (בשל ההעתקות הרבות הנדרשות, ובשל אי-ניצול של memory locality).
- Memoization - אם פונקציות הן טהורות, אזי ניתן בקלות ובבטחה לעשות caching לתוצאות החישוב. אם פונקציה היא טהורה, ומבני-הנתונים הם Immutable אז ניתן להחליף את הקריאה לפונקציה בתוצאת החישוב המוכנה מראש - מבלי לשנות בכלל את התוכנה. העיקרון הזה נקרא גם referential transparency. כמובן ש caching הוא לא תמיד יעיל, למשל - אם יש פרמוטציות אפשריות רבות להפעלת הפונקציה.
כמה שאלות ותשובות:
- האם באמת אפשר לכתוב מערכת רק מפונקציות "טהורות"? לא. כל מערכת חייבת Input/Output בכדי שתהיה לה משמעות. כלומר: יש את הבעיה הקטנה שמה שלקוחות המערכת צריכים ממנה - הם side effects. אז אי אפשר בלי side effects.
- ״במערכות על טהרת ה FP״ מנסים למקסם את אחוז הפונקציות הטהורות במערכת. כאשר מדובר ב batch processing (סוג בעיה שהרבה פעמים מפתחים עם FP) - אז פשוט אפשר להעביר את ה state בין הפונקציות מההתחלה עד הסוף - הוא לרוב מספיק קטן בכדי לא לאבד עליו שליטה / ששכפול שלו יהיה Overhead גדול מדי.
- גישה אחת, היא ליצור Queues שיקבלו הוראות על שינוי state (למשל: שינוי בבסיס הנתונים) ואז באמת יהיו כמה פונקציות, שמאוד ברור מי הן - שרק מבצעות "side effects". שולפות הודעות מה Queue - ומבצעות את השינויים.
- Actors הוא מודל מקביליות שתומך בגישה הזו.
- גישה אחרת, היא לאגור את ה state המשתנה במעין ״טרנזקציה״: הפונקציות יתרמו לשינוי state, אבל הוא לא יחול על לנקודה מאוד ברורה בקוד ובזמן. הגישה הזו עוזרת להתמודד עם עניינים כמו מקביליות / racing conditions של עדכון ה state או state מורכב שקשה לעדכן ע״י הודעות.
- שווה לציין שלא רבות המערכות שקמות ״על טהרת ה FP״. זה עובד נחמד ב Batch Processing (היכן שזה פשוט) - אבל זה יכול בקלות להסתבך.
- גישה שנראה לי שהולכת ותופסת תאוצה היא פשוט לסמן בצורה ברורה (coding conventions, annotations, וכו׳ - למשל IO Monand) אלו פונקציות אינן טהורות (impure) וכך לתאם ציפיות. אלו הפונקציות להיזהר מהן. כמובן שלא תמיד אפשר לכתוב מערכת שרוב הפונקציות בה הן impure ואולי יותר נפוץ, בגישה OO+FP מעורבת, לסמן פונקציות שהן pure - על מנת שיהיה אפשר לסמוך עליהן יותר.
- באם באמת כל הפונקציות שלא משנות state / ניגשות לנתונים חיצוניים - הן טהורות? לא בדיוק. למשל: כתיבת הודעת לוג מתוך פונקציה - הופכת אותה "רשמית" ללא טהורה. גם כאן, הפרגמטיות היא במידתיות. מבחינתי, אפשר להחשיב גם פונקציות עם side effects זניחים כ "פונקציות טהורות". אם נתפלסף, אזי גם לפונקציה שמחברת שני משתנים יכול להיות side effect. למשל: הוא גורמת לעדכון caches בתוך ה CPU. בואו לא נגזים.
- מדוע אומרים שפונקציות אסינכרוניות הן לא טהורות? נראה לי שזה ענין של קורולציה. בד"כ מפעילים פונקציות א-סינכרוניות עבור פעולות I/O (שזה side effect ברור), ולכן בדרך כלל הן לא טהורות. לא נראה לי שיש משהו "לא טהור" בפונקציה מעצם כך שהיא מורצת בצורה א-סינכרונית.
Function Composition
בגישת ה FP מדברים על "higher order functions״ כלומר - פונקציות שמקבלות פונקציות בתור פרמטר - על מנת להרכיב פונקציות מורכבות יותר.
למשל: רוב הפונקציות המאפשרות Functional Style הן צורה פשוטה של Function Composition. הפונקציות map או filter מקבלות פונקציה (״למבדה״) שבעזרתן הן עושות את הפעולה.
זה נחמד, שימושי, ומועיל. אפשר כך לפרק פונקציה מורכבת לפונקציות פשוטות יותר, זה מאפשר קוד Reuse, וזה מאוד תומך ב״דרך החשיבה הפונקצינלית״ שזו סוג של חשיבה מתמטית. הרי Function Composition (כמו רעיונות שונים של FP) - הוא רעיון שמגיע ישירות מהמתמטיקה.
Function Composition הוא גם מקור לצמיחת סיבוכיות, ופיזור קוד.
אין לי דוגמה לשלוף, אז שוב שרבטתי משהו מהיר (ושוב: בשפת קוטלין):
הנה דוגמה ל pure function שמצליחה להיות לא צפויה ולא ברורה.
כבר אמרנו שבכל כלי טוב - ניתן להשתמש בצורה גרועה?
קריאת קוד הפונקציה לא מלמד אותי מה היא עושה - הכל תלוי בפונקציות ששולחים לה כארגומנטים.
גם אם אגש מה call-site (הנקודה בקוד בה מפעילים את הפונקציה) ואראה מה שולחים לה - לא תהיה ברורה התוצאה, כי הקשר בין customerFilter ו selectionLogic הוא לא ברור-מאליו, בטח לא עם ״התוספות״ ששתלנו, כמו בדיקת ה balance ואם הלקוח פעיל.
האם מישהו כותב פונקציות כאלו?!
כן. זה קורה. מתוך אהבה ל FP אנשים מזהים דפוסים חוזרים בקוד - ומוציאים אותם ל composer functions שאמנם חסכו כמה שורות קוד כפולות - אך הפכו את הקוד למאתגר להבנה ותחזוקה.
לכן, כמו שאני ממליץ להשתמש בהורשה רק לאובייקטים פשוטים והיררכיות לא-עמוקות, כך אני ממליץ להשתמש ב Function Composition רק לפונקציות פשוטות, מובנות היטב, עם היררכיות לא-עמוקות. אחרת - זה עלול להיות כלי מזיק. כל פרדיגמה - והכלי המסוכן שלה.
טיפוסים גמישים
זה לא באמת עיקרון, אבל תכונה שלרוב תגיע ביחד עם רעיונות ה FP האחרים. בגלל העבודה הרבה עם מבני-נתונים, בגלל שערך החזרה יחיד של פונקציה הוא די מגביל (ואז מעדיפים להחזיר מבנה שגם יכול להחזיק שגיאות, וגם מצבים אחרים - ישנם צרכים שונים), ובגלל ה Immutability - תכונה נפוצה לתמיכה ב FP היא היכולת להגדיר מבני-נתונים בקלות רבה.
עוד טיפוס שימושי מאוד בשפות פונקציונליות הוא ה Enum. ה״פילוסופיה של FP״ היא לא להשאיר דברים ליד המקרה בזמן הריצה. לא להשתמש ב nulls, ולא לעבוד עם exceptions - הקוד שנכתב צריך לכסות את כל המקרים בזמן הקומפילציה.
על Haskell נאמר ש״אם הקוד מתקמפל - אזי התוכנה עובדת״ - בדיוק בשל אימוץ הגישה הזו.
Enums עוזרים לתאר את מרחב המצבים האפשריים מהפעלת פונקציה, ולוודא (בתמיכת השפה, כמובן) - שכל המקרים בהכרח מטופלים.
סיכום
השפעת הפרדיגמה הפונקציונלית ניכרת למדי בשנים האחרונות, גם אם לפעמים נדמה שיש לגביה מידה שווה של דיס-אינפורמציה ואינפורמציה.
אומר זאת שוב: אין עניין של בחירה ב OO או FP. שמעתי דיונים כאלו כמה פעמים - והם פשוט מטופשים. זה כמעט כמו לדון מה יותר טוב: HashTable או Vector/ArrayList?
את שניכם אתם רוצים שיהיו בסט הכלים שלכם, וכל דיון צריך להיות למקרה הספציפי והבעיה שאתם מנסים לפתור. יתרה מכך, השילוב ביניהם - הוא בד״כ האופציה הטובה ביותר. דיון רלוונטי הוא מתי, ועד כמה להשתמש בכל אחד.
פרדיגמת ה FP מציבה כמה רעיונות רבי-עוצמה, ששימוש מושכל בהם - יכול מאוד לתרום להתמודדות עם מורכבות של מערכות. התמודדות שרובנו חווים - כל יום.
הפרדיגמה הפונקציונלית לא חפה מנקודות חולשה וחסרונות. חלקן (צריכת משאבים מוגברת) - כבר לא רלוונטיים ברוב המקרים, ארגון המערכת וטיפול ב state מורכב - הן נקודות חולשה שעדיין תקפות במלוא עוצמתן. שדווקא רעיונות OO - יכולים לעזור להתמודד עם נקודות החולשה הללו.
אם ציפיתם, שאולי עכשיו, שלושים וקצת שנה לאחר שהמונח הזה נטבע - נמצא Silver Bullet שיפתור את כל בעייתנו - צר לי לאכזב אתכם. בכל מקרה, FP מרגיש כמו אחד הרעיונות ששווה בהחלט ללמוד ולהפנים. גם מבלי להיות Silver Bullet - יתכן ומדובר בסט חשוב של רעיונות שעשוי להעלות מדרגה את יכולת ההתמודדות שלנו עם סיבוכיות - לפחות אם נשתמש בהם בצורה נכונה.
שיהיה בהצלחה!
תודה רבה! מחכים ומאלף כתמיד.
השבמחקתודה רבה, מאיר!
מחקסיכום מעולה.
השבמחקלגבי השילוב של שפה FP בפרוייקט OO - האם חשבת גם על עקומת הלמידה למפתחים חדשים?
תודה רבה ליאור על הפוסט המעולה.
השבמחקנהדר, ליאור! סדרת כתבות מצויינת. תודה!
השבמחקתוספת קטנה משלי לגבי נושא ה-Immutability: כאשר מספר גדול של אנשים עובד על אותם נתונים, השמירה על Immutability תורמת להגנה על אותם נתונים. כלומר, גם בשפות כמו Javascript שבה המקביליות לרוב לא באה לידי ביטוי, לגישת ה- Immutability תהיה תרומה.
בהכללה, ניתן לומר ש-Immutability יכול לתרום בכל מקום שבו קיימת מקביליות - בין אם בזמן ריצה, או בזמן פיתוח.
תודה אופיר!
מחקמעניין מאד.
השבמחקעכשיו רק נשאר להבין מה הקטע עם תכנות ריאקטיבי...