הבלוג של ינון פרק

טיפים קצרים וחדשות למתכנתים

החזרת מספר בטווח ב TypeScript

02/05/2023

נתבונן בפונקציה הבאה בטייפסקריפט:

function createRandomNumber(): number {
    return Math.floor(Math.random() * 10);
}

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

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

דרך אחת טובה להוסיף את ה-10 לממשק היא פשוט לכתוב את כל המספרים השלמים מאפס עד תשע בתור ערך ההחזר של הפונקציה:

type Range10 = 0|1|2|3|4|5|6|7|8|9;
function createRandomNumber() {
    return Math.floor(Math.random() * 10) as Range10;
}

לשיטה זו יש את יתרון הקריאות - מי שמגיע לקוד מבין מיד מה רציתם להגיד, אבל היא יכולה להיות מייגעת לכתיבה.

הפיתרון הגנרי משתמש ברקורסיה ונראה כך:

export type Range<N extends number,R extends number[]=[]> = 
    R['length'] extends N ?  R[number] : Range<N, [...R, R['length']]>;


function createRandomNumber() {
    return Math.floor(Math.random() * 10) as Range<10>;
}

וההסבר-

  1. הרקורסיה בונה את המערך R, כך שכל איטרציה היא מוסיפה ל R מספר נוסף.

  2. המספר שמתווסף הוא האורך הנוכחי של R, ולכן המספרים יהיו בטווח מאפס עד המספר N.

  3. בסוף הרקורסיה מחזירים איחוד של כל המספרים השמורים ב R.

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

איך לבלבל את החברים עם קובץ usercustomize.py

01/05/2023

לא רבים יודעים אבל מספר קבצים מסתוריים במערכת הקבצים של משתמשי פייתון. אחד מהם נקרא usercustomize.py. אני לא בטוח מה היתה המטרה המקורית של הפיצ'ר, אבל המצב היום הוא שאם תשימו קובץ כזה בתוך תיקיית site-packages על המחשב שלכם, אז כל פעם שתפעילו סקריפט פייתון המפרש יטען קודם כל את הקובץ usercustomize.py ורק אחר כך יעבור להפעיל את הסקריפט.

(וכן זה עובד אפילו עם ה REPL9).

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

python -c "import site; print(site.getsitepackages())"

אצלי היא הדפיסה:

['/Users/ynonp/.pyenv/versions/3.11.1/lib/python3.11/site-packages']

אני יוצר קובץ חדש בתוך התיקיה בשם usercustomize.py, ובתוכו כותב את התוכן הבא:

print("I will NOT go away. I do NOT wish to go!")

בואו ננסה את זה. אני מפעיל REPL ומקבל את הפלט:

I will NOT go away. I do NOT wish to go!
Python 3.11.1 (main, Apr  6 2023, 09:09:27) [Clang 14.0.0 (clang-1400.0.29.202)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>

אני מפעיל שרת HTTP ומקבל:

$ python -m http.server 8000

I will NOT go away. I do NOT wish to go!
Serving HTTP on :: port 8000 (http://[::]:8000/) ...

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

אז למה זה טוב? (חוץ מלבלבל את החברים). הנה כמה רעיונות:

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

  2. אפשר להוסיף עוד ספריות ל PYTHONPATH כדי לטעון מהן מודולים.

  3. אפשר לתעד כל הפעלה של פייתון או למנוע הפעלות מסוימות (לדוגמה אפשר להוסיף sys.exit בתוך הקובץ אם לא רוצים לאפשר הפעלה מסוימת).

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

יש לכם עוד רעיונות טובים לשימוש ב usercustomize.py? נתקלתם ב Use Cases מעניינים איתו? ספרו לי בתגובות.

החיים באי וודאות

30/04/2023

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

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

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

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

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

אבל היכולת לחיות עם אי וודאות היא הכרחית אם החלום שלנו הוא לפתור בעיות מעניינות (כאלה ש Chat GPT לא יודע לפתור).

הנה כמה רעיונות איך לאמן את שריר החיים עם אי הוודאות, בהתחלה על דברים לא חשובים ולאט לאט גם על דברים יותר חשובים-

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

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

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

  4. נסו לבנות פרויקט צד ולהביא אותו לרמה של מוצר שאתם יכולים למכור. לא משנה מה הפרויקט.

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

השוואה סובייקטיבית לגמרי בין neo4j ל datomic

29/04/2023

השבוע התבשרנו שדטומיק, או יותר נכון nubank, החברה שמחזיקה ב datomic, החליטו לבטל את כל עסקי הרישיון המסחרי של דטומיק ולהפיץ גם את גירסת הפרו בחינם. כדי לחגוג את האירוע הנה השוואה קצרה וסובייקטיבית לגמרי שלי בין שני בסיסי נתוניים גרפיים שאהבתי במיוחד - Datomic מול neo4j.

המשך קריאה

סיבה ותוצאה

28/04/2023

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

ורציתי לשאול אותה - למה את עושה את זה לעצמך? את הרי כבר כל כך ספורטיבית. בשביל מה את צריכה לצאת שוב לרוץ? למה לסבול ככה?

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

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

וברגע הכל התחבר. המטרה של שינוי היא לא התוצאה, אלא להבין איך לאהוב את הדרך.

איפה להשקיע את זמן הלימוד שלי?

27/04/2023

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

שני הצירים מחלקים את העולם ל-4 ריבועים:

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

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

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

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

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

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

חיפוש טוב יותר

26/04/2023

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

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

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

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

איך לא לייצר סיסמה אקראית

25/04/2023

ביקשתי מ Chat GPT לכתוב פונקציית פייתון שמחזירה סיסמה אקראית מתוך אורך ורשימה של תווים בסיסמה. זאת היתה התוצאה:

import random

def generate_password(length, symbols):
    password = ''
    for i in range(length):
        password += random.choice(symbols)
    return password

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

import string
import random

def generate_password(length):
    chars = string.ascii_letters + string.digits + string.punctuation
    while True:
        password = ''.join(random.choice(chars) for _ in range(length))
        if (any(c.islower() for c in password)
            and any(c.isupper() for c in password)
            and any(c.isdigit() for c in password)
            and any(c in string.punctuation for c in password)):
            return password

אז נכון גם הרבה מתכנתים אנושיים שאני מכיר היו משתמשים ב random.choice במקום ב os.urandom בלי לשים לב, ואולי גם אתם כתבתם קוד כזה בעבר. בכל מקרה אלה כמה סיבות בגללן אני משתדל לא לכתוב כך, גם במערכות שאינן קריטיות לאבטחה של אף אחד:

  1. קוד מידבק - כש Chat GPT כותב קוד, או כשקטע קוד מופיע ב Stack Overflow, אנשים נוטים לקחת אותו כמו שהוא. התופעה אפילו יותר חמורה בתוך הארגון. כשאני כותב באיזשהו מקום במערכת קוד לא מאובטח, מישהו הולך להעתיק אותו לעוד מקומות, ואין לי שליטה לאן הקוד הזה יגיע בסוף.

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

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

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

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

import os

def make_random_password(length=12, symbols='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@$^_+&'):
    password = []
    for i in map(lambda x: int(len(symbols)*x/255.0), os.urandom(length)):
        password.append(symbols[i])
    return ''.join(password)

הטעות שכולם עושים

24/04/2023

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

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

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

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

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

וזו הטעות הגדולה שכולם עושים.

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

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

מתי לחתוך

23/04/2023

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

לא נופתע לגלות שהקבוצה הראשונה בנתה בממוצע פחות יצירות. מאוד קשה לבנות דברים שאתה יודע שאף אחד לא ישתמש בהם, ועוד רגע הם יפורקו.

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

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

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

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

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

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

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