תחשיב למדא

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

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

סימון למדה הוא ביטוי מהצורה f=λx.f(x) כאשר האיבר אחרי הנקודה הוא כלל התאמה שבדרך כלל מוצג כביטוי של x. למשל את הפונקציה של שורש ריבועי נרשום כך: λx.x .

הגדרה:
הביטויים בתחשיב λ מוגדרים באופן הבא:
א) משתנה: x,y,z ... כל משתנה הוא ביטוי.
ב) Abstraction: אם x הוא משתנה ו t1 הוא ביטוי אז λx.t1 הוא ביטוי שנקרא ״אבסטרקציה״ לדוגמה: λx.x+1
ג) Application: אם t1,t2 הם ביטויים אז t1t2 הוא ביטוי ״הפעלה״ לדוגמה: (λx.x+1)3

Note

סוגריים שהושמטו בהפעלות חודרות שמאלה למשל t1t2t3=(t1t2)t3

הגדרה:
בביטוי λx.t כל מופע של x ב t נקרא קשור. x נקרא חופשי אם אינו קשור.
לדוגמה, xy הוא ביטוי שבוא גם x וגם y חופשיים אבל ב λy.xy מתקיים ש x חופשי ו y קשור

קומבינטור

ביטוי ללא משתנים חופשיים נקרא קומבינטור.

הגדרה:
קומבינטור הזהות הוא λx.x ונסמנה id .

כלל אלפה

כלל זה אומר ש λx.f(x)=λy.f(y) ומשמעותו הוא ש למדא הוא כמת לוגי קושר. כלומר, מותר לשנות את השם של המשתנה הקשור, כך עוד מחליפים את שמו לשם של משתנה המופיע חופשית.

כלל בתא

רדוקציית β מוגדרת כך:

(λx.t12)t2t12[xt2]

כאשר t12[xt2] הוא הביטוי המתקבל מ t12 על ידי החלפת כל מופע חופשי של x ב t2 , זה נקרא substitution .

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

נשים לב שמחפשים משתנים חופשיים ב t12 ולא ב λx.t12 . זה עוזר בהמשך כאשר נתעסק עם למדות שמפעילות למדות אחרות.

דוגמה:
(λx.x)yy , (λx.x+3)710

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

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

תכנות בתחשיב למדא

פונקציה עם כמה ארגומנטים מתנהגים כמו בOCaml :

λx.λy.t=λx.(λy.t)

ביטויים בוליאניים
הגדרה: הקומבינטורים tru ו fls מוגדרים כך :

tru=λt.λf.tfls=λt.λf.f

במילים אחרות זאת פונקציה שמקבלת שני ארגומנטים ומחזירה את הראשון עבור tru ועבור fls זאת פונקציה שמקבל שני ארגומנטים ומחזירה את השני.

נוכל להגדיר אופרטורים יותר מורכבים למשל Not=λb.b fls tru
הגדרה:
נגדיר את הקומבינטור test להיות

test=λl.λm.λn.lmn

נראה דוגמה

 test tru v w=(λl.λm.λn.lmn)true v w(λm.λn.tru m n)v w(λn.tru v n)wtru v wv

הפיתוח הוא לפי רדוקציית β עליה דיברנו.

הגדרה:
נגדיר את הקומבינטור and להיות

and=λb.λc.b c fls

נוכח להוכיח בחלוקה למקרים:
אם b=tru :
אם c=true אז מפעילים את הפונקציה tru tru fls ונקבל מהגדרה tru.
אם c=false אז נריץ את הפונקציה tru fls fls ומהגדרה יוחזר fls.

המקרה השני כמובן תמיד יחזיר false.

באופן דומה :

Or=λb.λc.b tru c

הגדרה:
זוגות סדורים

pair = λf.λs.λb.b f sfirst=λp.p trusnd = λp.p fls

Screenshot 2024-02-13 at 11.51.44.png

pair זאת פונקציה שמקבל שני ארגומנטים ומחזירה פונקציה בוליאנית. אם הקלט אליה הוא tru אז היא תחזיר את f , אחרת היא תחזיר את s. הפונקציות האחרות הן דרך לשלוף את האיבר הראשון/ השני בהתאמה מהזוג הסודר. למשל כדי להוציא את האיבר הראשון נריץ first pair .

הגדרה:
מספרים טבעיים
הקומבינטורים c0,c1, מוגדרים כך

c0=λs.λz.zc1=λs.λz.szc2=λs.λz.s(sz)c3=λs.λz.s(s(sz))

בעצם s היא פונקציית successor ו z זאת פונקציית האפס ועבור cn מפעילים את פונקציית הsuc n פעמים.

הבחנה

הייצוג של 0 הוא בידיוק כמו הייצוג של fls ולכן יש שפות תכנות שבהן 0 מוגדר כ False

נגדיר את פונקציית הsucc : succ=λn.λs.λz.s(n s z)

בעצם היא מפעילה את s על הפלט מהפעלת s, n פעמים על z.

אריתמטיקה:

plus=λm.λn.λs.λz. m s(n s z)

כאשר m,n אמורים להיות בפועל הקומבינטורים ci,cj כלשהם.

times=λm.λn.m(plus n)c0pow=λmλn.m(times n )c1

מה זה plus n ? זאת פונקציה שמקבלת מספר כלשהו ומוסיפה לו את n.

את הפונקציה הזאת מפעילים m פעמים עם הקלט c0 וסך הכל מקבלים mn .

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

iszero=λm.m(λx.false)tru

Pasted image 20240213121445.png

אם m=c0 אז הפונקציה תקבל c0 תקבל שני ארגומנטים ותחזיר את השני ולכן במקרה הזה מתקבל tru. כל מקרה אחר יריץ את פונקצית ה succ במקרה הזה λx.false שאפשר לראות שלכל n היא מחזירה fls בסוף.

פתרון לבעיית פיבונצ׳י בתחשיב למדא
הגדרה:

zz=pair c0 c0ss=λp.pair(snd p)(plus c1(snd p))prd=λm.fst(m ss zz)

zz מגדיר את הזוג הסדור c0,c0 ו ss מקבלת זוג סדור p ומחזירה זוג סדור כך ש האיבר הראשון הוא האיבר השני בp והאיבר השני הוא האיבר השני בp פלוס 1.
כעת הפונקציה prd עושה את החישוב המלא כאשר היא מחזירה את האיבר הראשון מזוג סדור המתקבל מהרצת ss m פעמים עם הקלט ההתחלתי zz.

כעת נוכל להגדיר קומבינטורים אריתמטיים נוספים

sub=λm.λn.n prd m

כמו כן נוכל להגדיר שיוויון על ידי

equal=λm.λn.and(iszero(sub m n)(iszero(sub m m)))

הסיבה שלעשות רק iszero(sub m n) לא יעבור היא שהמספרים השליליים לא מוגדרים עבור sub ולכן זה פשוט יצא עבור מספרים שליליים.

לולאה אינסופית

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

לפני שנגדיר נשים לב שעבור הקומבינטור

Ω=(λx.x x)(λx.x x)

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

בעיית העצירה

נזכיר ש בעיית העצירה היא בעיה NPH שמתארת פונקציה וארגומנט ובודקת אם היא עוצרת (מחזירה tru אם עוצרת ו fls אם נתקעת):

Halt=λf.λx./*if f Halt on x /* True False

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

isNotHalting:=λf.Halt(f,f) (Ω) True

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

כעת נשאל, מה הערך שיוחזר עבור Halt(IsNotHalting  isNotHalting) ?

נניח שצריך לחזור אמת כלומר Halt הכריעה שהפונקציה עוצרת על עצמה אבל :

Pasted image 20240213144107.png
בסתירה לכך שחזר true

נניח שזה מחזיר false :
Screenshot 2024-02-13 at 14.46.21.png
בסתירה לכך ש Halt החזירה false

המסקנה היא שלא קיימת פונקציה Halt.

רקורסיה

כעת נגדיר את הקומבינטור Y

rec=Y=λf.(λx.f(x x))(λx.f(x x))

למה זה בעצם מהווה רקורסיה?
Screenshot 2024-02-13 at 15.58.30.png
בעצם Y מאפשר לנו להעביר כקלט פונקציה אחרת ולהפעיל אותה בצורה אינסופית. על הפונקציה שמעבירים לקבוע מה יהיה התנאי העצירה של הרקורסיה.

למשל נרצה לחשב את פונקציית העצרת: בצורה שהיא לא תחשיבית
למדא טהורה זה היה נראה מהצורה fact=λf.λn. if n =1 then 1 else n * f n-1 .

בצורה שמבוססת רק על תחשיב למדא זה היה נראה כך :

λf.λn. test (realeq n c1)c1(times n f pred n)

Screenshot 2024-02-13 at 16.13.27.png

Substitution

יהי t ביטוי. קבוצת המשתנים החופשיים של t מסומנת ב Fv(t) ומוגדרת כך

Fv(x)={x}Fv(λx.t)=Fv(t){x}Fv(t1,t2)=Fv(t1)Fv(t2)

משתנה שמופיע ב t נקרא חופשי בt אם הוא שייך ל Fv(t) וקשור אחרת.

כעת נוכל להגדיר את כללי ה substitution t[xs] :

x[xs]=sy[xs]=y(λy.t1)[xs]=λy.(t1[xs])(t1 t2)[xs]=(t1[xs])(t2[xs])

את (λy.t1)[xs]=λy.(t1[xs]) מותר לי לעשות רק אם y משתנה שונה מ x ו yFv(s)
נשים לב שבלי הדרישה הראשונה נקבל

(λx.x)[xy]=λx.y

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

בלי הדרישה השנייה נקבל

(λy.x)[xy]=λy.y

כאן הפכנו ביטוי שאינו קומבינטור לקומבינטור.

בצורה יותר מורחבת נקבל:
Pasted image 20240325180406.png

סמנטיקה לא דטרמינסטית

הסמנטיקה של רדוקציית בטא היא לא דטרמינסטית למשל :

Pasted image 20240213161745.png

Pasted image 20240213161843.png

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

הגדרה: ביטוי מהצורה (λ x.t1)t2 נקרא רדקס. לעומת זאת, לביטוי מהצורה λx.t  קוראים אבסטרקציה.

(λ x.t)abstract sredex

כמו כן כללי הרדוקציה הקיימים בצורה פורמלית הם
Pasted image 20240213162521.png
הראשון אומר ש אם ניתן לבצע רדוקציית בטא מ t1 ל t1 אז הרדוקציית בטא קודם כל תהיה t1t2t1t2 כלומר לבצע קודם כל על t1 אם ניתן.

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

השלישי אומר שאם מדובר ב״רדקס״ אז מחזירים את t1 עם evaluation של xv2 .

evaluation strategies

כעת ננסה להבין כיצד לקרוא ביטוי מהצורה (x y)(z w): מספר אסטרטגיות אפשריות:
א) רדוקציה מלאה - אין מגבלות.
ב) סדר נורמלי - מתחילים תמיד עם הרדקס השמאלי ביותר (החיצוני ביותר).
Pasted image 20240211222227.png
ג) call by name - כמו סדר נורמלי אבל לא עושים רדוקצית בטא בתוך אבסטרקציות. כלומר קודם מבצעים השמה ורק אם צריך מחשבים את הביטוי (קדימות ל E-AppAbs)
Pasted image 20240211222239.png
ד) call by value - מותר לבצע רדוקציית בטא על רדקס רק אם צד ימין של הרדקס הוא אבסטרקציה או משתנה. אסור לבצע רדוקציות בתוך אבסטרקציות. כלומר מבצעים השמה רק עבור משתנים ולא ביטויים. (קדימות ל E-App)
Pasted image 20240211222259.png

Pasted image 20240227135047.png
ניתן לראות כיצד ב call by value קודם חישבו את ההפעלה ״הפנימית״ ואז החליפו בהפעלה ״החיצונית״ בעוד ש ב call by name קודם ביצעו השמה באבסטרקצייה החיצונית ואז חישבו את ההפעלה הפנימית.

תחשיב למדא עם טיפוסים

בכל השפות תכנות יש תמיכה בטיפוסים. למשל בOCaml על משנה let x = 5 לא נוכל להתייחס כפונקציה. ויש מקרים מורכבים יותר שהקומפילר מצליח לזהות למשל קלט לא תקין לפונקציה.

הקומפילר למעשה לא מריץ התוכנית כדי לגלות את השגיאות הללו. אלא הוא מייצר Abstract Syntax Tree בשלב הראשון ולאחר מכן מבצע Type Checking ו (ישנן שפות כמו אוקמל שעושות גם Type Inference).

נרצה להבין כיצד קומפיילרים מבצעים את הבדיקת טיפוסים הזאת
נשים לב ש type inference זה פיצ׳ר יותר מתקדם מ type checking ולא נתעסק בו כאן.

הגדרה:
קבוצת הטיפוסים מוגדרת כך:
א. Bool
ב. אם T ו S טיפוסים אז TS הוא גם טיפוס.

עם ההגדרות האלה ניתן להרחיב את ההגדרות שלנו לטיפוסים נוספים למשל מספרים טבעיים
נגדיר T:==Nat עם החוקים הבאים :
Pasted image 20240215132636.png|300
נשים לב שזה הגדרה לא מדויקת שכן pred 0 לא משאיר אותנו בטבעיים אבל זה בסדר כי אנחנו יכולים גם להגדיר ש pred 0=0 אם נרצה. כעת נוכל להשתמש בכללי גזירה בסיסיים על הטיפוסים האלה

הגדרה:
ביטויים בתחשיב λ עם טיפוסים מוגדרים כך:
א. כל משתנה הוא ביטוי.
ב. אם x משתנה , T טיפוס ו t ביטוי אז λx:T.t הוא ביטוי.
ג. אם t1,t2 הם ביטויים אז גם t1t2
ד. true, false הם ביטויים.
ה. אם t1,t2,t3 הם ביטויים אז גם if t1 then t2 else t3 .

דוגמה: נסמן id=λx:Bool.x ונגדיר
f=λx:BoolBool.λy:Bool xy
זאת פונקציה שמקבלת ״שני ארגומנטים״ שמפעילה אותם אחד על השני ומחזירה את הפלט הבוליאני (f=test).

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

הגדרה:
evaluation מוגדר להיות

(λx:T1.t2)v2t2[xv2]if true then t1 else t2t1if false then t1 else t2t2

דוגמה:
נמשיך להסתכל על הפונקציות מהדוגמה הקודמת.

f id truetruef true id(λx:BoolBool.λy:Bool.x y)(λy:Bool true y)idtrue id

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

הגדרה:
ביטוי ללא משתנים חופשיים נקרא ערך אם הוא true,false,λx:T.t

הקשרי הטפסה ועצי טיפוסים

הגדרה:
הקשר הטפסה הוא קבוצה של היגדים מהצורה x:T כך x משתנה ו T טיפוס.

נגדיר כעת את יחס ההטפסה להיות

Γt:T

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

הביטוי t:T משמעותו שביטוי t הוא מטיפוס T בסביבה הריקה כלומר בלי הנחות מוקדמות.

Pasted image 20240215152454.png

מוגדר לפי הכללים הבאים :
א. Tvar : x:TΓΓx:T

ב. TTrue Γtrue:Bool

ג. TFlase Γfalse:Bool

ד. TIF Γt1:Bool  Γt2:T  t3:TΓif t1 then t2 else t3:T

ה. TAPP Γt1:T11T12 Γt2:T11Γt1t2:T12

ו. TABS Γ,x:T1t2:T2Γ(λx:T1.t2):T1T2
כאשר אין בΓ ביטוי מהצורה λx... במצב זה מפעילים כלל אלפה. הסימון Γ,x:T1 שקול ל Γ{x:T1} .

למשל דוגמה לשימוש בכללי גזירה לפי הקשר הטפסה:
Pasted image 20240215132951.png
Pasted image 20240215153028.png
Pasted image 20240215153201.png

נראה דוגמה נוספת על id,f שהגדרנו למעלה.
נרצה לראות ש f id true:Bool וגם שאין T המקיים f true id:T .

Pasted image 20240215153556.png

Pasted image 20240215153948.png

Type Safety

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

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

Well-typed programs cannot "go wrong". Robin milner

הגדרה:
ביטוי t נקרא מטופס היטב (well-typed program) אם Γt:T עבור T,Γ כלשהם.
הגדרה:
נכתוב tt אם t=t1t2t .

משפט:
אם t ביטוי ללא משתנים חופשיים ומטופס היטב, ו tt אז t הוא ערך או שיש t כך ש tt .

הוכחה:
ראשית נגדיר את:

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

למת השימור: אם Γt:T ו tt ו t אינו ערך אז Γt:T
כעת מכיוון ש tt אז יש t=t1tn=t . נוכיח באינדוקציה על n ש tn הוא ערך או שאפשר להתקדם ממנו וכן ש tn מטופס היטב.

בסיס- n=1 אז t=t ולכן מטופס היטב ולפי למת ההתקדמות או שהוא ערך או שאפשר להתקדם ממנו.

צעד- n>1 ו t=tn . אם tn הוא ערך אז סיימנו כי כל הערכים מטופסים היטב. אחרת, לפי הגדרת האינדוקציה tn1 מטופס היטב ומלמת השימור מתקיים tn מטופס היטב. כמו כן מלמת ההתקדמות מתקיים שניתן להתקדם ממנו.

הוכחת למת ההתקדמות:
באינדוקציה על הגזירה של Γt:T (קיימים כאלה כי t מטופס היטב).

בסיס: אם הכלל הוא TTrue או TFalse אז t הוא ערך.
לא יתכן שהכלל הוא TVar כי ב t אין משתנים חופשיים.

צעד: עבור TABS אז t הוא אבסטרקציה ולכן ערך.
אם TAPP אז t=t1t2 עבור t1,t2 כלשהם. לפי הכלל מתקיים ששניהם מטופסים היטב ולפי הנחת האינדוקציה או שהם ערכים או שניתן להתקדם מהם לפי המקרים הבאים
א. t1 ניתן להתקדם ל t1 ובמקרה כזה t1t2t1t2
ב. אם ניתן להתקדם מ t2 זה כמו סעיף א.
ג. אם שניהם ערכים אז ΓT11T12 לפי הכלל. לכן
$$t_{1}=\lambda x: T_{11}.t_{12}$$
עבור x,t12 כלשהם.

כלומר t1t2=(λx.T11:t12)t2t12[xt2] וכך התקדמנו.

עבור TIF נקבל הוכחה דומה.

הוכחת למת השימור:
נוכיח באינדוקציה על הגזירה של Γt:T .
בסיס:
1) אם T-true אז t ערך
2) כנ״ל לגבי T-false
3) T-VAR אז t משתנה ולכן t t .

צעד:
1) T-ABS אז t ערך
2) T-IF יהיה דומה ל T-APP
3) T-APP

Γt1:T11T12   Γt2:T11Γt1t2:T12

כך ש t=t1t2 וגם t1=λx:T11.t11 ולכן t=t11[xt2] .

נרצה להוכיח ש Γt11[xt2]:T כיוון שהנחת האינדוקציה שלנו היא על t1 ולא על t11 ולכן נתקענו כרגע.

כדי להמשיך את ההוכחה נשתמש ב-

למת ההחלפה:
אם Γ,x:St:T וגם Γs:S אז Γt[xs]:T .
במילים, זה אומר שאם נבצע החלפה בין משתנים מאותו טיפוס, הביטוי עדיין נשאר באותו טיפוס.

נוכיח באינדוקציה על עץ הגזירה של Γ,x:St:t .
בסיס:
1) T-TRUE אז t=true כלומר T=Bool ולכן t[xs]=true ולכן מתקבל הדרוש.
2) T-FALSE דומה ל1.
3) T-VAR אז t משתנה. נחלק ל2 מקרים
* t=x אז S=T ו t[xs]=s. וכבר ידוע ש Γs:S .
* t משתנה אחר ואז t[xs]=t ואז נפעיל את T-VAR ונקבל את הדרוש.

צעד:
א) T-APP. אז Γ,x:St1:T11T12  Γ.x:St2T11Γ,x:St1t2:T12

נרצה להראות ש Γ(t1t2)[xs]:T12 כלומר Γt1[xs]t2[xs]:T12.

לפי הנחת האינדוקציה מתקיים ש Γt1[xs]:T11T12 וגם Γt2[xs]:T11

נפעיל שוב את T-APP ונקבל

Γt1[s]:T11T12  Γt2[xs]:T11Γt1[xs]t2[xs]:T12

שזה בידיוק מה שרצינו להוכיח.

ב) T-ABS :

Γ,x:S,y:T1t2:T2Γ,x:Sλy:T1.t2:T1T2

כאשר נסמן t=λy:T1.t2 ו T=T1T2 .
לפי הנחת האינדוקציה מתקיים Γ,y:T1t2[xs]:T2 אם נפעיל את T-ABS כעת נקבל את הדרוש.

Γ,y:T1t2[xs]:T2Γλy:T1.t2[xs]:T1T2

ג) T-IF דומה.

כעת נוכל לחזור להוכחה של למת השימור רצינו להראות ש: Γt11[xt2]:T לאחר שהפעלנו T-APP:

Γt1:T11T12   Γt2:T11Γt1t2:T12

והסברנו ש t=t1t2 וגם t1=λx:T11.t11 ולכן t=t11[xt2] .

מלמת ההחלפה די להראות
א) Γt2:T11
ב) Γ,x:T11t11:T12

הסעיף הראשון ידוע מהנחות הכלל T-APP.
לגבי ב׳ , ידוע ש Γt1:T11T12

ניתן לקבל זאת רק משימוש ב T-ABS

Γ,x:T11t11:T12Γλx:T11.t11:T11T12

שזה בידיוק מה שרצינו להוכיח.

נורמליזציה

נגדיר ביטוי t כמנתנרמל אם יש ערך t כך ש tt .
כעת, משפט הנורמליזציה אומר ש-
אם ב t אין משתנים חופשיים ו t:T אז t מתנרמל.

הוכחה:
כדי להוכיח את המשפט נניח שהאסטרטגה היא CBV. כעת, נגדיר עבור טיפוס T את הקבוצה RT באופן הבא:
* RBool היא קבוצת כל הביטויים מטיפוס Bool שמתנרמלים
* RT1T2 היא קבוצת כל הביטויים t מטיפוס T1T2 שמתנרמלים ובנוסף לכל ביטוי sRT1 מתקיים ש tsRT2 .

דוגמה:
(λx:Bool.x)true:RBool

למה: אם Γt:T ו tt אז tRTtRT
נוכיח באינדוקציה על T.
בסיס:
T=Bool :
נניח tRBool . מלמת השימור t:Bool . מכיוון שאנו בCBV ו t מתנרמל, גם t מתנרמל
דומה (פשוט יותר)

צעד:
T=T1T2
נניח ש tRT כמו קודם, ברור ש t מתנרמל ומטיפוס T.
יהי sRT2. נרצה להוכיח ש tsRT2 .
tRT ולכן tsRT2. מכיוון ש tt אז גם tsts הטיפוס של שניהם הוא T2 ולפי הנחת האינדוקציה כיוון ש tsRT נקבל שכך גם ts
דומה.

כעת כדי להוכיח את הטענה הקודמת, נכליל את הטענה ונניח כי

x1:T1,,xn:Tnt:T

ו v1vn ערכים מטיפוסים Ti בהתאמה כך שלכל i מתקיים viRTi אז

t:=t[x1v1][x2v2][xnvn]RT

הוכחה:
באינדוקציה על עץ הגזירה. המקרה המעניין היחיד הוא T-ABS כאשר t=λx:S1.t2.

ברור ש t ערך ולכן מתנרמל. יהי sRS1. נוכיח ש tsRS2. מהגדרת RS1 sv עבור v ערך כלשהו. מהלמה הקודמת vRS1 ולפי האנחת האינדוקציה t2[][]RS2 אבל זה בידיוק מה שנקבל מ ts.

נשים לב: עבור n=0 קיבלנו מיד את משפט הנורמליזציה לפי הגדרת RT

הבחנה

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

Curry Howard Isomorphism

שימוש חשוב ל typed lambda expression הוא CHI. הרעיון הוא שניתן להוכיח פסוקיות לוגיות בעזרת חישוב בתחשיב למדא עם טיפוסים:
א) ניתן לייצר משתנים בלוגיקה פסוקית כטיפוסים.
ב) הוכחה היא פונקציה.
ג) נייצג אופרטורים לוגיים בעזרת תחשיב למדא על טיפוסים.

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

Screenshot 2024-02-15 at 15.57.26.png|300

נסתכל על הביטוי הבא

P(QR)(PQ)(PR)

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

f:P+(Q×R)(P+Q)×(P+R)

כעת נוכל להראות שזה תיאותולוגיה אם נראה עת גזירה שמראה שהטיפוס של f הוא אמת.