טיפוסים דינאמיים או סטטיים?

ב־23.1.2012, מאת ארתיום; פורסם תחת: תכנה חופשית, פיתוח, תכנה ומחשבים; ‏21 תגובות

נתקלתי בכתבה מעניינת

Static vs Dynamic Debate Decided

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

תגובות

מאיר, ב־23.1.2012, 10:30

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

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

ארתיום, ב־23.1.2012, 10:41

תשמע מאיר, אני בהחלט אוהב שפות דינאמיות, יש להן תכונות ייחודיות, Python ו־JavaScript הן שפות מעולות...

מצד שני זה באמת קשה, לבלבל וקל לעשות טעויות.

מאיר, ב־23.1.2012, 10:52

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

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

ליבוביץ, ב־23.1.2012, 12:29

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

מאיר, ב־23.1.2012, 12:45

ב-vim:

‎:RopeRename וזהו ?

נשמע ממש כאילו אנשים לא מכירים את הכלים או בכלל היו מעורבים בפרוייקטים שכאלה.

ארתיום, ב־23.1.2012, 14:05

מאיר, האם הכלי שלך ידע לשנות את הקוד הבא:

class foo:
    def x(self):
        print 1

class baz:
    def x(self):
        print 2

class bar:
    def x(self)
        print "Something else"




f = foo()
f.x()

f = baz()
f.x()


y=bar()
y.x()

לקוד הבא:

class foo:
    def y(self):
        print 1

class baz:
    def y(self):
        print 2

class bar:
    def x(self)
        print "Something else"




f = foo()
f.y()

f = baz()
f.y()


y=bar()
y.x()

אני מניח שלא, כי Refactoring כמו למשל גם overloading לא מוגדר בשפות דינאמיות. זה משהו מובנה בשפות דינאמיות

ik, ב־23.1.2012, 14:19

ארתיום, מצטער, אבל גם כאן אני לא מסכים אתך.

אתה מתבלבל בין שפה לבין כלי. refactoring שייך לכלי, לא לשפה. בעוד ש overloading שייך לשפה ספציפית ולא לסביבה.

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

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

למשל לרשת constructor של האב שלך. אתה מסוגל בפסקל, רובי ופיתון, אבל לא ב ++C ולא ב PHP ולא בג'אווה. זה לא הכלי שמונע ממך, זו השפה שמונעת ממך.

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

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

מאיר, ב־23.1.2012, 14:33

רגע, אתם באמת לא מסוגלים להבדיל בין שפה ל-ide ? איך אתה עושה את זה בשפה סטטית ללא ide ?

בשביל הכיף, בדקתי את הדוגמא שלך כעת עם וide אחר, pycharm, וה-rename שלו ב-refactor. עבד. בקיצור, שטויות.

ארתיום, ב־23.1.2012, 14:36

אתה מתבלבל בין שפה לבין כלי. refactoring שייך לכלי, לא לשפה.

אולי אני אסביר למה אני מתכוון, תסתכל בדוגמה שנתתי: ב־Python זה לא מוגדר אם foo,‏ bar ו־baz מממשים את אותו הממשק או לא. רק צורת השימוש שלהם קובעת האם הם באמת אותו API או לא.

לעומת זאת, ב־Java, Pascal, C++, C#. D ועוד שפות סטטיות רבות אחרות אתה יכול כי אם אתה משתמש בממשק מסוים אתה צריך להגדיר אותו:

class XCallable {
public:
  virtual void x() = 0;
};

class Foo : public XCallable { ... };
class Baz : public XCallable { ... };
class Bar { };

במקרה הזה אתה יודע מבחינה סמנטית מה זה x ובשפות סטטיות אתה יכול לשנות את x ל־y בצורה אלגוריתמית כי זאת משימה מוגדרת היטב.

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

תראה, אני לא מדבר על כלי אלא על יכולת. שד"א קיימת ברוב ה־IDE ולא רק בלזרוס, אבל זה סיפור אחר.

למשל לרשת constructor של האב שלך

אאל"ט ב־Pascal אתה חייב לקרוא לו באופן מפורש, ב־C++‎ אתה הוא נקראה אוטמטית או שאתה חייב לקרוא לו לפני שאתה מייצר את הצאצא. קרי אבא חייב להיות "בנוי" לפני שאתה בונה את הבן. מגבלה? אולי, אבל זה סיפור אחר.

ארתיום, ב־23.1.2012, 14:39

בשביל הכיף, בדקתי את הדוגמא שלך כעת עם וide אחר, pycharm, וה-rename שלו ב-refactor. עבד. בקיצור, שטויות.

וזה באמת לא שינה את המתודה x של bar ל־y אבל כן שינתה את המתודות x של baz ושל foo ל־y? וגם לא הייתה צריך להתערב בהחלטה?

מאיר, ב־23.1.2012, 14:39

אבל ניסיתי את דוגמת ה-refactor שלך ב-ide שמיועד לפייתון וזה עבד, אז מה זה אומר, שלא יודעים על מה מדברים ? שאין ניסיון ? שיש בורות ?

מאיר, ב־23.1.2012, 14:40

אתה רוצה screencast או להוריד ולנסות בעצמך ?

ארתיום, ב־23.1.2012, 14:45

אתה רוצה screencast או להוריד ולנסות בעצמך ?

אני מאוווד מסוקרן ואני בהחלט אנסה בעצמי...

כי זה לא נראה לי הגיוני.

אתה יכול לאמת שנית ששמת לנקודה שלי (חלק מהמתודות השתנו וחלק לא בצורה אוטומטית לחלוטין)

foo::x -> foo::y
baz::x -> baz::y

bar::x -> bar::x
מאיר, ב־23.1.2012, 15:13

http://youtu.be/ygB_STUxjgc?hd=1

ארתיום, ב־23.1.2012, 15:28

מאיר Dude לא הבנת את הנקודה שלי :-)

הנה הקוד:

class Base {
public:
     virtual void x() = 0;
     virtual ~Base() {}
};

class Foo : public Base {
public:
    virtual void x() { std::cout << 1 << std::endl; }
};

class Baz : public Base {
public:
    virtual void x() { std::cout << 2 << std::endl; }
};

class Bar {
public:
    virtual void x() { std::cout << "Hello" << std::endl; }
};

אני הולך ב־eclipse ומשנה ל־Base את המתודה x ל־y וכל המחלקות שיורשות מ־Base מתעדכנות אוטומטית (קרי Foo ו־Baz אבל לא Bar כי הוא לא יורש מ־Base).

ב־Python כיוון שאין צורך בלהגדיר "ממשק" מוגדר כמו Base אתה חייב לעבור מחלקה מחלקה כי זה לא מוגדר מי באמת יורש ומי לא. אתה אומר את זה באופן מפורש.

כך למשל אם הייתה לך פונקציה:

def something(p):
   p.x()

הכלי שלך לא היה יודע לשנות את x כי אין לו מספיק הקשר.

זה מה שאני מדבר עליו.

(אבל תודה על Screencast :-) )

ik, ב־23.1.2012, 16:18

ארתיום, אם אתה יורש בפיתון (או רובי) אז IDE נורמאלי ידע את ההקשר.

בלי קשר, בלזרוס, רק אם אני אשתמש בלי Refactoring לשינוי השם זה יעבוד כמו שאתה מציע, אחרת הוא גם לא ישנה כלום. הכלי refactoring של שינוי שם מנסה להבין את המשמעות של הקוד שהוא משנה, ולא רק משנה כמו search and replace.

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

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

זה לא קשור לשפה, זה קשור לסביבה. זה מה שמאיר מנסה להסביר לך.

מאיר, ב־23.1.2012, 16:25

זו לא דוגמת הפייתון שהדבקת פה, וזה מה שהלכתי לפיו.

בכל מקרה: http://youtu.be/59rN4do0rrw?hd=1

שטויות, אמרנו ?

אה כן, והזהר בבקשה לפני שאתה מדביק קוד של C++, פשוט דוחה :)

ארתיום, ב־23.1.2012, 16:40

תראה מאיר, כל הנקודה עם השפות הדינאמיות כמו Python זה שאתה לא ממש צריך להגדיר Base או ממשק. וזה גם מה שעושים. כמובן אם אתה הולך על פי "סגנון C++‎" זה משהו אחר.

שים לב ה־Base שהגדרתי הוא ממשק לא "מחלקה" שיורשים ממנה.

כמו כן בדוגמה השניה:

def something(p):
    p.x()

אתה באופן עקרוני לא יכול לעשות refactoring נכון כי "נכון" לא מוגדר בלי הקשר של שימוש ב־something והוא לא תמיד קיים.

מאיר, ב־23.1.2012, 16:56

כמובן שצורת עבודה נכונה יותר בשפות דינאמיות תהיה להעביר את ה-instance method ולא את האובייקט עצמו.

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

אם אנשים מתעקשים לעבוד עקום כמו בשפות סטטיות, בפייתון יש גם Abstract Base Classes, אם ממש רוצים intefaces, יש את zope.interfaces וכו'. בקיצור למה לדבר סתם וללא ניסיון ?

עפר, ב־25.1.2012, 17:41

ואו איזה דיון ייצרי, על קוד.

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

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

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

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

מימוש זהה שכתבתי ב-C# (אני יודע, לא מומלץ) עם שימוש רב ב-Reflection (שלא היה כיף לכתיבה) פשוט אוכף את הכללים יותר טוב.

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

(בכל זאת אני כותב כבר שנים באיזה 4 שפות וסביבות שונות) וקיבלתי

Yakov, ב־11.2.2022, 5:38

I must agree with you on this one. Dynamically typed languages tend to be 'write only'. The author has the code structure cached in their head, but there's nothing in the language to document that structure for future readers or for interpreters to verify. When reading python code, say, I find myself symbolically executing arbitrary turing-complete code in order to figure out an API. Type systems allow this separation to happen to a large extent -- I don't need to look at the innards of a C library to figure out how to use the API declared in its headers. It's so true that python had type annotations added to the language; take this as evidence in favor of static typing.

הוסף תגובה:

 
 כתובת דוא"ל לא תוצג
 

ניתן לכתוב תגובות עם שימוש בתחביר Markdown.

חובה לאפשר JavaScript כדי להגיב.

דפים

נושאים