שפות dynamic typing לפרויקטים גדולים

S h a r k 1 8

New member
שפות dynamic typing לפרויקטים גדולים

בשנים האחרונות יש פריחה של שפות תכנות dynamic typing כמו python ו node.js וחברות רבות משתמשות בהן גם לפרויקטי תוכנה גדולים.

אמנם dynamic typing הופך את פעולת הקידוד לזריזה יותר, אבל יש לה חסרונות ברורים שמתעצמים כשמדובר בפרויקטים גדולים. למשל:
1. קשה יותר להיכנס לקוד קיים. ציון ה type של המשתנה נותן מידע על מה המשתנה מייצג ואילו פעולות ניתן לעשות עליו.
2. מציאת באגים כבר בזמן כתיבת התוכנית במקום ב run-time, למשל אם מאתחלים ערך מ type מסוים למשתנה שהוגדר עם type אחר, ה IDE מייד יראה שגיאה.
3. אפשר לאחסן באותו משתנה ערכים מ type-ים שונים, מה שעלול לגרום לבלאגן ולשגיאות.

אלה מכם שיצא לכם להתנסות בשפות dynamic typing בפרויקטים גדולים, האם תוכלו לספר מהחויות שלכם ואיך התמודדתם עם החסרונות האלה? בשורה התחתונה, הייתם ממליצים לעבוד עם השפות האלה בפרויקטים גדולים?
 

fellow1

New member
אין לי הרבה נסיון עדיין

אבל מהנסיון שיש לי ההבדלים דרסטיים: קוד בשפות מתקמפלות כמו C CPP וג'אווה מאוד קל להבין ובדרך כלל המתכנתים שכותבים אותו מאוד מסודרים. אם לוקח לך שעה לקמפל את הקוד אתה תהפוך בסופו של דבר להיות מסודר ותפתח הרגלים בריאיים כי התשלום שאתה משלם על שגיאות מפגרות יכול היות יקר מאוד.
&nbsp
בnode.js שאגב היא לא שפה אלה מכונה וירטואלית שמריצה את הV8, הקוד שניתקלתי בו היה מבולגן עם משתנים עם שמות לא ברורים חוסר אינדנטציה והכי גרוע הרבה העתק הדבק מהאינטרנט.
&nbsp
אתה מוזמן לנסות לחפש שגיאה שמתגלה בזמן ריצה בגלל תו ascii סורר שאין לו הצגה טקסטואלית שהגיע כשאיזה טמבל עשה העתק הדבק מאיזה עמוד כשאין לך שום אינדיקציה איפה ומה השגיאה והקוד ניהיה בעייתי רק כשהVM מחליטה לקמפל את הפונקציה...
&nbsp
תוסיף לזה גם את זה שהרבה יותר מהמתכנתים הם אנשים שעשו הסבה (כמוני) ושקשה לשכנע אותם לשמור על קונבנציות.
&nbsp
יש גם בעיות נוספות, לטעמי שמות של פונקציות ומשתנים צריכים להיות כמה שיותר מפורטים. כשפונקציה מתקמפלת כמה פעמים בזמן ריצה כמו בnode זה יכול לפגוע בביצועים או ליצור ביצועים לא רציפים כשהקומפיילר צריך לתרגם שמות ממש ארוכים ויש מצבים שזה יכול לשנות תלוי על מה התוכנה רצה.
&nbsp
node גם מכילה לא מעט באגים לא צפויים, גם בשפה עצמה (היו מקרים שהיו לי פיצרים שבגלל באגים מוזרים לא עבדו בלינוקס אבל כן בווינדווס) ובחבילות הרחבה שכולם משתמשים בהם וזה יכול לגרום לך לעיכובים בלתי צפויים כשאתה צריך להחליף חלק גדול מהקוד שכתבת. אין את זה בשפות ותיקות יותר...
 

BravoMan

Active member
מה בדיוק מכל מה שכתבת קשור ל-dynamic typeing?

קוד ב-CPP יכול להגיע להיות הדבר הכי לא קריא שנתקלתי בו.
כשמתחילים לעשות שימו מוגזם ב-templates בסימנים שמשמשים מספר אופרטורים והפירוש תלוי הקשר, ועוד פיצ'רים מתקדמים של השפה, קל מאוד להגיע לקוד שגם מתכנת מנוסה יתקשה לפענח.
&nbsp
מתכנתים זבל יש בכל שפה.
וזה עיקר התלונה שלך - שעבדת מול מתכנתים לא טובים.
התלונה השנייה שלך היא על מימוש מנוע ספציפי של VM.
&nbsp
אגב, 11++C הכניסו auto ומאוד גאים בזה. מעניין למה?
 

fellow1

New member
קומפיילר טוב אומר לך איפה טעית.

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

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

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

user32

Well-known member
מנהל
זה חוסר נסיון ו/או חוסר מקצועיות

ההבדל היחיד האמיתי בשפות האלה זה הtypes. כל שאר הדברים שהזכרת: העתקות מהאינטרנט, אינדנטציה, קוד לא קריא, דברים סינכרוניים וא-סינכרוניים אלה דברים שלא קשורים לשפה ולא "באשמתה".
בתור אחד שעבר משפות מקומפלות js/python בהתחלה הקוד היה נראה לי כמו בלאגן אחד גדול ושזה מיועד לחובבנים שרוצים לקשקש משהו מהיר. עם הזמן העין התרגלה לקרוא את הקוד וגם להבדיל בין קוד שנכתב בצורה חובבנית לקוד איכותי.
בעניין הtypes: קודם כל טסטים טסטים וטסטים. שנית, גם כשאין טסטים בסוף זה מייצר שגיאות מסוג מאוד מסויים שקל למצוא בruntime. בדרך כלל undefined property ודברים כאלה. בימינו הרוב כותבים עם ES שמוסיף סינטקס הרבה יותר מובן לחובבי השפות הOO הקלאסיות ומי שממש רוצה ללכת לכיוון הקשוח כותב בTS.
 

BravoMan

Active member
דין JS אינו דין Python שאינו דין BASH

גם אני חשבתי הרבה שנים שאם זה לא מתקמפל ל-EXE זה לא שפת תכנות אמתית.
&nbsp
למדתי על בשרי כמה זה שגוי.
כשאתה רוצה משהו שירוץ הן על Desktop של Windows והן על אחד של Linux, בלי שינוי, כשאתה צריך כלי ייעודי לאוטומציה, כשאתה רוצה שפה שתהיה נוחה לפרויקט לימוד מכונה ול-one liners בשורת פקודה ועוד ועוד.
&nbsp
עם קומפילציה יש צרות משלה, ורוב המתכנתים שאני מכיר לא מנצלים זמני קומפילציה ארוכים כדי להרהר מה הם עשו לא בסדר בקוד, אלא כדי לגלוש באינטרנט או לשחק בנייד.
&nbsp
זה נחמד, אבל לא ממש משפר את איכות וקריאות הקוד.
&nbsp
תראה, אני מבין מנין אתה מגיע:
JS היא באמת שפה שנוצרה במקור למעצבי אתרים שלא יודעים לתכנת, ועדיין באיטרציה השישית שלה סוחבת הרבה מהמטען הזה.
&nbsp
בנוסף, נתנו לך לחפור ב-VM ספציפי שיש ועכשיו אתה "חם" על כל הצרות שלו.
&nbsp
אם הייתי מספר לך כמה פעמים נתקלתי בבעיות קומפילציה בפרויקטים גדולים והייתי צריך לנבור באינטרנט ובערמות Makefles, או גרוע מזה:
ה-VS של MS כתב שהפרויקט התקמפל ללא שגיאות ובפועל גילית שאין EXE.
&nbsp
לכל שפה, ולכל כלי יש צרות משלה.
אבל שפות סקריפט אינן רעות בהכרח, ו-typing לא יציל אותך ממתכנתים גרועים.
 

user32

Well-known member
מנהל
המשקעים שJS סוחבת מאיך שנולדה עדיין רודפים אותה

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

fellow1

New member
אוקי

לא דיברתי על שפות סקריפט בכלליות, אבל אם אתה רוצה לכתוב בצד שרת עם Javascript אז היום משתמשים הרבה פחות באלטרנטיבות לnodejs , הנה אפילו הבחור שפתח את הנושא חשב שמדובר בשפה מרוב שהVM הזה פופולארי על כל הבעיות שלו וזו הסיבה שעניתי לו על הבעיות הספיציפיות שלו. אני ממש לא מכיר כל framework שעושה שימוש בJS כדי לצלוב את כל השימושים שלה בפרויקטים גדולים.

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

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

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

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

BravoMan

Active member
למזלי, אני לא עובד עם node, ולא עם JS, TS, ES

ויתר מוטציות של החולירה.
&nbsp
מה ששבר אותי כשניסיתי ללמוד JS, היה הערה בספר לימוד לגבי '=='.
נכתב שם: אל תשתמשו בזה! הלוגיקה מאחורי ההשוואה הזו כ"כ מסובכת, שגם מתכנתים מנוסים עלולים לטעות בניסיון לנחש תוצאות השוואה.
תשתמשו תמיד ב-'==='.
&nbsp
כשראיתי המלצה כזו לגבי פיצ'ר כ"כ בסיסי ומרכזי של השפה, אמרתי לעצמי: די!
&nbsp
(וכן - אני יודע ש-JS אינה היחידה שעושה את זה)
&nbsp
אבל אומר לך משהו לגבי שפות מתקמפלות:
ב-13 שנותיי בתעשייה עבדתי עם C, C++ ובשנים האחרונות Java.
&nbsp
ומעולם, אבל מעולם, לא ראיתי מתכנת שתחת לחץ מתחיל לכתוב בצורה זהירה וקפדנית יותר!
זה פשוט לא קורה! ככל שיש יותר לחץ, כך הקוד מכוער יותר, וזמן קומפילציה אינו משמש כהרתעה.
&nbsp
הקונספט של typing לא נוצר כדי לעזור למתכנתים.
להפך - הוא הגיע משפות קרובות לחומרה, ונועד לפשט את העובדה של הקומפיילר.
&nbsp
אבל מה לעשות שמתכנתים אוהבים אבסטרקציות, וככל שהפרויקט יותר גדול ומסובך, כך יש לך יותר אבסטרקציות, כי אתה רוצה למחזר קוד, ולהיות מוכן לכל מיני מקרים, ועוד ועוד.
&nbsp
אז בשפות C התחילו לממש ADT עם מצביעים, וזה לא היה נעים.
++C הוסיפו templates, אבל בניגוד לקוד רגיל, לדבג tamplate זה סיוט.
ב-Java, ה-Generics, שבמבט חטוף קצת מזכירים את ה-template של ++C הם פיצ'ר מסכן וחצי אפוי.
&nbsp
קח אוסף גינרי ותראה שחצי מהפונקציות שלו מחזירות Object או מערך Object במקום הטיפוס שאיתו יצרנו את האוסף.
&nbsp
dynamic typing פותר את כל הצרות האלה, וחוסך למתכנתים זמן ותסבוכות על יצירת אבסטרקציות רק כדי לטפל בנתונים בצורה יפה וגינרית יותר.
&nbsp
יש לזה מחיר, אבל להערכתי, זה עדיין שווה את זה.
 

user32

Well-known member
מנהל
הפתעת אותי

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

BravoMan

Active member
אכן, קוראים לזה העמסת אופרטורים

וכן, זה כלי שעלול להיות מסוכן במבט ראשון, אבל אי אפשר להשוות אותו לטבלת ההמרות של JS.
&nbsp
והנה 2 סיבות עיקריות:
1. לא כל פרויקט משתמש בהעמסת אופרטורים ולאה שמשתמשים לא מעמיסים כל מה שבא ליד.
פרויקט נורמלי יעמיס אופרטור אך ורק כשזה הגיוני ונחוץ לטפל במבנה נתונים מסוג חדש, אז בד"כ העמסה תבוא במקום בו לא היה אופרטור לפני זה.
&nbsp
מה שאומר, שמבט חטוף בקוד מיד מבהיר מתי יש שימוש באופרטור מועמס ומה ההעמסה עושה.
&nbsp
אז, בניגוד ל-== שאי אפשר להתחמק ממנו, העמסת אופרטורים תכניס אותך לצרות רק אם מישהו ממש מנסה לחבל לך בקוד.
&nbsp
2. אם אתה עובד עם סביבת פיתוח סבירה, הסביבה תוכל להגיד לך בקלות איזה אופרטור מועמס יופעל לפי סוגי הפרמטרים.
&nbsp
אני מודה שאני לא מכיר סביבות פיתוח ל-JS, אבל האם יש דרך קלה להגיע לאותה לוגיקת השוואות?
(אני יודע שיש לוגיקה, הבעיה כמה היא נגישה, וכמה היא מורכבת)
 

user32

Well-known member
מנהל
נו, אז זה מאוד דומה

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

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

לגבי עזרה של IDE: מה שהרוב עושים זה LINT שאוכף ===. הכי פשוט.
 

ipv6

Member
אם אתם בקטע של להראות

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

BravoMan

Active member
רק שמי שלא מקצוען ב-++C לא ילך להעמיס אופרטורים

כי זה פיצ'ר למתקדמים.
&nbsp
נתקלתי במפתח JS שאמר לי בכנות שהוא אוהב מאוד להשתמש ב-== ב-Client side, כי זה חוסך לו כאב ראש ומשווה כל מה שמשתמש מכניס ולא משנה מה.
(לא בדיוק במילים האלה).
&nbsp
אני עדיין ממתין ליום שיהיה לי זמן למצוא את הקוד הספציפי שלו כדי לראות כמה קל יהיה לשבור את זה.
 

ipv6

Member
אני מציע לך לא להגיד שהעמסת אופרטורים זה "פיצ'ר למתקדמים"

בראיונות עבודה לתפקידי פיתוח בשפה הזאת..
 

יבגניי34

New member
מומלץ גם להמנע מלאמר ש״שימוש ב == חוסך כאב ראש״

כי ״זה משווה כל מה שנותנים לו״ בראיונות עבודה...
 

user32

Well-known member
מנהל
אני למדתי את זה בתיכון. זה בשליש הראשון של הספר בערך

 

יבגניי34

New member
החוקים מסובכים כי להשוות 2 דברים זה... מסובך.

השפה-שאין-להזכיר-את-שמה פתרה את העניין הזה יפה.
 

ipv6

Member
auto נותן לא רק קריאות

אלא בעיקר 2 דברים מרכזיים:
1. חוסך ממך התעסקות בתלות בין טיפוסים בין אבני הבניין שלך:
קוד:
#include <iostream>
#include <string>
using DataV1 = struct {
    int id = 200;
    std::string name="user32";
};
using DataV2 = struct {
    short id = 100;
    unsigned long long longId = 342652424;
};
void printId(auto data) {
    std::cout << "ID = " << data.id << "\n";
}
int main()
{
  DataV1 v1;
  DataV2 v2;

  printId(v2);
  return 0;
}

2. מונע ממך בעיות יעילות \ התנהגות מוזרה במקרים בהם לא ממש ברור מה אתה מחזיק ועלול לשלם מחיר בביצועים כתוצאה מזה:

קוד:
void mapPrinterV1(std::unordered_map<std::string,int>& map) {
    for (const std::pair<std::string,int>& kv : map) {
        std::cout << "key = " << kv.first << " " << "val = " << kv.second << "\n";
    }
}
void mapPrinterV2(std::unordered_map<std::string,int>& map) {
    for (const auto& kv : map) {
        std::cout << "key = " << kv.first << " " << "val = " << kv.second << "\n";
    }
}

הגרסה עם הauto יעילה יותר משום שבגרסה הראשונה, בגלל "טעות" שלי (חסר const ב-string בתוך ה-pair) , יש העתקה שקורת מתחת לרגליים.
auto כמובן לא טועה כמוני.

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

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

auto נהנה משני העולמות, מאפשר להנות מהיתרונות של טיפוס דינאמי מצד אחד, מהצד השני מתקפל ודואג ומאפשר לך לתפוס שגיאות מוקדם יותר.
 
למעלה