תחשיב למדא הוא צורה לוגית-פורמלית להצגה וטיפול בפונקציות במתמטיקה ומדעי המחשב.
תחשיב למדא הוא מודל חישובי שעל פי תזת צ'רץ'-טיורינג הוא שלם טיורינג כלומר הוא שקול ל מכונת טיורינג .
סימון למדה הוא ביטוי מהצורה
הגדרה:
הביטויים בתחשיב
א) משתנה:
ב) Abstraction: אם
ג) Application: אם
סוגריים שהושמטו בהפעלות חודרות שמאלה למשל
הגדרה:
בביטוי
לדוגמה,
ביטוי ללא משתנים חופשיים נקרא קומבינטור.
הגדרה:
קומבינטור הזהות הוא
כלל זה אומר ש
רדוקציית
כאשר
המשמעות היא שהפעלה של הפונקציה על איבר כלשהו מחזירה את התמונה של אותו איבר לפי כלל ההתאמה של הפונקציה. במילים אחרות, זה כלל הצבה לחישוב הערך שמחזירה הפונקציה.
נשים לב שמחפשים משתנים חופשיים ב
דוגמה:
בהקשר לנאמר למעלה על ביטויים חופשיים, נסתכל על הדוגמה הבאה
אנחנו כעת מסתכלים על הביטוי
פונקציה עם כמה ארגומנטים מתנהגים כמו בOCaml :
ביטויים בוליאניים
הגדרה: הקומבינטורים tru ו fls מוגדרים כך :
במילים אחרות זאת פונקציה שמקבלת שני ארגומנטים ומחזירה את הראשון עבור tru ועבור fls זאת פונקציה שמקבל שני ארגומנטים ומחזירה את השני.
נוכל להגדיר אופרטורים יותר מורכבים למשל
הגדרה:
נגדיר את הקומבינטור test להיות
נראה דוגמה
הפיתוח הוא לפי רדוקציית
הגדרה:
נגדיר את הקומבינטור and להיות
נוכח להוכיח בחלוקה למקרים:
אם
אם
אם
המקרה השני כמובן תמיד יחזיר
באופן דומה :
הגדרה:
זוגות סדורים
pair זאת פונקציה שמקבל שני ארגומנטים ומחזירה פונקציה בוליאנית. אם הקלט אליה הוא tru אז היא תחזיר את f , אחרת היא תחזיר את s. הפונקציות האחרות הן דרך לשלוף את האיבר הראשון/ השני בהתאמה מהזוג הסודר. למשל כדי להוציא את האיבר הראשון נריץ
הגדרה:
מספרים טבעיים
הקומבינטורים
בעצם s היא פונקציית successor ו z זאת פונקציית האפס ועבור
הייצוג של
נגדיר את פונקציית הsucc :
בעצם היא מפעילה את
אריתמטיקה:
כאשר
מה זה
את הפונקציה הזאת מפעילים
נשתמש בכלים שהגדרנו כדי לכתוב קומבינטור שמקבל מספר ומחזיר tru אם הוא 0 ו fls אחרת:
אם
פתרון לבעיית פיבונצ׳י בתחשיב למדא
הגדרה:
כעת הפונקציה
כעת נוכל להגדיר קומבינטורים אריתמטיים נוספים
כמו כן נוכל להגדיר שיוויון על ידי
הסיבה שלעשות רק
האתגר כאן הוא קריאה של פונקציה לעצמה שכן לפונקציות אין שם בתחשיב למדה.
לפני שנגדיר נשים לב שעבור הקומבינטור
נקבל שלפי כלל
נזכיר ש בעיית העצירה היא בעיה NPH שמתארת פונקציה וארגומנט ובודקת אם היא עוצרת (מחזירה
נניח שזאת פונקצייה אפשרית אזי נגדיר פונקציה חדשה
נשים לב שאם f עוצרת על עצמה אז נכנסים ללולאה אינסופית , אם היא לא עוצרת אז הפונקציה מחזירה tru.
כעת נשאל, מה הערך שיוחזר עבור
נניח שצריך לחזור אמת כלומר Halt הכריעה שהפונקציה עוצרת על עצמה אבל :
בסתירה לכך שחזר true
נניח שזה מחזיר false :
בסתירה לכך ש Halt החזירה false
המסקנה היא שלא קיימת פונקציה Halt.
כעת נגדיר את הקומבינטור Y
למה זה בעצם מהווה רקורסיה?
בעצם Y מאפשר לנו להעביר כקלט פונקציה אחרת ולהפעיל אותה בצורה אינסופית. על הפונקציה שמעבירים לקבוע מה יהיה התנאי העצירה של הרקורסיה.
למשל נרצה לחשב את פונקציית העצרת: בצורה שהיא לא תחשיבית
למדא טהורה זה היה נראה מהצורה
בצורה שמבוססת רק על תחשיב למדא זה היה נראה כך :
יהי
משתנה שמופיע ב
כעת נוכל להגדיר את כללי ה substitution
את
נשים לב שבלי הדרישה הראשונה נקבל
הפכנו קומבינטור לביטוי שאינו קומבינטור.
בלי הדרישה השנייה נקבל
כאן הפכנו ביטוי שאינו קומבינטור לקומבינטור.
בצורה יותר מורחבת נקבל:
הסמנטיקה של רדוקציית בטא היא לא דטרמינסטית למשל :
כדי להבין מהן השיטות לגשת לבעיות מסוג זה נגדיר קודם כל את המונח רדקס.
הגדרה: ביטוי מהצורה
כמו כן כללי הרדוקציה הקיימים בצורה פורמלית הם
הראשון אומר ש אם ניתן לבצע רדוקציית בטא מ
השני אומר שאם לא ניתן לבצע על
השלישי אומר שאם מדובר ב״רדקס״ אז מחזירים את
כעת ננסה להבין כיצד לקרוא ביטוי מהצורה
א) רדוקציה מלאה - אין מגבלות.
ב) סדר נורמלי - מתחילים תמיד עם הרדקס השמאלי ביותר (החיצוני ביותר).
ג) call by name - כמו סדר נורמלי אבל לא עושים רדוקצית בטא בתוך אבסטרקציות. כלומר קודם מבצעים השמה ורק אם צריך מחשבים את הביטוי (קדימות ל E-AppAbs)
ד) call by value - מותר לבצע רדוקציית בטא על רדקס רק אם צד ימין של הרדקס הוא אבסטרקציה או משתנה. אסור לבצע רדוקציות בתוך אבסטרקציות. כלומר מבצעים השמה רק עבור משתנים ולא ביטויים. (קדימות ל E-App)
ניתן לראות כיצד ב call by value קודם חישבו את ההפעלה ״הפנימית״ ואז החליפו בהפעלה ״החיצונית״ בעוד ש ב call by name קודם ביצעו השמה באבסטרקצייה החיצונית ואז חישבו את ההפעלה הפנימית.
בכל השפות תכנות יש תמיכה בטיפוסים. למשל בOCaml על משנה let x = 5
לא נוכל להתייחס כפונקציה. ויש מקרים מורכבים יותר שהקומפילר מצליח לזהות למשל קלט לא תקין לפונקציה.
הקומפילר למעשה לא מריץ התוכנית כדי לגלות את השגיאות הללו. אלא הוא מייצר Abstract Syntax Tree בשלב הראשון ולאחר מכן מבצע Type Checking ו (ישנן שפות כמו אוקמל שעושות גם Type Inference).
נרצה להבין כיצד קומפיילרים מבצעים את הבדיקת טיפוסים הזאת
נשים לב ש type inference זה פיצ׳ר יותר מתקדם מ type checking ולא נתעסק בו כאן.
הגדרה:
קבוצת הטיפוסים מוגדרת כך:
א. Bool
ב. אם
עם ההגדרות האלה ניתן להרחיב את ההגדרות שלנו לטיפוסים נוספים למשל מספרים טבעיים
נגדיר
נשים לב שזה הגדרה לא מדויקת שכן
הגדרה:
ביטויים בתחשיב
א. כל משתנה הוא ביטוי.
ב. אם
ג. אם
ד. true, false הם ביטויים.
ה. אם
דוגמה: נסמן
זאת פונקציה שמקבלת ״שני ארגומנטים״ שמפעילה אותם אחד על השני ומחזירה את הפלט הבוליאני (f=test).
נשים לב שההגדרות שלנו עדיין לא מטפלות בטיפוסים למשל
הגדרה:
evaluation מוגדר להיות
דוגמה:
נמשיך להסתכל על הפונקציות מהדוגמה הקודמת.
true הוא כעת ביטוי בסיסי בשפה ולפי חוקי ה evaluation לא ניתן לפתח את זה ונתקענו עם ביטוי ״בעייתי״ כלומר לפני שהגעתי לערך כלשהו.
הגדרה:
ביטוי ללא משתנים חופשיים נקרא ערך אם הוא
הגדרה:
הקשר הטפסה הוא קבוצה של היגדים מהצורה
נגדיר כעת את יחס ההטפסה להיות
המשמעות של זה באופן אינטואיטיבי היא שהטיפוס של הביטוי
הביטוי
א.
ב.
ג.
ד.
ה.
ו.
כאשר אין ב
למשל דוגמה לשימוש בכללי גזירה לפי הקשר הטפסה:
נראה דוגמה נוספת על
נרצה לראות ש
בטיחות סוג ותקינות סוג הן המידה שבה שפת תכנות מרתיעה או מונעת שגיאות סוג. שפות תכנות שונות מגדירות איך לטפל בסתירות או בעיות בהגדרות הסוגים בשפה. כאשר נתקלים בבעיה שכזו, היא תוכל לגרור למשל שגיאה, אזהרה או פשוט התעלמות מהבעיה.
אכיפת סוגים יכולה להיות סטטית, כלומר, לתפוס שגיאות פוטנציאליות בזמן הידור, או דינמית, כלומר לשייך מידע על הסוגים השונים לערכים בזמן ריצה ולהשתמש בסוגים הקיימים לפי הצורך כדי לזהות שגיאות קרובות, או שילוב של שתיהן. אכיפה דינאמית מאפשרת לעיתים קרובות להפעיל תוכניות שיהיו לא חוקיות במסגרת אכיפה סטטית.
Well-typed programs cannot "go wrong". Robin milner
הגדרה:
ביטוי
הגדרה:
נכתוב
משפט:
אם
הוכחה:
ראשית נגדיר את:
למת ההתקדמות: נניח ש
למת השימור: אם
כעת מכיוון ש
בסיס-
צעד-
הוכחת למת ההתקדמות:
באינדוקציה על הגזירה של
בסיס: אם הכלל הוא
לא יתכן שהכלל הוא
צעד: עבור
אם
א.
ב. אם ניתן להתקדם מ
ג. אם שניהם ערכים אז
$$t_{1}=\lambda x: T_{11}.t_{12}$$
עבור
כלומר
עבור
הוכחת למת השימור:
נוכיח באינדוקציה על הגזירה של
בסיס:
1) אם T-true אז t ערך
2) כנ״ל לגבי T-false
3) T-VAR אז t משתנה ולכן
צעד:
1) T-ABS אז t ערך
2) T-IF יהיה דומה ל T-APP
3) T-APP
כך ש
נרצה להוכיח ש
כדי להמשיך את ההוכחה נשתמש ב-
למת ההחלפה:
אם
במילים, זה אומר שאם נבצע החלפה בין משתנים מאותו טיפוס, הביטוי עדיין נשאר באותו טיפוס.
נוכיח באינדוקציה על עץ הגזירה של
בסיס:
1) T-TRUE אז t=true כלומר T=Bool ולכן
2) T-FALSE דומה ל1.
3) T-VAR אז t משתנה. נחלק ל2 מקרים
*
*
צעד:
א) T-APP. אז
נרצה להראות ש
לפי הנחת האינדוקציה מתקיים ש
נפעיל שוב את T-APP ונקבל
שזה בידיוק מה שרצינו להוכיח.
ב) T-ABS :
כאשר נסמן
לפי הנחת האינדוקציה מתקיים
ג) T-IF דומה.
כעת נוכל לחזור להוכחה של למת השימור רצינו להראות ש:
והסברנו ש
מלמת ההחלפה די להראות
א)
ב)
הסעיף הראשון ידוע מהנחות הכלל T-APP.
לגבי ב׳ , ידוע ש
ניתן לקבל זאת רק משימוש ב T-ABS
שזה בידיוק מה שרצינו להוכיח.
נגדיר ביטוי t כמנתנרמל אם יש ערך
כעת, משפט הנורמליזציה אומר ש-
אם ב t אין משתנים חופשיים ו
הוכחה:
כדי להוכיח את המשפט נניח שהאסטרטגה היא CBV. כעת, נגדיר עבור טיפוס T את הקבוצה
*
*
דוגמה:
למה: אם
נוכיח באינדוקציה על T.
בסיס:
T=Bool :
צעד:
יהי
כעת כדי להוכיח את הטענה הקודמת, נכליל את הטענה ונניח כי
ו
הוכחה:
באינדוקציה על עץ הגזירה. המקרה המעניין היחיד הוא T-ABS כאשר
ברור ש
נשים לב: עבור n=0 קיבלנו מיד את משפט הנורמליזציה לפי הגדרת
המשמעות של משפט זה היא שתחשיב
שימוש חשוב ל typed lambda expression הוא CHI. הרעיון הוא שניתן להוכיח פסוקיות לוגיות בעזרת חישוב בתחשיב למדא עם טיפוסים:
א) ניתן לייצר משתנים בלוגיקה פסוקית כטיפוסים.
ב) הוכחה היא פונקציה.
ג) נייצג אופרטורים לוגיים בעזרת תחשיב למדא על טיפוסים.
ניתן להשתמש בתחשיב למדא כמערכת הוכחה ללוגיקה פסוקית כאשר כל פרדיקט מהווה איזשהו טיפוס.
נסתכל על הביטוי הבא
משפט זה הוא תיאותולוגיה (פסוק שהוא תמיד אמת). ניתן להוכיח שהוא תיאותולוגיה בעזרת בניית פונקציה
כעת נוכל להראות שזה תיאותולוגיה אם נראה עת גזירה שמראה שהטיפוס של