Դինամիկ մուտքագրում գ. Ստատիկ և դինամիկ մուտքագրում: Դինամիկ մուտքագրված լեզուներ



Այս հոդվածում խոսվում է ստատիկ տիպային և դինամիկ տպագրված լեզուների միջև եղած տարբերության մասին, քննարկվում են «ուժեղ» և «թույլ» մուտքագրման հասկացությունները և համեմատում տարբեր լեզուների տիպային համակարգերի հզորությունը: Վերջերս հստակ շարժում է նկատվել դեպի ծրագրավորման ավելի ուժեղ և հզոր տիպի համակարգեր, ուստի կարևոր է հասկանալ, թե ինչ հարցականի տակտեսակների և մուտքագրման մասին խոսելիս.



Տեսակը հնարավոր արժեքների հավաքածու է: Ամբողջ թիվը կարող է ունենալ 0, 1, 2, 3 և այլն արժեքներ: Բուլյան կարող է լինել ճշմարիտ կամ կեղծ: Դուք կարող եք գալ ձեր սեփական տեսակը, օրինակ, «DayFive» տեսակը, որում հնարավոր են «տալ» և «5» արժեքները, և ուրիշ ոչինչ: Դա տող կամ թիվ չէ, դա նոր, առանձին տեսակ է։


Ստատիկ տպագրված լեզուները սահմանափակում են փոփոխականների տեսակները. ծրագրավորման լեզուն կարող է իմանալ, օրինակ, որ x-ը ամբողջ թիվ է: Այս դեպքում ծրագրավորողին արգելվում է անել x = true, դա սխալ կոդ կլինի: Կոմպիլյատորը կհրաժարվի այն կազմելուց, ուստի մենք չենք կարող նույնիսկ գործարկել այս կոդը: Ստատիկ կերպով տպագրված մեկ այլ լեզու կարող է ունենալ տարբեր արտահայտչական հնարավորություններ, և ոչ մի հանրաճանաչ տիպային համակարգ չի կարող արտահայտել մեր բարձր հինգ տեսակը (սակայն շատերը կարող են արտահայտել այլ, ավելի բարդ գաղափարներ):


Դինամիկ տպագրված լեզուները արժեքները նշում են տեսակներով. լեզուն գիտի, որ 1-ը ամբողջ թիվ է, 2-ը՝ ամբողջ թիվ, բայց չի կարող իմանալ, որ x-ը միշտ պարունակում է ամբողջ թիվ:


Լեզվի գործարկման ժամանակը ստուգում է այս պիտակները տարբեր ժամանակներում: Եթե ​​մենք փորձենք ավելացնել երկու արժեք, այն կարող է ստուգել՝ արդյոք դրանք թվեր են, տողեր կամ զանգվածներ։ Այնուհետև նա կավելացնի այս արժեքները, դրանք կսոսնձի կամ սխալ կտա՝ կախված տեսակից:

ստատիկ տպագրված լեզուներ

Ստատիկ լեզուները ծրագրում ստուգում են տեսակները կոմպիլյացիայի ժամանակ, նախքան ծրագիրը նույնիսկ գործարկվելը: Ցանկացած ծրագիր, որտեղ տիպերը խախտում են լեզվի կանոնները, համարվում է վատ ձևավորված։ Օրինակ, ստատիկ լեզուների մեծ մասը կմերժի «a» + 1 արտահայտությունը (C-ն բացառություն է այս կանոնից): Կազմողը գիտի, որ «a»-ն տող է, իսկ 1-ը՝ ամբողջ թիվ, և որ +-ն աշխատում է միայն այն դեպքում, երբ ձախ և աջ մասերը նույն տիպի են։ Այսպիսով, նա կարիք չունի գործարկել ծրագիրը, որպեսզի հասկանա, որ խնդիր կա: Ստատիկ տպագրված լեզվի յուրաքանչյուր արտահայտություն հատուկ տեսակի է, որը կարող է որոշվել առանց գործարկման կոդի:


Ստատիկ տպագրված շատ լեզուներ պահանջում են նշել այդ տեսակը: Java public int add(int x, int y) ֆունկցիան վերցնում է երկու ամբողջ թիվ և վերադարձնում երրորդ ամբողջ թիվը։ Ստատիկ տպագրված այլ լեզուները կարող են ինքնաբերաբար որոշել տեսակը: Հասկելում նույն գումարման ֆունկցիան այսպիսի տեսք ունի՝ ավելացնել x y = x + y: Մենք լեզվին չենք ասում տեսակները, բայց նա կարող է ինքն իրեն սահմանել, քանի որ գիտի, որ +-ն աշխատում է միայն թվերի վրա, ուստի x և y-ը պետք է թվեր լինեն, ուստի ավելացնել ֆունկցիան ընդունում է երկու թիվ որպես արգումենտ։


Սա չի նվազեցնում տիպային համակարգի «ստատիկ» բնույթը: Haskell-ի տիպային համակարգը հայտնի է ստատիկ, խիստ և հզոր լինելու համար, և այս բոլոր ճակատներում Haskell-ը առաջ է Java-ից:

Դինամիկ մուտքագրված լեզուներ

Դինամիկ տպագրված լեզուները ձեզանից չեն պահանջում տեսակ նշել, բայց դրանք նույնպես չեն սահմանում: Փոփոխական տեսակները հայտնի չեն, քանի դեռ գործարկման ժամանակ նրանք չունեն հատուկ արժեքներ: Օրինակ՝ ֆունկցիա Python-ում


def f(x, y): վերադարձ x + y

կարող է ավելացնել երկու ամբողջ թիվ, միացնել տողեր, ցուցակներ և այլն, և մենք չենք կարող հստակ պարզել, թե ինչ է կատարվում, քանի դեռ չենք գործարկել ծրագիրը: Միգուցե f-ն ինչ-որ պահի կանչվի երկու տողով, իսկ մեկ այլ կետում՝ երկու թվով: Այս դեպքում x և y արժեքները կպարունակեն տարբեր տեսակներտարբեր ժամանակներում: Հետևաբար, դինամիկ լեզուներում արժեքներն ասում են, որ ունեն տեսակ, բայց փոփոխականներն ու ֆունկցիաները չունեն: 1 արժեքը հաստատ ամբողջ թիվ է, բայց x-ը և y-ն կարող են լինել ցանկացած բան:

Համեմատություն

Դինամիկ լեզուների մեծամասնությունը սխալ կթողնի, եթե տիպերը սխալ օգտագործվեն (JavaScript-ը տխրահռչակ բացառություն է. այն փորձում է արժեք վերադարձնել ցանկացած արտահայտության համար, նույնիսկ երբ դա իմաստ չունի): Դինամիկ տպագրված լեզուներ օգտագործելիս արտադրական միջավայրում կարող է առաջանալ նույնիսկ պարզ «a» + 1 սխալ: Ստատիկ լեզուները կանխում են նման սխալները, բայց, իհարկե, կանխարգելման աստիճանը կախված է տիպային համակարգի հզորությունից:


Ստատիկ և դինամիկ լեզուները կառուցված են ծրագրի ճիշտության վերաբերյալ սկզբունքորեն տարբեր գաղափարների վրա: Դինամիկ լեզվով «a» + 1-ը վավեր ծրագիր է. կոդը կաշխատի, և գործարկման ժամանակ սխալ կառաջանա: Այնուամենայնիվ, ստատիկ տպագրված լեզուների մեծ մասում «a» + 1 արտահայտությունն է ոչ ծրագիր: այն չի կազմվի և չի գործարկվի։ Դա վավեր կոդ չէ, ինչպես պատահական նիշերի մի խումբ:&%^@*&%^@* վավեր կոդ չէ: Կոռեկտության և սխալի այս լրացուցիչ հասկացությունը դինամիկ լեզուներում համարժեք չունի:

Ուժեղ և թույլ մուտքագրում

«Ուժեղ» և «թույլ» հասկացությունները շատ երկիմաստ են։ Ահա դրանց օգտագործման մի քանի օրինակներ.

    Երբեմն «ուժեղ» նշանակում է «ստատիկ»:
    Դա պարզ է, բայց ավելի լավ է օգտագործել «ստատիկ» տերմինը, քանի որ մարդկանց մեծ մասն այն օգտագործում և հասկանում է:

    Երբեմն «ուժեղ» նշանակում է «չի ենթադրում տեսակի փոխակերպում»:
    Օրինակ, JavaScript-ը թույլ է տալիս գրել «a» + 1, որը կարելի է անվանել «թույլ մուտքագրում»: Բայց գրեթե բոլոր լեզուներն ապահովում են անուղղակի փոխակերպման որոշակի մակարդակ, որը թույլ է տալիս ավտոմատ կերպով փոխարկել ամբողջ թվերից լողացող կետ թվերի, ինչպիսիք են 1 + 1.1: Իրականում մարդկանց մեծամասնությունն օգտագործում է «ուժեղ» բառը՝ ընդունելի և անընդունելի փոխակերպումների սահմանը սահմանելու համար: Ընդհանրապես ընդունված սահման չկա, դրանք բոլորն էլ ճշգրիտ չեն և կախված են կոնկրետ մարդու կարծիքից։

    Երբեմն «ուժեղ»-ը նշանակում է, որ անհնար է շրջանցել լեզվի տպագրության խիստ կանոնները:

  • Երբեմն «ուժեղ» նշանակում է հիշողության համար անվտանգ:
    C-ն հիշողության համար ոչ անվտանգ լեզվի օրինակ է: Եթե ​​xs-ը չորս թվերից կազմված զանգված է, ապա C-ն ուրախությամբ կկատարի xs կամ xs՝ վերադարձնելով հիշողությունից որոշակի արժեք xs-ից անմիջապես հետո:

Եկեք կանգ առնենք. Ահա թե ինչպես են որոշ լեզուներ համապատասխանում այս սահմանումներին: Ինչպես տեսնում եք, միայն Հասկելն է բոլոր առումներով հետևողականորեն «ուժեղ»: Լեզուների մեծ մասը այնքան էլ պարզ չէ:



(«Երբ որպես» սյունակում «Անկանխատեսելի փոխարկումներ» նշանակում է, որ ուժեղ և թույլ բաժանումը կախված է նրանից, թե ինչպիսի փոխարկումներ ենք մենք ընդունելի համարում):


Հաճախ «ուժեղ» և «թույլ» տերմինները վերաբերում են վերը նշված տարբեր սահմանումների չսահմանված համակցությանը, և այստեղ չցուցադրված այլ սահմանումներ: Այս ամբողջ խառնաշփոթը գործնականում անիմաստ է դարձնում «ուժեղ» և «թույլ» բառերը։ Երբ ցանկանում եք օգտագործել այս տերմինները, ավելի լավ է նկարագրել, թե կոնկրետ ինչ է նշանակում: Օրինակ, կարող եք ասել «JavaScript-ը վերադարձնում է արժեք, երբ տողը ավելացվում է թվին, բայց Python-ը վերադարձնում է սխալ»։ Այդ դեպքում մենք մեր էներգիան չենք վատնի՝ փորձելով համաձայնության գալ «ուժեղ» բառի բազմակի իմաստների շուրջ։ Կամ, նույնիսկ ավելի վատ՝ տերմինաբանության պատճառով մենք հայտնվում ենք չլուծված թյուրիմացությունների մեջ:


Շատ դեպքերում համացանցում «ուժեղ» և «թույլ» տերմինները կոնկրետ մարդկանց անորոշ և վատ սահմանված կարծիքներ են: Դրանք օգտագործվում են լեզվին «վատ» կամ «լավ» անվանելու համար, և այս կարծիքը վերածվում է տեխնիկական ժարգոնի։



Ուժեղ մուտքագրում. Տիպային համակարգ, որը ես սիրում և հարմարավետ եմ զգում:

Թույլ մուտքագրում. Տիպային համակարգ, որն ինձ անհանգստացնում է կամ ինձ հարմար չէ:

Աստիճանական մուտքագրում

Հնարավո՞ր է դինամիկ լեզուներին ստատիկ տիպեր ավելացնել: Որոշ դեպքերում՝ այո։ Մյուսների դեպքում դա դժվար է կամ անհնար: Առավել ակնհայտ խնդիրը eval-ի և այլ նմանատիպ դինամիկ լեզվական հատկանիշների հետ է: Python-ում 1 + eval("2") անելը տալիս է 3: Բայց ի՞նչ է տալիս 1 + eval(read_from_the_network()): Դա կախված է նրանից, թե ինչ է ցանցում կատարման պահին: Եթե ​​ստանում ենք թիվ, ապա արտահայտությունը ճիշտ է։ Եթե ​​լար, ապա ոչ: Գործարկումից առաջ իմանալու ոչ մի միջոց չկա, ուստի անհնար է վերլուծել տեսակը ստատիկ կերպով:


Գործնականում անբավարար լուծում է eval() արտահայտությունը սահմանել Any-ի տիպի, որը նման է Object-ին որոշ օբյեկտի վրա հիմնված ծրագրավորման լեզուներում կամ ինտերֆեյսի() Go-ում. սա մի տեսակ է, որը բավարարում է ցանկացած արժեք:


Any տիպի արժեքները ոչնչով սահմանափակված չեն, ուստի տիպային համակարգը մեզ ոչ մի կերպ չի կարող օգնել էվալ կոդով: Լեզուները, որոնք ունեն և՛ eval, և՛ տիպային համակարգ, պետք է հրաժարվեն տիպի անվտանգությունից ամեն անգամ, երբ օգտագործվում է eval-ը:


Որոշ լեզուներ ունեն ընտրովի կամ աստիճանական մուտքագրում. դրանք լռելյայն դինամիկ են, բայց թույլ են տալիս ավելացնել որոշ ստատիկ ծանոթագրություններ: Python-ը վերջերս ավելացրել է կամընտիր տեսակներ; TypeScript-ը JavaScript-ի հավելում է, որն ունի ընտրովի տեսակներ; Flow արտադրում է ստատիկ վերլուծությունլավ հին JavaScript կոդը:


Այս լեզուները ապահովում են ստատիկ մուտքագրման որոշ առավելություններ, բայց դրանք երբեք չեն տրամադրի այն բացարձակ երաշխիքը, որ իսկապես ստատիկ լեզուներն են տալիս: Որոշ գործառույթներ ստատիկ կերպով մուտքագրվելու են, իսկ որոշները՝ դինամիկ: Ծրագրավորողը միշտ պետք է իմանա և զգուշանա տարբերությունից:

Ստատիկ մուտքագրված կոդի կազմում

Երբ ստատիկ տպագրված կոդը կազմվում է, նախ ստուգվում է շարահյուսությունը, ինչպես ցանկացած կոմպիլյատորում: Այնուհետեւ ստուգվում են տեսակները։ Սա նշանակում է, որ ստատիկ լեզուն կարող է նախ բողոքել մեկ շարահյուսական սխալի մասին, իսկ այն շտկելուց հետո բողոքել 100 տպագրական սխալների մասին։ Շարահյուսական սխալի շտկումը չստեղծեց այդ 100 մուտքագրման սխալները: Կազմողն ուղղակի տիպային սխալները հայտնաբերելու միջոց չուներ, քանի դեռ շարահյուսությունը չի ուղղվել:


Ստատիկ լեզվի կոմպիլյատորները սովորաբար կարող են ավելին ստեղծել արագ կոդըքան դինամիկ կոմպիլյատորները: Օրինակ, եթե կոմպիլյատորը գիտի, որ ավելացնել ֆունկցիան ընդունում է ամբողջ թվեր, ապա այն կարող է օգտագործել հայրենի ADD հրահանգը: CPU. Դինամիկ լեզուն կստուգի գործարկման ժամանակ՝ ընտրելով բազմաթիվ ավելացման գործառույթներից մեկը՝ կախված տեսակներից (ավելացնելով ամբողջ թվեր կամ լողացողներ, կամ միացնելով տողեր կամ միգուցե ցուցակներ): Կամ պետք է որոշել, որ սխալ է տեղի ունեցել, իսկ տեսակները՝ ոչ: համընկնում. Այս բոլոր ստուգումները ժամանակ են պահանջում: Դինամիկ լեզուները օպտիմիզացման համար օգտագործում են տարբեր հնարքներ, օրինակ՝ JIT (ճիշտ ժամանակի) կոմպիլյացիան, որտեղ ծածկագիրը վերակոմպիլացվում է գործարկման ժամանակ՝ տեսակների մասին բոլոր անհրաժեշտ տեղեկությունները ստանալուց հետո: Այնուամենայնիվ, ոչ մի դինամիկ լեզու չի կարող համընկնել Rust-ի նման լեզվով կոկիկ գրված ստատիկ կոդի արագությանը:

Փաստարկներ ստատիկ և դինամիկ տիպերի համար

Ստատիկ տիպի համակարգի կողմնակիցները նշում են, որ առանց տիպային համակարգի պարզ սխալներկարող է հանգեցնել արտադրության խնդիրների: Սա, իհարկե, ճիշտ է։ Յուրաքանչյուր ոք, ով օգտագործել է դինամիկ լեզու, դա զգացել է առաջին ձեռքից:


Դինամիկ լեզուների կողմնակիցները նշում են, որ դինամիկ լեզուներով կոդավորումն ավելի հեշտ է թվում: Սա, անկասկած, ճիշտ է որոշ տեսակի կոդերի համար, որոնք մենք ժամանակ առ ժամանակ գրում ենք, օրինակ՝ էվալ կոդը: Սա հակասական որոշում է կանոնավոր աշխատանքի համար, և այստեղ իմաստ ունի հիշել «հեշտ» անորոշ բառը: Ռիչ Հիկին մեծ աշխատանք է կատարել «հեշտ» բառի և «պարզ» բառի հետ դրա առնչության հարցում: Այս ռեպորտաժը դիտելուց հետո կհասկանաք, որ հեշտ չէ ճիշտ օգտագործել «հեշտ» բառը։ Զգուշացեք «թեթևությունից».


Ստատիկ ընդդեմ դինամիկ տիպի համակարգերի դրական և բացասական կողմերը դեռևս վատ են հասկացվում, բայց դրանք միանշանակ կախված են լեզվից և կոնկրետ լուծվող խնդրից:


JavaScript-ը փորձում է շարունակել, նույնիսկ եթե դա նշանակում է անիմաստ փոխակերպում (ինչպես «a» + 1, որը տալիս է «a1»): Մյուս կողմից, Python-ը փորձում է լինել պահպանողական և հաճախ վերադարձնում է սխալներ, ինչպես «a» + 1-ի դեպքում:


Անվտանգության տարբեր մակարդակներով տարբեր մոտեցումներ կան, բայց Python-ը և JavaScript-ը երկուսն էլ դինամիկ մուտքագրված լեզուներ են:



Haskell-ը ձեզ թույլ չի տա նախապես ավելացնել ամբողջ թիվ և float առանց հստակ փոխակերպման: C-ն և Haskell-ը երկուսն էլ ստատիկ կերպով տպագրված են՝ չնայած այս մեծ տարբերություններին:


Կան դինամիկ և ստատիկ լեզուների բազմաթիվ տարբերակներ: Ցանկացած անհիմն հայտարարություն, ինչպիսին է «ստատիկ լեզուները ավելի լավն են, քան դինամիկ լեզուները, երբ խոսքը վերաբերում է X-ին», գրեթե երաշխավորված է անհեթեթություն: Սա կարող է ճիշտ լինել կոնկրետ լեզուների համար, բայց հետո ավելի լավ է ասել «Haskell-ը ավելի լավ է, քան Python-ը, երբ խոսքը վերաբերում է X-ին»:

Ստատիկ տիպի համակարգերի բազմազանություն

Եկեք նայենք ստատիկ տպագրված լեզուների երկու հայտնի օրինակներին՝ Go և Haskell: Go-ի տիպային համակարգում չկան ընդհանուր տիպեր, այլ տեսակների «պարամետրերով» տիպեր։ Օրինակ, դուք կարող եք ստեղծել ձեր սեփական տեսակը MyList ցուցակների համար, որը կարող է պահել մեզ անհրաժեշտ ցանկացած տվյալ: Մենք ցանկանում ենք, որպեսզի կարողանանք ստեղծել ամբողջ թվերի MyList, տողերի MyList և այլն՝ առանց փոխելու աղբյուրԻմ ցուցակը. Կոմպիլյատորը պետք է հոգ տանի մուտքագրելու մասին. եթե կա ամբողջ թվերի MyList, և մենք պատահաբար դրան ավելացնենք տող, ապա կոմպիլյատորը պետք է մերժի ծրագիրը:


Go-ն հատուկ նախագծված էր այնպես, որ անհնար էր սահմանել այնպիսի տեսակներ, ինչպիսիք են MyList-ը: Լավագույնը, որ դուք կարող եք անել, «դատարկ միջերեսների» MyList-ի ստեղծումն է. MyList-ը կարող է պարունակել օբյեկտներ, բայց կոմպիլյատորը պարզապես չգիտի դրանց տեսակը: Երբ մենք առբերում ենք օբյեկտները MyList-ից, մենք պետք է կոմպիլյատորին ասենք, թե դրանք ինչ տեսակ են: Եթե ​​ասենք «Ես վերցնում եմ տող», բայց իրականում արժեքը թիվ է, ապա գործարկման ժամանակի սխալ կլինի, ինչպես դա դինամիկ լեզուների դեպքում է։


Go-ին բացակայում են նաև այսօրվա ստատիկ տպագրված լեզուներում (կամ նույնիսկ 1970-ականների որոշ համակարգերում) հայտնաբերված շատ այլ հատկանիշներ: Go-ի ստեղծողները այս որոշումների համար ունեին իրենց սեփական պատճառները, սակայն կողմնակի մարդկանց կարծիքներն այս հարցում երբեմն կարող են կոպիտ հնչել:


Հիմա համեմատենք Haskell-ի հետ, որն ունի շատ հզոր տիպային համակարգ։ Եթե ​​տեսակը դրված է MyList-ի վրա, ապա «թվերի ցանկ» տեսակը պարզապես MyList Integer է: Haskell-ը թույլ չի տալիս մեզ պատահաբար տող ավելացնել ցանկին և համոզվում է, որ մենք ցուցակից որևէ տարր չենք դնում տողային փոփոխականի մեջ:


Haskell-ը կարող է շատ ավելի բարդ գաղափարներ արտահայտել ուղղակի տիպերի հետ։ Օրինակ, Num a => MyList a նշանակում է «Արժեքների MyList, որոնք նույն թվային տիպի են»: Դա կարող է լինել ամբողջ թվերի ցանկ, լողացող կամ տասնորդական թվերֆիքսված ճշգրտությամբ, բայց դա հաստատ երբեք չի լինի տողերի ցանկ, որը ստուգվում է կոմպիլյացիայի ժամանակ։


Դուք կարող եք գրել ավելացնել ֆունկցիա, որն աշխատում է ցանկացած թվային տիպի հետ: Այս ֆունկցիան կունենա Num a => (a -> a -> a) տեսակը: Դա նշանակում է:

  • a-ն կարող է լինել ցանկացած թվային տեսակ (Num a =>):
  • Ֆունկցիան վերցնում է a տիպի երկու արգումենտ և վերադարձնում a տեսակը (a -> a -> a):

Վերջին օրինակը. Եթե ​​ֆունկցիայի տեսակը String -> String է, ապա այն վերցնում է տող և վերադարձնում տող: Բայց եթե դա String -> IO String է, ապա այն նաև կատարում է որոշ I/O: Սա կարող է լինել սկավառակ մուտք գործելը, ցանց մուտք գործելը, տերմինալից կարդալը և այլն:


Եթե ​​ֆունկցիան ունի տեսակ Ոչ IO, ապա մենք գիտենք, որ այն չի կատարում որևէ I/O գործողություն: Վեբ հավելվածում, օրինակ, դուք կարող եք պարզել, թե արդյոք ֆունկցիան փոխում է տվյալների բազան միայն նայելով դրա տեսակը: Ոչ մի դինամիկ և գրեթե ոչ մի ստատիկ լեզու չի կարող դա անել: Սա ամենահզոր տպագրական համակարգով լեզուների առանձնահատկությունն է:


Լեզուների մեծ մասում մենք պետք է գործ ունենանք ֆունկցիայի և բոլոր գործառույթների հետ, որոնք կանչվում են այնտեղից և այլն՝ փորձելով գտնել մի բան, որը փոխում է տվյալների բազան: Դա հոգնեցուցիչ գործընթաց է, և հեշտ է սխալվել: Իսկ Haskell տիպի համակարգը կարող է պարզ ու հուսալի պատասխանել այս հարցին։


Համեմատեք այս հզորությունը Go-ի հետ, որն ի վիճակի չէ արտահայտել MyList-ի պարզ գաղափարը, էլ չասած «մի ֆունկցիա, որը վերցնում է երկու արգումենտ՝ և՛ թվային, և՛ նույն տեսակի, և որը կատարում է I/O»։


Go մոտեցումը հեշտացնում է Go ծրագրավորման գործիքներ գրելը (մասնավորապես, կոմպիլյատորի իրականացումը կարող է պարզ լինել): Բացի այդ, սովորելու ավելի քիչ հասկացություններ կան: Թե ինչպես են այս առավելությունները համեմատվում էական սահմանափակումների հետ, սուբյեկտիվ հարց է: Այնուամենայնիվ, չի կարելի պնդել, որ Haskell-ն ավելի դժվար է սովորել, քան Go-ն, և որ Haskell-ի տիպային համակարգը շատ ավելի հզոր է, և որ Haskell-ը կարող է կանխել բազմաթիվ այլ տեսակի սխալների կազմավորումը:


Go-ն և Haskell-ը այնքան տարբեր լեզուներ են, որ դրանք խմբավորելը «ստատիկ լեզուների» նույն դասի մեջ կարող է ապակողմնորոշիչ լինել, չնայած տերմինը ճիշտ է օգտագործվում: Անվտանգության գործնական առավելությունների առումով Go-ն ավելի մոտ է դինամիկ լեզուներին, քան Haskell-ին:


Մյուս կողմից, որոշ դինամիկ լեզուներ ավելի ապահով են, քան որոշ ստատիկ լեզուներ: (Python-ը սովորաբար համարվում է շատ ավելի անվտանգ, քան C-ն): Երբ խոսքը վերաբերում է ստատիկ կամ դինամիկ լեզուների վերաբերյալ ընդհանրացումներին՝ որպես խմբերի, մի մոռացեք լեզուների միջև եղած հսկայական տարբերությունների մասին:

Տիպային համակարգերի հնարավորությունների տարբերությունների կոնկրետ օրինակներ

Ավելի հզոր տիպային համակարգերում դուք կարող եք սահմանել սահմանափակումներ ավելի փոքր մակարդակներում: Ահա մի քանի օրինակներ, բայց մի կախեք դրանցից, եթե շարահյուսությունը պարզ չէ:


Go-ում կարող եք ասել «ավելացնել գործառույթը վերցնում է երկու ամբողջ թիվ և վերադարձնում է մի ամբողջ թիվ»:


func add(x int, y int) int (վերադարձ x + y)

Haskell-ում կարող եք ասել «գործառույթը վերցնում է ցանկացածթվային տիպ և վերադարձնում է նույն տիպի մի շարք».


f:: Num a => a -> a -> a ավելացնել x y = x + y

Idris-ում կարող եք ասել «ֆունկցիան վերցնում է երկու ամբողջ թիվ» և վերադարձնում է ամբողջ թիվ, բայց առաջին արգումենտը պետք է փոքր լինի երկրորդ արգումենտից»:


ավելացնել՝ (x: Nat) -> (y: Nat) -> (ավտո փոքր՝ LT x y) -> Nat ավելացնել x y = x + y

Եթե ​​փորձեք կանչել ավելացնել 2 1 ֆունկցիան, որտեղ առաջին արգումենտը մեծ է երկրորդից, ապա կոմպիլյատորը կմերժի ծրագիրը։ կազմման ժամանակ. Անհնար է գրել ծրագիր, որտեղ առաջին արգումենտն ավելի մեծ է, քան երկրորդը: Հազվագյուտ լեզուն ունի այս հնարավորությունը: Լեզուների մեծ մասում այս ստուգումը տեղի է ունենում գործարկման ժամանակ. մենք կգրեինք նման բան, եթե x >= y. բարձրացնել SomeError() .


Haskell-ում չկա համարժեք Idris օրինակի տիպին վերևում, իսկ Go-ում չկա ոչ Haskell օրինակին, ոչ էլ Idris օրինակին: Արդյունքում, Իդրիսը կարող է կանխել բազմաթիվ սխալներ, որոնք Haskell-ը չի կարող կանխել, իսկ Haskell-ը կարող է կանխել բազմաթիվ սխալներ, որոնք Go-ն չի նկատի: Երկու դեպքում էլ ձեզ հարկավոր է լրացուցիչ հնարավորություններմուտքագրման համակարգեր, որոնք լեզուն ավելի բարդ են դարձնում:

Որոշ ստատիկ լեզուների տիպային համակարգեր

Ահա որոշ լեզուների տիպային համակարգերի մոտավոր ցուցակը՝ ըստ հզորության աճի։ Այս ցուցակը ձեզ ընդհանուր պատկերացում կտա համակարգերի հզորության մասին, ձեզ հարկավոր չէ այն ընդունել որպես բացարձակ ճշմարտություն: Մեկ խմբում հավաքված լեզուները կարող են շատ տարբեր լինել միմյանցից: Յուրաքանչյուր տիպի համակարգ ունի իր տարօրինակությունները, և դրանցից շատերը շատ բարդ են:

  • C (1972), Go (2009)Այս համակարգերը ամենևին էլ հզոր չեն, առանց ընդհանուր տեսակների աջակցության: Հնարավոր չէ MyList տիպը դնել «ամբողջ թվերի ցուցակ», «տողերի ցուցակ» և այլն: Փոխարենը, դուք ստիպված կլինեք կատարել «չստորագրված արժեքների ցուցակ»: Ծրագրավորողը պետք է ձեռքով ասի «սա տողերի ցուցակ է» ամեն անգամ, երբ տողը վերցվում է ցանկից, և դա կարող է հանգեցնել գործարկման ժամանակի սխալի:
  • Java (1995), C# (2000)Երկու լեզուներն էլ աջակցում են ընդհանուր, այնպես որ կարող եք ասել MyList և ստացեք տողերի ցանկ, որոնց մասին գիտի կոմպիլյատորը և կարող է կիրառել՝ հակառակ տեսակի կանոնների: Ցանկի տարրերը կլինեն String տիպի, կոմպիլյատորը սովորականի պես կպարտադրի կանոնները, այնպես որ գործարկման ժամանակի սխալները քիչ հավանական են:
  • Haskell (1990), Rust (2010), Swift (2014)Այս բոլոր լեզուներն ունեն մի քանի առաջադեմ առանձնահատկություններ, ներառյալ ընդհանուր տեսակները, հանրահաշվական տվյալների տեսակները (ADTs) և տիպի դասերը կամ նմանատիպ որևէ այլ բան (համապատասխանաբար դասի տեսակներ, հատկություններ և արձանագրություններ): Rust-ը և Swift-ն ավելի հայտնի են, քան Haskell-ը, և դրանք գովազդվում են խոշոր կազմակերպությունների կողմից (համապատասխանաբար Mozilla և Apple):
  • Agda (2007), Իդրիս (2011)Այս լեզուներն աջակցում են կախյալ տիպերին, ինչը թույլ է տալիս ստեղծել այնպիսի տեսակներ, ինչպիսին է «գործառույթը, որը վերցնում է երկու ամբողջ թվեր x և y, որտեղ y-ը x-ից մեծ է»: Նույնիսկ «y-ը x-ից մեծ է» սահմանափակումն ուժի մեջ է մտնում կոմպիլյացիայի ժամանակ: Երբ կատարվի, y-ը երբեք փոքր կամ հավասար չի լինի x-ից, անկախ նրանից, թե ինչ կլինի: շատ նիհար, բայց կարևոր հատկություններհամակարգերը կարելի է ստատիկ կերպով ստուգել այս լեզուներով: Շատ քիչ ծրագրավորողներ են ուսումնասիրում դրանք, բայց նրանք շատ են ոգևորված այս լեզուներով:

Հստակ շարժում կա դեպի ավելի հզոր տիպային համակարգեր, հատկապես, երբ չափվում է լեզուների ժողովրդականությամբ, այլ ոչ միայն լեզուների գոյության փաստով: Հատկանշական բացառություն է Go-ն, որը բացատրում է, թե ինչու են ստատիկ լեզուների շատ կողմնակիցներ այն համարում հետընթաց քայլ:


Երկրորդ խումբը (Java և C#) հիմնական լեզուներ են, հասուն և լայնորեն օգտագործվող:


Երրորդ խումբը հիմնական հոսք մտնելու շեմին է՝ Mozilla-ի (Rust) և Apple-ի (Swift) մեծ աջակցությամբ:


Չորրորդ խումբը (Իդրիսը և Աղդան) հեռու են հիմնական հոսքից, բայց դա կարող է փոխվել ժամանակի ընթացքում: Երրորդ խմբի լեզուները տասը տարի առաջ հեռու էին հիմնական հոսքից:

Այս հոդվածը պարունակում է այն նվազագույնը, ինչ դուք պարզապես պետք է իմանաք մուտքագրելու մասին, որպեսզի դինամիկ մուտքագրումը չարիք չանվանեք, Lisp-ը՝ չտպագրված լեզու, իսկ C-ը՝ խիստ տպագրված լեզու:

IN ամբողջական տարբերակըգտնվում է մանրամասն նկարագրությունբոլոր տեսակի մուտքագրումներ՝ համեմված կոդերի օրինակներով, ծրագրավորման հայտնի լեզուների հղումներով և ցուցադրական նկարներով:

Խորհուրդ եմ տալիս նախ կարդալ հոդվածի համառոտ տարբերակը, իսկ հետո ցանկության դեպքում՝ ամբողջական։

Կարճ տարբերակ

Մուտքագրման ծրագրավորման լեզուները սովորաբար բաժանվում են երկու մեծ ճամբարների. մուտքագրվածԵվ անտիպ (անտիպ) Առաջինը ներառում է C, Python, Scala, PHP և Lua, մինչդեռ երկրորդը ներառում է անսամբլի լեզու, Forth և Brainfuck:

Քանի որ «անտիպ մուտքագրումը» իր էությամբ նույնքան պարզ է, որքան խցանը, այն հետագայում չի բաժանվում որևէ այլ տեսակի: Բայց տպագրված լեզուները բաժանվում են ևս մի քանի համընկնող կատեգորիաների.

  • ստատիկ / դինամիկմուտքագրում. Ստատիկը որոշվում է նրանով, որ փոփոխականների և ֆունկցիաների վերջնական տեսակները սահմանվում են կոմպիլյացիայի ժամանակ: Նրանք. արդեն կոմպիլյատորը 100%-ով վստահ է, թե որ տեսակը որտեղ է: Դինամիկ մուտքագրման ժամանակ բոլոր տեսակները որոշվում են գործարկման ժամանակ:

    Օրինակներ.
    Ստատիկ՝ C, Java, C#;
    Դինամիկ՝ Python, JavaScript, Ruby:

  • ուժեղ / թույլմուտքագրում (նաև երբեմն կոչվում է խիստ / ոչ խիստ): Ուժեղ մեքենագրումն առանձնանում է նրանով, որ լեզուն թույլ չի տալիս արտահայտությունների մեջ խառնվել Տարբեր տեսակներև չի կատարում ավտոմատ անուղղակի փոխարկումներ, օրինակ, դուք չեք կարող մի շարք հանել տողից: Թույլ տպագրված լեզուները ինքնաբերաբար կատարում են բազմաթիվ անուղղակի փոխակերպումներ, նույնիսկ եթե ճշգրտության կամ փոխակերպման կորուստը կարող է երկիմաստ լինել:

    Օրինակներ.
    Ուժեղ՝ Java, Python, Haskell, Lisp;
    Թույլ՝ C, JavaScript, Visual Basic, PHP:

  • Բացահայտ / անուղղակիմուտքագրում. Հստակ մուտքագրված լեզուները տարբերվում են նրանով, որ նոր փոփոխականների / ֆունկցիաների / դրանց արգումենտների տեսակը պետք է բացահայտորեն սահմանվի: Համապատասխանաբար, անուղղակի մուտքագրմամբ լեզուները տեղափոխում են այս առաջադրանքը դեպի կոմպիլյատոր / թարգմանիչ:

    Օրինակներ.
    Բացահայտ՝ C++, D, C#
    Անուղղակի՝ PHP, Lua, JavaScript

Հարկ է նաև նշել, որ այս բոլոր կատեգորիաները հատվում են, օրինակ, C լեզուն ունի ստատիկ թույլ բացահայտ տպագրում, իսկ Python լեզուն ունի դինամիկ ուժեղ իմպլիցիտ մուտքագրում:

Այնուամենայնիվ, միաժամանակ ստատիկ և դինամիկ մուտքագրմամբ լեզուներ չկան: Չնայած առաջ նայելով, ես կասեմ, որ ես այստեղ պառկած եմ, նրանք իսկապես գոյություն ունեն, բայց դրա մասին ավելի ուշ:

մանրամասն տարբերակ

Եթե ​​կարճ տարբերակը ձեզ չի բավականացնում, լավ: Զարմանալի չէ, որ մանրամասն գրեցի? Հիմնական բանն այն է, որ կարճ տարբերակում պարզապես անհնար էր տեղավորել բոլոր օգտակար և հետաքրքիր տեղեկությունները, իսկ մանրամասնը, հավանաբար, շատ երկար կլիներ, որպեսզի բոլորը կարդան այն առանց լարվելու:

Անտիպ մուտքագրում

Անտիպ ծրագրավորման լեզուներում բոլոր սուբյեկտները համարվում են ընդամենը տարբեր երկարությունների բիթերի հաջորդականություն:

Անտիպ տպագրումը սովորաբար բնորոշ է ցածր մակարդակի (ասեմբլեր լեզու, Forth) և էզոթերիկ (Brainfuck, HQ9, Piet) լեզուներին: Այնուամենայնիվ, իր թերությունների հետ մեկտեղ, այն ունի նաև որոշ առավելություններ.

Առավելությունները
  • Թույլ է տալիս գրել չափազանց ցածր մակարդակով, և կոմպիլյատորը / թարգմանիչը չի խանգարի որևէ տեսակի ստուգմանը: Դուք ազատ եք ցանկացած տեսակի տվյալների վրա կատարել ցանկացած գործողություն:
  • Ստացված ծածկագիրը սովորաբար ավելի արդյունավետ է:
  • Հրահանգների թափանցիկություն. Լեզվի իմացությամբ, սովորաբար կասկած չկա, թե ինչ է իրենից ներկայացնում այս կամ այն ​​ծածկագիրը։
Թերություններ
  • Բարդություն. Հաճախ անհրաժեշտություն է առաջանում ներկայացնել այնպիսի բարդ արժեքներ, ինչպիսիք են ցուցակները, տողերը կամ կառուցվածքները: Սա կարող է անհարմարություն առաջացնել:
  • Չեկեր չկան։ Ցանկացած անիմաստ գործողություն, ինչպիսին է նիշից զանգվածի ցուցիչը հանելը, կհամարվի միանգամայն նորմալ, ինչը հղի է նուրբ սխալներով:
  • Աբստրակցիայի ցածր մակարդակ: Ցանկացած բարդ տվյալների տիպի հետ աշխատելը ոչնչով չի տարբերվում թվերի հետ աշխատելուց, ինչը, իհարկե, շատ դժվարություններ կստեղծի:
Ուժեղ անտիպ մուտքագրո՞ւմ:
Այո, սա կա: Օրինակ, assembly լեզվով (x86 / x86-64 ճարտարապետության համար, ես ուրիշներին չգիտեմ) դուք չեք կարող ծրագիր հավաքել, եթե փորձեք տվյալները բեռնել rax ռեգիստրից (64 բիթ) cx ռեգիստրում (16): բիթ):

Mov cx, eax ; հավաքման ժամանակի սխալ

Այսպիսով, պարզվում է, որ assembler-ը դեռ մուտքագրում է: Կարծում եմ՝ այս ստուգումները բավարար չեն։ Եվ ձեր կարծիքը, իհարկե, կախված է միայն ձեզանից:

Ստատիկ և դինամիկ մուտքագրում

Հիմնական բանը, որը տարբերում է ստատիկ (ստատիկ) մուտքագրումը դինամիկից (դինամիկ) այն է, որ բոլոր տեսակի ստուգումները կատարվում են կոմպիլյացիայի ժամանակ, և ոչ թե գործարկման ժամանակ:

Ոմանք կարող են մտածել, որ ստատիկ մուտքագրումը չափազանց սահմանափակող է (իրականում դա այդպես է, բայց այն վաղուց ազատվել է որոշ տեխնիկայի օգնությամբ): Ոմանց համար դինամիկ տպագրված լեզուները խաղում են կրակի հետ, բայց ո՞ր հատկանիշներն են դրանք առանձնանում: Երկու տեսակներն էլ գոյության հնարավորություն ունե՞ն: Եթե ​​ոչ, ինչո՞ւ են այդքան շատ ստատիկ և դինամիկ տպագրված լեզուներ:

Եկեք պարզենք այն:

Ստատիկ մուտքագրման առավելությունները
  • Տիպի ստուգումները կատարվում են միայն մեկ անգամ՝ կոմպիլյացիայի ժամանակ: Եվ սա նշանակում է, որ մենք կարիք չենք ունենա անընդհատ պարզելու, թե արդյոք մենք փորձում ենք թիվը բաժանել տողի վրա (և կա՛մ սխալ գցել, կա՛մ փոխակերպում կատարել):
  • Կատարման արագություն. Նախորդ կետից պարզ է դառնում, որ ստատիկ տպագրված լեզուները գրեթե միշտ ավելի արագ են, քան դինամիկ տպագրված լեզուները:
  • Որոշ լրացուցիչ պայմաններում այն ​​թույլ է տալիս հայտնաբերել պոտենցիալ սխալները արդեն իսկ կազմման փուլում:
  • Զարգացման արագացում IDE-ի աջակցությամբ (տարբերակների զննում, որոնք ակնհայտորեն հարմար չեն տեսակի համար):
Դինամիկ մուտքագրման առավելությունները
  • Ունիվերսալ հավաքածուներ ստեղծելու հեշտություն՝ ամեն ինչի և ամեն ինչի կույտեր (հազվադեպ է նման անհրաժեշտություն առաջանում, բայց երբ առաջանում է, դինամիկ մուտքագրումը կօգնի):
  • Ընդհանրացված ալգորիթմների նկարագրության հարմարավետություն (օրինակ՝ զանգվածների տեսակավորում, որը կաշխատի ոչ միայն ամբողջ թվերի, այլև իրական թվերի և նույնիսկ տողերի ցանկի վրա):
  • Հեշտ է սովորել - Դինամիկ մուտքագրված լեզուները սովորաբար շատ լավ են ծրագրավորում սկսելու համար:

Ընդհանուր ծրագրավորում
Դե, դինամիկ մուտքագրման ամենակարևոր փաստարկը ընդհանուր ալգորիթմների նկարագրության հարմարավետությունն է: Եկեք պատկերացնենք խնդիր՝ մեզ անհրաժեշտ է որոնման ֆունկցիա մի քանի զանգվածների (կամ ցուցակների) համար՝ ամբողջ թվերի զանգված, իրականների զանգված և նիշերի զանգված:

Ինչպե՞ս ենք լուծելու: Եկեք լուծենք այն 3 տարբեր լեզուներով՝ մեկը դինամիկ մուտքագրմամբ և երկուսը ստատիկ մուտքագրմամբ:

Ես կվերցնեմ որոնման ամենապարզ ալգորիթմներից մեկը՝ թվարկումը։ Ֆունկցիան կստանա որոնված տարրը, զանգվածը (կամ ցուցակը) ինքնին և կվերադարձնի տարրի ինդեքսը, կամ եթե տարրը չի գտնվել՝ (-1):

Դինամիկ լուծում (Python):
def find(required_element, list): համար (ինդեքս, տարր) innumerate(list): if element == պահանջվող_տարր. վերադարձի ինդեքսը վերադարձ (-1)

Ինչպես տեսնում եք, ամեն ինչ պարզ է, և խնդիրներ չկան այն բանի հետ, որ ցուցակը կարող է պարունակել զույգ թվեր, նույնիսկ ցուցակներ, թեև այլ զանգվածներ չկան: Շատ լավ. Եկեք ավելի հեռու գնանք՝ նույն խնդիրը լուծեք C!

Ստատիկ լուծում (C):
unsigned int find_int(int պահանջվող_տարր, int զանգված, անստորագիր int չափ) ( for (unsigned int i = 0; i< size; ++i) if (required_element == array[i]) return i; return (-1); } unsigned int find_float(float required_element, float array, unsigned int size) { for (unsigned int i = 0; i < size; ++i) if (required_element == array[i]) return i; return (-1); } unsigned int find_char(char required_element, char array, unsigned int size) { for (unsigned int i = 0; i < size; ++i) if (required_element == array[i]) return i; return (-1); }

Դե, յուրաքանչյուր գործառույթ առանձին-առանձին նման է Python տարբերակին, բայց ինչու՞ կան երեքը: Ստատիկ ծրագրավորումը կորե՞լ է:

Այո եւ ոչ. Կան ծրագրավորման մի քանի տեխնիկա, որոնցից մեկը մենք հիմա կքննարկենք: Այն կոչվում է Generic Programming, և C++ լեզուն բավականին լավ է աջակցում դրան: Եկեք նայենք նոր տարբերակին.

Ստատիկ լուծում (ընդհանուր ծրագրավորում, C++):
կաղապար unsigned int find(T պահանջվող_տարր, std::vector զանգված) ( համար (անստորագիր int i = 0; i< array.size(); ++i) if (required_element == array[i]) return i; return (-1); }

Լավ! Այն շատ ավելի բարդ տեսք չունի, քան Python տարբերակը, և դրա համար շատ գրել չի պահանջվել: Բացի այդ, մենք ստացանք իրականացում բոլոր զանգվածների համար, ոչ միայն 3-ի, որոնք անհրաժեշտ են խնդիրը լուծելու համար:

Այս տարբերակը կարծես թե հենց այն է, ինչ մեզ անհրաժեշտ է. մենք ստանում ենք և՛ ստատիկ մուտքագրման առավելությունները, և՛ դինամիկայի որոշ առավելություններ:

Հիանալի է, որ դա նույնիսկ հնարավոր է, բայց դա կարող է ավելի լավ լինել: Նախ, ընդհանուր ծրագրավորումը կարող է ավելի հարմար և գեղեցիկ լինել (օրինակ, Haskell-ում): Երկրորդ, ընդհանուր ծրագրավորումից բացի, կարող եք նաև օգտագործել պոլիմորֆիզմ (արդյունքը ավելի վատ կլինի), ֆունկցիայի գերբեռնվածություն (նմանապես) կամ մակրոներ:

Ստատիկ դինամիկայի մեջ
Հարկ է նաև նշել, որ շատ ստատիկ լեզուներ թույլ են տալիս դինամիկ մուտքագրում, օրինակ.
  • C#-ն աջակցում է դինամիկ կեղծ տիպին:
  • F#-ն աջակցում է շարահյուսական շաքարին ? օպերատորի տեսքով, որը կարող է օգտագործվել դինամիկ մուտքագրումը ընդօրինակելու համար:
  • Haskell - դինամիկ մուտքագրումն ապահովում է Data.Dynamic մոդուլը:
  • Դելֆի - հատուկ տիպի տարբերակի միջոցով:
Նաև դինամիկ տպագրված որոշ լեզուներ թույլ են տալիս օգտվել ստատիկ մուտքագրումից.
  • Common Lisp - տիպի հայտարարություններ:
  • Perl - սկսած 5.6 տարբերակից, բավականին սահմանափակ:
Այսպիսով, եկեք առաջ շարժվենք:

Ուժեղ և թույլ մուտքագրում

Խիստ տպագրված լեզուները թույլ չեն տալիս արտահայտությունների մեջ խառնել տարբեր տեսակի սուբյեկտներ և չեն կատարում ավտոմատ փոխարկումներ: Դրանք նաև կոչվում են «խիստ տպագրված լեզուներ»։ Սրա համար անգլերեն տերմինը ուժեղ մուտքագրում է:

Թույլ տպագրված լեզուները, ընդհակառակը, խրախուսում են ծրագրավորողին խառնել տարբեր տեսակներ մեկ արտահայտության մեջ, իսկ կոմպիլյատորն ինքն ամեն ինչ կվերափոխի մեկ տեսակի: Դրանք նաև կոչվում են «թույլ տպագրված լեզուներ»։ Սրա անգլերեն տերմինը թույլ տպագրություն է:

Թույլ մուտքագրումը հաճախ շփոթում են դինամիկ մուտքագրման հետ, ինչը լիովին սխալ է: Դինամիկ տպագրված լեզուն կարող է լինել ինչպես թույլ, այնպես էլ ուժեղ տպագրված:

Սակայն քչերն են կարեւորում մուտքագրման խստությունը։ Հաճախ պնդում են, որ եթե լեզուն ստատիկ կերպով տպագրված է, ապա դուք կարող եք բռնել շատ հավանական կոմպիլյատորական սխալներ: Նրանք ստում են ձեզ!

Լեզուն պետք է ունենա նաև ուժեղ մուտքագրում։ Իսկապես, եթե կոմպիլյատորը սխալի մասին զեկուցելու փոխարեն պարզապես տող է ավելացնում թվին, կամ, ավելի վատ, մի զանգվածից հանում է մյուսը, ի՞նչ օգուտ մեզ համար, որ տիպերի բոլոր «ստուգումները» կլինեն կոմպիլյացիայի փուլում: Դա ճիշտ է. թույլ ստատիկ մուտքագրումը նույնիսկ ավելի վատ է, քան ուժեղ դինամիկ մուտքագրումը: (Դե, դա իմ կարծիքն է)

Ուրեմն ինչու՞ թույլ մուտքագրումն ընդհանրապես առավելություններ չունի: Կարող է այդպես թվալ, բայց չնայած այն հանգամանքին, որ ես ուժեղ տպագրության կողմնակից եմ, պետք է համաձայնեմ, որ թույլ տպագրությունն էլ ունի առավելություններ։

Ցանկանու՞մ եք իմանալ, թե որոնք են:

Ուժեղ մուտքագրման առավելությունները
  • Հուսալիություն - Դուք կստանաք բացառություն կամ կոմպիլյացիայի սխալ՝ սխալ պահվածքի փոխարեն:
  • Արագություն - անուղղակի փոխակերպումների փոխարեն, որոնք կարող են բավականին թանկ լինել, ուժեղ մուտքագրմամբ, դուք պետք է դրանք բացահայտորեն գրեք, ինչը ծրագրավորողին առնվազն տեղյակ է դարձնում, որ կոդի այս հատվածը կարող է դանդաղ լինել:
  • Հասկանալով, թե ինչպես է աշխատում ծրագիրը. կրկին, ենթադրյալ տիպի քասթինգի փոխարեն, ծրագրավորողն ինքն է գրում ամեն ինչ, ինչը նշանակում է, որ նա մոտավորապես հասկանում է, որ լարը և թիվը համեմատելը ինքնին չի լինում և ոչ կախարդական:
  • Հստակություն. երբ դուք ձեռքով գրում եք փոխակերպումներ, դուք հստակ գիտեք, թե ինչ եք փոխակերպում և ինչի: Բացի այդ, դուք միշտ կհասկանաք, որ նման փոխարկումները կարող են հանգեցնել ճշգրտության կորստի և սխալ արդյունքների:
Թույլ մուտքագրման առավելությունները
  • Խառը արտահայտությունների օգտագործման հարմարավետություն (օրինակ՝ ամբողջ թվերից և իրական թվերից):
  • Վերացություն մուտքագրելուց և կենտրոնանալով առաջադրանքի վրա:
  • Արձանագրության հակիրճությունը.
Լավ, հասկացանք, պարզվում է, որ թույլ մուտքագրումն էլ ունի առավելություններ։ Կա՞ն միջոցներ թույլ մուտքագրման առավելությունները ուժեղ մուտքագրման փոխանցելու համար:

Պարզվում է՝ նույնիսկ երկուսն են։

Անուղղակի տիպի ձուլում, միանշանակ իրավիճակներում և առանց տվյալների կորստի
Wow… Բավական երկար պարբերություն: Թույլ տվեք հետագայում այն ​​կրճատել որպես «սահմանափակված անուղղակի փոխակերպում»: Այսպիսով, ի՞նչ է նշանակում միանշանակ իրավիճակը և տվյալների կորուստը:

Միանշանակ իրավիճակը փոխակերպում կամ գործողություն է, որի էությունը անմիջապես պարզ է դառնում: Օրինակ, երկու թվերի գումարումը միանշանակ իրավիճակ է: Բայց թիվը զանգվածի վերածելը այդպես չէ (հնարավոր է, որ ստեղծվի մեկ տարրի զանգված, գուցե այդպիսի երկարությամբ զանգված, որը լռելյայնորեն լցված է տարրերով, և գուցե թիվը փոխարկվի տողի, այնուհետև զանգվածի։ կերպարների):

Տվյալների կորուստը նույնիսկ ավելի հեշտ է: Եթե ​​3.5 իրական թիվը վերածենք ամբողջ թվի, ապա կկորցնենք տվյալների մի մասը (իրականում այս գործողությունը նույնպես երկիմաստ է. ինչպե՞ս է կատարվելու կլորացումը՝ վերև, ներքև, կոտորակային մասի գցում)։

Փոխակերպումները ոչ միանշանակ իրավիճակներում և տվյալների կորստի հետ փոխակերպումները շատ, շատ վատ են: Ծրագրավորման մեջ սրանից վատ բան չկա։

Եթե ​​չեք հավատում ինձ, սովորեք PL/I լեզուն կամ պարզապես փնտրեք դրա առանձնահատկությունները: Այն ունի փոխակերպման կանոններ ԲՈԼՈՐ տվյալների տեսակների միջև: Դա պարզապես դժոխք է:

Լավ, եկեք հիշենք սահմանափակ անուղղակի փոխակերպման մասին: Կա՞ն այդպիսի լեզուներ։ Այո, օրինակ Pascal-ում կարող եք ամբողջ թիվը վերածել իրական թվի, բայց ոչ հակառակը։ Նման մեխանիզմներ կան նաև C#-ում, Groovy-ում և Common Lisp-ում:

Լավ, ես ասացի, որ ուժեղ լեզվով թույլ տպելու մի քանի առավելություններ ստանալու ևս մեկ տարբերակ կա: Եվ այո, այն գոյություն ունի և կոչվում է կոնստրուկտորային պոլիմորֆիզմ։

Ես դա կբացատրեմ՝ որպես օրինակ օգտագործելով Haskell հրաշալի լեզուն:

Պոլիմորֆ կոնստրուկտորները առաջացել են այն դիտարկման արդյունքում, որ թվային տառերի օգտագործման ժամանակ ամենից հաճախ անհրաժեշտ են անվտանգ անուղղակի փոխարկումներ:

Օրինակ, pi + 1 արտահայտության մեջ դուք չեք ցանկանում գրել pi + 1.0 կամ pi + float(1): Ես ուզում եմ գրել պարզապես pi + 1!

Եվ դա արվում է Հասկելում, շնորհիվ այն բանի, որ բառացի 1-ը կոնկրետ տեսակ չունի։ Այն ոչ ամբողջական է, ոչ իրական, ոչ էլ բարդ։ Դա ուղղակի թիվ է։

Արդյունքում, պարզ ֆունկցիայի գումար x y գրելիս, որը բազմապատկում է բոլոր թվերը x-ից մինչև y (1-ի աճով), մենք ստանում ենք միանգամից մի քանի տարբերակ՝ գումար ամբողջ թվերի համար, գումար՝ ռեալների, գումար՝ ռացիոնալների համար, գումար՝ բարդ: թվեր և նույնիսկ գումար բոլոր այն թվային տեսակների համար, որոնք դուք ինքներդ եք սահմանել:

Իհարկե, այս տեխնիկան խնայում է միայն թվային տառերով խառը արտահայտություններ օգտագործելու դեպքում, և սա միայն այսբերգի գագաթն է:

Այսպիսով, կարելի է ասել, որ լավագույն ելքը կլինի հավասարակշռությունը եզրին, ուժեղ և թույլ տպագրման միջև։ Բայց մինչ այժմ ոչ մի լեզու չունի կատարյալ հավասարակշռություն, ուստի ես ավելի շատ հակված եմ դեպի խիստ տպագրված լեզուները (օրինակ՝ Haskell, Java, C#, Python), այլ ոչ թե թույլ տպագրված լեզուները (օրինակ՝ C, JavaScript, Lua, PHP) .

Բացահայտ և անուղղակի մուտքագրում

Հստակ մուտքագրված լեզուն ծրագրավորողից պահանջում է նշել բոլոր փոփոխականների և գործառույթների տեսակները, որոնք նա հայտարարում է: Սրա համար անգլերեն տերմինը բացահայտ մուտքագրում է:

Մյուս կողմից, անուղղակի մուտքագրված լեզուն ձեզ հրավիրում է մոռանալ տեսակների մասին և թողնել տիպի եզրակացության խնդիրը կազմողին կամ թարգմանչին: Անգլերեն տերմինը դրա համար անուղղակի մուտքագրում է:

Սկզբում կարելի է մտածել, որ անուղղակի մուտքագրումը համարժեք է դինամիկ մուտքագրմանը, իսկ բացահայտ մուտքագրումը համարժեք է ստատիկ մուտքագրմանը, բայց հետո կտեսնենք, որ դա այդպես չէ:

Կա՞ն առավելություններ յուրաքանչյուր տեսակի համար, և կրկին, կա՞ն դրանց համակցություններ և կա՞ն լեզուներ, որոնք աջակցում են երկու մեթոդներին:

Հստակ մուտքագրման առավելությունները
  • Յուրաքանչյուր ֆունկցիայի համար ստորագրություն ունենալը (օրինակ՝ int add(int, int)) հեշտացնում է որոշել, թե ինչ է անում ֆունկցիան:
  • Ծրագրավորողը անմիջապես գրում է, թե ինչ տեսակի արժեքներ կարող են պահվել որոշակի փոփոխականում, ինչը վերացնում է դա հիշելու անհրաժեշտությունը:
Անուղղակի մուտքագրման առավելությունները
  • Կարճագիր - def add(x, y) ակնհայտորեն ավելի կարճ է, քան int add(int x, int y):
  • Փոփոխությունների նկատմամբ դիմացկունություն. Օրինակ, եթե ֆունկցիայի մեջ ժամանակավոր փոփոխականը նույն տեսակին է, ինչ մուտքային արգումենտը, ապա բացահայտ տպագրված լեզվով, երբ մուտքագրման փաստարկի տեսակը փոխվում է, ժամանակավոր փոփոխականի տեսակը նույնպես պետք է փոխվի:
Դե, թվում է, թե երկու մոտեցումներն էլ ունեն և՛ դրական, և՛ դեմ կողմեր ​​(և ո՞վ էր այլ բան սպասում), ուստի եկեք ուղիներ փնտրենք այս երկու մոտեցումները համատեղելու համար:
Բացահայտ մուտքագրում ըստ ընտրության
Կան լեզուներ լռելյայն մուտքագրմամբ և անհրաժեշտության դեպքում արժեքների տեսակը նշելու հնարավորությամբ: Կազմողն ինքնաբերաբար կգուշակի արտահայտության իրական տեսակը: Նման լեզուներից մեկը Haskell-ն է, թույլ տվեք ձեզ պարզ օրինակ բերել՝ լուսաբանելու համար.
-- Չկա բացահայտ տիպի ավելացնել (x, y) = x + y -- Բացահայտ տիպի ավելացնել:: (Integer, Integer) -> Integer add (x, y) = x + y

Նշում. Ես դիտավորյալ օգտագործել եմ չգործող ֆունկցիա, ինչպես նաև դիտավորյալ գրել եմ մասնավոր ստորագրություն՝ ավելի ընդհանուր ավելացնելու փոխարեն.: (Num a) => a -> a -> a *, քանի որ Ես ուզում էի ցույց տալ գաղափարը՝ առանց Հասկելի շարահյուսությունը բացատրելու։

Պարտադիր պայմաններ

Ուժեղ մուտքագրումը ենթադրում է հետևյալ նախադրյալները.

  1. Ցանկացած տվյալների օբյեկտ (փոփոխական, հաստատուն, արտահայտություն) լեզվում միշտ ունի խիստ սահմանված տեսակ, որը ամրագրվում է ծրագրի կազմման պահին (ստատիկ մուտքագրում) կամ որոշվում է գործարկման ժամանակ (դինամիկ մուտքագրում):
  2. Դուք կարող եք փոփոխականին վերագրել միայն արժեք, որն ունի նույն տվյալների տեսակը, ինչ փոփոխականը, նույն սահմանափակումները կիրառվում են պարամետրերի փոխանցման և ֆունկցիայի արդյունքները վերադարձնելու համար:
  3. Յուրաքանչյուր գործողություն պահանջում է խիստ սահմանված տեսակների պարամետրեր:
  4. Անուղղակի տիպի փոխարկումը չի թույլատրվում (այսինքն՝ կոմպիլյատորը փոփոխականի, պարամետրի, ֆունկցիայի կամ գործողության համար հայտարարված այլ տեսակի արժեք օգտագործելու ցանկացած փորձ դիտարկում է որպես շարահյուսական սխալ)։

Ուժեղ մուտքագրման պահանջների խստիվ պահպանմամբ, նույնիսկ տվյալների տեսակները, որոնք նույնական են արժեքների կազմով և վավեր գործառնություններով, անհամատեղելի են: Եթե ​​ծրագրում անհրաժեշտ է մեկ տվյալների տիպի արժեք վերագրել մեկ այլ տեսակի փոփոխականին, դա կարելի է անել, բայց միայն հստակորեն կիրառելով հատուկ տեսակի փոխակերպման գործողություն, որը նման դեպքերում սովորաբար ծրագրավորման լեզվի մի մասն է ( թեև դա կարող է ոչ թե պաշտոնապես լինել, այլ տրամադրվել ստանդարտ գրադարանների կողմից):

Մուտքագրում ծրագրավորման լեզուներով

Հղումներ

տես նաեւ


Վիքիմեդիա հիմնադրամ. 2010 թ .

Տեսեք, թե ինչ է «ուժեղ մուտքագրումը» այլ բառարաններում.

    Տվյալների տեսակը ծրագրավորման տեսության հիմնարար հասկացությունն է: Տվյալների տեսակը սահմանում է արժեքների մի շարք, գործողությունների մի շարք, որոնք կարող են կիրառվել այդ արժեքների վրա, և, հնարավոր է, արժեքների պահպանման և գործողություններ կատարելու միջոց: Ցանկացած ... ... Վիքիպեդիա

    Տվյալների մուտքագրում Տիպի անվտանգություն Տիպի եզրակացություն Դինամիկ մուտքագրում Ստատիկ մուտքագրում Ուժեղ մուտքագրում Փափուկ մուտքագրում Կախված տեսակներ Բադ մուտքագրում Հիմնական հոդված՝ Ուժեղ մուտքագրում Դինամիկ մուտքագրման տեխնիկա, լայնորեն ... ... Վիքիպեդիա

    Տվյալների մուտքագրում Տիպի անվտանգություն Տիպի եզրակացություն Դինամիկ մուտքագրում Ստատիկ մուտքագրում Ուժեղ մուտքագրում Փափուկ մուտքագրում Կախված տեսակներ Բադ մուտքագրում Հիմնական հոդված՝ Ուժեղ մուտքագրում Ստատիկ մուտքագրման տեխնիկա, լայնորեն ... ... Վիքիպեդիա

    Դինամիկ մուտքագրումը ծրագրավորման լեզուներում և հստակեցման լեզուներում լայնորեն կիրառվող տեխնիկա է, որտեղ փոփոխականը կապված է տիպի հետ արժեքի նշանակման պահին, և ոչ թե փոփոխականի հայտարարման պահին: Այսպիսով, տարբեր ոլորտներում ... Վիքիպեդիա

    Տվյալների մուտքագրում Տիպի անվտանգություն Տիպի եզրակացություն Դինամիկ մուտքագրում Ստատիկ մուտքագրում Ուժեղ մուտքագրում Փափուկ մուտքագրում Կախված տեսակները Բադ մուտքագրում Տիպի եզրակացություն ծրագրավորման կոմպիլյատորի հատկանիշում ... ... Վիքիպեդիա

    Տվյալների մուտքագրում Տիպի անվտանգություն Տիպի եզրակացություն Դինամիկ մուտքագրում Ստատիկ մուտքագրում Ուժեղ մուտքագրում Փափուկ մուտքագրում Կախված տեսակներ Բադ մուտքագրում Կախված տեսակ, համակարգչային գիտության և տրամաբանության մեջ, մի տեսակ, որը կախված է արժեքից: Կախված ... ... Վիքիպեդիա

    - (կա նաև տվյալների տեսակ տերմինը) ծրագրավորման տեսության հիմնարար հասկացություն։ Տվյալների տեսակը սահմանում է արժեքների մի շարք, գործողությունների մի շարք, որոնք կարող են կիրառվել այդպիսի արժեքների վրա, և, հնարավոր է, արժեքների պահպանման միջոց իրականացնելու համար, և ... ... Վիքիպեդիա:

    Տվյալների տեսակը Բովանդակություն 1 Պատմություն 2 Սահմանում 3 Տվյալների տեսակների օգտագործման անհրաժեշտությունը ... Վիքիպեդիա

    Այս տերմինն այլ իմաստներ ունի, տես ՄԼ (իմաստներ)։ ML Իմաստաբանություն. բազմաբնույթ պարադիգմ. ֆունկցիոնալ, հրամայական, մոդուլային Հայտնվել է 1973 թվականին Հեղինակ(ներ)՝ Ռոբին Միլներ և այլք Էդինբուրգի համալսարան ... Վիքիպեդիա

Ամեն ինչ շատ պարզ է. Դա նման է հյուրանոցի և մասնավոր բնակարանի տարբերությունին:

Բնակարանում ապրում են միայն այնտեղ գրանցվածները։ Եթե ​​այնտեղ ապրում է, ասենք, Սիդորովների ընտանիքը, ապա Պուպկինների ընտանիքը ողջ կյանքի ընթացքում չի կարողանա այնտեղ ապրել։ Միևնույն ժամանակ, Պետյա Սիդորովը կարող է ապրել այս բնակարանում, այնուհետև Գրիշա Սիդորովը կարող է տեղափոխվել այնտեղ (երբեմն նրանք նույնիսկ կարող են միաժամանակ ապրել այնտեղ. սա զանգված է): Սա ստատիկ մուտքագրում է:

Սիդորովների ընտանիքը կարող է որոշ ժամանակ ապրել հյուրանոցում։ Նրանք նույնիսկ պարտավոր չեն գրանցվել այնտեղ: Հետո նրանք կհեռանան այնտեղից, իսկ Պապկինները կտեղափոխվեն այնտեղ։ Եվ հետո Կուզնեցովները. Եվ հետո մեկ ուրիշը: Սա դինամիկ մուտքագրում է:

Եթե ​​վերադառնանք ծրագրավորմանը, ապա առաջին դեպքը (ստատիկ մուտքագրում) տեղի է ունենում, ասենք, C, C++, C#, Java և այլն: Նախքան փոփոխականին առաջին անգամ արժեք նշանակելը, դուք պետք է ասեք, թե ինչ եք պահելու այնտեղ՝ ամբողջ թվեր, լողացող կետով թվեր, տողեր և այլն։ Սիդորովներն այս բնակարանում են ապրելու) Մյուս կողմից, դինամիկ մուտքագրում չի պահանջվում: Երբ դուք արժեք եք վերագրում, դուք միաժամանակ վերագրում եք փոփոխականի իր տեսակը ( Պուպկինների ընտանիքից Վասյա Պուպկինն այժմ ապրում է հյուրանոցի այս համարում) Սա կարելի է գտնել այնպիսի լեզուներում, ինչպիսիք են PHP-ն, Python-ը և JavaScript-ը:

Երկու մոտեցումներն էլ ունեն իրենց առավելություններն ու թերությունները: Որն է ավելի լավը կամ վատը, կախված է լուծվելիք խնդիրներից: Ավելի մանրամասն կարելի է գտնել, ասենք, Վիքիպեդիայում։

ստատիկ մուտքագրման դեպքում դուք հստակ գիտեք փոփոխականի տեսակը ծրագիրը գրելու և ալգորիթմը մշակելու պահին և դա հաշվի առեք։ դրանք. եթե դուք ասացիք, որ G փոփոխականը չորս բայթանոց անստորագիր ամբողջ թիվ է, ապա ալգորիթմում այն ​​միշտ կլինի չորս բայթանոց անստորագիր ամբողջ թիվ (եթե որևէ բան կա, ապա դուք պետք է բացահայտորեն փոխարկեք այն կամ իմանաք, թե ինչպես է թարգմանիչը փոխակերպում այն իրավիճակների որոշակի շրջանակ, բայց հիմնականում, եթե կա տիպի անհամապատասխանություն, սա ալգորիթմի սխալ է, և կոմպիլյատորը ձեզ գոնե կզգուշացնի), ստատիկով դուք չեք կարող «Վասյա հիմարը» տողը դնել թվի մեջ և լրացուցիչ ստուգումներնախքան փոփոխական օգտագործելը դրա համար «թիվ կա», - չեն պահանջվում, դուք ծախսում եք տվյալների ամբողջ ճշգրտությունը ծրագրում դրանք մուտքագրելու պահին կամ ինչպես պահանջում է հենց ալգորիթմը:

դինամիկ մուտքագրմամբ, նույն փոփոխականի տեսակը in ընդհանուր դեպքձեզ հայտնի չէ և կարող է փոխվել արդեն ծրագրի կատարման ընթացքում, և դուք դա հաշվի եք առնում, ոչ ոք ձեզ չի զգուշացնի տիպի անհամապատասխանության պատճառով հնարավոր ալգորիթմի սխալի մասին (ալգորիթմը մշակելիս դուք ենթադրում էիք, որ G-ն ամբողջ թիվ է պարունակում. , և օգտատերը մուտքագրեց, ասենք, լողացող կետով թիվ, կամ ավելի վատ՝ տող, կամ ասենք հետո թվաբանական գործողությունայնտեղ, ամբողջ թվի փոխարեն, պարզվեց, որ դա լողացող կետով թիվ է, և հաջորդ քայլում դուք կփորձեք օգտագործել բիթային գործողություններ ...), մյուս կողմից, դուք պետք չէ անհանգստանալ շատ քիչ բանով. բաներ.

Այս հոդվածում խոսվում է ստատիկ տիպային և դինամիկ տպագրված լեզուների միջև եղած տարբերության մասին, քննարկվում են «ուժեղ» և «թույլ» մուտքագրման հասկացությունները և համեմատում տարբեր լեզուների տիպային համակարգերի հզորությունը: Վերջերս հստակ շարժում է նկատվում դեպի ծրագրավորման ավելի ուժեղ և հզոր մուտքագրման համակարգեր, ուստի կարևոր է հասկանալ, թե ինչ է ասվում տեսակների և մուտքագրման մասին խոսելիս:



Տեսակը հնարավոր արժեքների հավաքածու է: Ամբողջ թիվը կարող է ունենալ 0, 1, 2, 3 և այլն արժեքներ: Բուլյան կարող է լինել ճշմարիտ կամ կեղծ: Դուք կարող եք գալ ձեր սեփական տեսակը, օրինակ, «DayFive» տեսակը, որում հնարավոր են «տալ» և «5» արժեքները, և ուրիշ ոչինչ: Դա տող կամ թիվ չէ, դա նոր, առանձին տեսակ է։


Ստատիկ տպագրված լեզուները սահմանափակում են փոփոխականների տեսակները. ծրագրավորման լեզուն կարող է իմանալ, օրինակ, որ x-ը ամբողջ թիվ է: Այս դեպքում ծրագրավորողին արգելվում է անել x = true, դա սխալ կոդ կլինի: Կոմպիլյատորը կհրաժարվի այն կազմելուց, ուստի մենք չենք կարող նույնիսկ գործարկել այս կոդը: Ստատիկ կերպով տպագրված մեկ այլ լեզու կարող է ունենալ տարբեր արտահայտչական հնարավորություններ, և ոչ մի հանրաճանաչ տիպային համակարգ չի կարող արտահայտել մեր բարձր հինգ տեսակը (սակայն շատերը կարող են արտահայտել այլ, ավելի բարդ գաղափարներ):


Դինամիկ տպագրված լեզուները արժեքները նշում են տեսակներով. լեզուն գիտի, որ 1-ը ամբողջ թիվ է, 2-ը՝ ամբողջ թիվ, բայց չի կարող իմանալ, որ x-ը միշտ պարունակում է ամբողջ թիվ:


Լեզվի գործարկման ժամանակը ստուգում է այս պիտակները տարբեր ժամանակներում: Եթե ​​մենք փորձենք ավելացնել երկու արժեք, այն կարող է ստուգել՝ արդյոք դրանք թվեր են, տողեր կամ զանգվածներ։ Այնուհետև նա կավելացնի այս արժեքները, դրանք կսոսնձի կամ սխալ կտա՝ կախված տեսակից:

ստատիկ տպագրված լեզուներ

Ստատիկ լեզուները ծրագրում ստուգում են տեսակները կոմպիլյացիայի ժամանակ, նախքան ծրագիրը նույնիսկ գործարկվելը: Ցանկացած ծրագիր, որտեղ տիպերը խախտում են լեզվի կանոնները, համարվում է վատ ձևավորված։ Օրինակ, ստատիկ լեզուների մեծ մասը կմերժի «a» + 1 արտահայտությունը (C-ն բացառություն է այս կանոնից): Կազմողը գիտի, որ «a»-ն տող է, իսկ 1-ը՝ ամբողջ թիվ, և որ +-ն աշխատում է միայն այն դեպքում, երբ ձախ և աջ մասերը նույն տիպի են։ Այսպիսով, նա կարիք չունի գործարկել ծրագիրը, որպեսզի հասկանա, որ խնդիր կա: Ստատիկ տպագրված լեզվի յուրաքանչյուր արտահայտություն հատուկ տեսակի է, որը կարող է որոշվել առանց գործարկման կոդի:


Ստատիկ տպագրված շատ լեզուներ պահանջում են նշել այդ տեսակը: Java public int add(int x, int y) ֆունկցիան վերցնում է երկու ամբողջ թիվ և վերադարձնում երրորդ ամբողջ թիվը։ Ստատիկ տպագրված այլ լեզուները կարող են ինքնաբերաբար որոշել տեսակը: Հասկելում նույն գումարման ֆունկցիան այսպիսի տեսք ունի՝ ավելացնել x y = x + y: Մենք լեզվին չենք ասում տեսակները, բայց նա կարող է ինքն իրեն սահմանել, քանի որ գիտի, որ +-ն աշխատում է միայն թվերի վրա, ուստի x և y-ը պետք է թվեր լինեն, ուստի ավելացնել ֆունկցիան ընդունում է երկու թիվ որպես արգումենտ։


Սա չի նվազեցնում տիպային համակարգի «ստատիկ» բնույթը: Haskell-ի տիպային համակարգը հայտնի է ստատիկ, խիստ և հզոր լինելու համար, և այս բոլոր ճակատներում Haskell-ը առաջ է Java-ից:

Դինամիկ մուտքագրված լեզուներ

Դինամիկ տպագրված լեզուները ձեզանից չեն պահանջում տեսակ նշել, բայց դրանք նույնպես չեն սահմանում: Փոփոխական տեսակները հայտնի չեն, քանի դեռ գործարկման ժամանակ նրանք չունեն հատուկ արժեքներ: Օրինակ՝ ֆունկցիա Python-ում


def f(x, y): վերադարձ x + y

կարող է ավելացնել երկու ամբողջ թիվ, միացնել տողեր, ցուցակներ և այլն, և մենք չենք կարող հստակ պարզել, թե ինչ է կատարվում, քանի դեռ չենք գործարկել ծրագիրը: Միգուցե f-ն ինչ-որ պահի կանչվի երկու տողով, իսկ մեկ այլ կետում՝ երկու թվով: Այս դեպքում x-ը և y-ը տարբեր ժամանակներում կպարունակեն տարբեր տեսակի արժեքներ: Հետևաբար, դինամիկ լեզուներում արժեքներն ասում են, որ ունեն տեսակ, բայց փոփոխականներն ու ֆունկցիաները չունեն: 1 արժեքը հաստատ ամբողջ թիվ է, բայց x-ը և y-ն կարող են լինել ցանկացած բան:

Համեմատություն

Դինամիկ լեզուների մեծամասնությունը սխալ կթողնի, եթե տիպերը սխալ օգտագործվեն (JavaScript-ը տխրահռչակ բացառություն է. այն փորձում է արժեք վերադարձնել ցանկացած արտահայտության համար, նույնիսկ երբ դա իմաստ չունի): Դինամիկ տպագրված լեզուներ օգտագործելիս արտադրական միջավայրում կարող է առաջանալ նույնիսկ պարզ «a» + 1 սխալ: Ստատիկ լեզուները կանխում են նման սխալները, բայց, իհարկե, կանխարգելման աստիճանը կախված է տիպային համակարգի հզորությունից:


Ստատիկ և դինամիկ լեզուները կառուցված են ծրագրի ճիշտության վերաբերյալ սկզբունքորեն տարբեր գաղափարների վրա: Դինամիկ լեզվով «a» + 1-ը վավեր ծրագիր է. կոդը կաշխատի, և գործարկման ժամանակ սխալ կառաջանա: Այնուամենայնիվ, ստատիկ տպագրված լեզուների մեծ մասում «a» + 1 արտահայտությունն է ոչ ծրագիր: այն չի կազմվի և չի գործարկվի։ Դա վավեր կոդ չէ, ինչպես պատահական նիշերի մի խումբ:&%^@*&%^@* վավեր կոդ չէ: Կոռեկտության և սխալի այս լրացուցիչ հասկացությունը դինամիկ լեզուներում համարժեք չունի:

Ուժեղ և թույլ մուտքագրում

«Ուժեղ» և «թույլ» հասկացությունները շատ երկիմաստ են։ Ահա դրանց օգտագործման մի քանի օրինակներ.

    Երբեմն «ուժեղ» նշանակում է «ստատիկ»:
    Դա պարզ է, բայց ավելի լավ է օգտագործել «ստատիկ» տերմինը, քանի որ մարդկանց մեծ մասն այն օգտագործում և հասկանում է:

    Երբեմն «ուժեղ» նշանակում է «չի ենթադրում տեսակի փոխակերպում»:
    Օրինակ, JavaScript-ը թույլ է տալիս գրել «a» + 1, որը կարելի է անվանել «թույլ մուտքագրում»: Բայց գրեթե բոլոր լեզուներն ապահովում են անուղղակի փոխակերպման որոշակի մակարդակ, որը թույլ է տալիս ավտոմատ կերպով փոխարկել ամբողջ թվերից լողացող կետ թվերի, ինչպիսիք են 1 + 1.1: Իրականում մարդկանց մեծամասնությունն օգտագործում է «ուժեղ» բառը՝ ընդունելի և անընդունելի փոխակերպումների սահմանը սահմանելու համար: Ընդհանրապես ընդունված սահման չկա, դրանք բոլորն էլ ճշգրիտ չեն և կախված են կոնկրետ մարդու կարծիքից։

    Երբեմն «ուժեղ»-ը նշանակում է, որ անհնար է շրջանցել լեզվի տպագրության խիստ կանոնները:

  • Երբեմն «ուժեղ» նշանակում է հիշողության համար անվտանգ:
    C-ն հիշողության համար ոչ անվտանգ լեզվի օրինակ է: Եթե ​​xs-ը չորս թվերից կազմված զանգված է, ապա C-ն ուրախությամբ կկատարի xs կամ xs՝ վերադարձնելով հիշողությունից որոշակի արժեք xs-ից անմիջապես հետո:

Եկեք կանգ առնենք. Ահա թե ինչպես են որոշ լեզուներ համապատասխանում այս սահմանումներին: Ինչպես տեսնում եք, միայն Հասկելն է բոլոր առումներով հետևողականորեն «ուժեղ»: Լեզուների մեծ մասը այնքան էլ պարզ չէ:



(«Երբ որպես» սյունակում «Անկանխատեսելի փոխարկումներ» նշանակում է, որ ուժեղ և թույլ բաժանումը կախված է նրանից, թե ինչպիսի փոխարկումներ ենք մենք ընդունելի համարում):


Հաճախ «ուժեղ» և «թույլ» տերմինները վերաբերում են վերը նշված տարբեր սահմանումների չսահմանված համակցությանը, և այստեղ չցուցադրված այլ սահմանումներ: Այս ամբողջ խառնաշփոթը գործնականում անիմաստ է դարձնում «ուժեղ» և «թույլ» բառերը։ Երբ ցանկանում եք օգտագործել այս տերմինները, ավելի լավ է նկարագրել, թե կոնկրետ ինչ է նշանակում: Օրինակ, կարող եք ասել «JavaScript-ը վերադարձնում է արժեք, երբ տողը ավելացվում է թվին, բայց Python-ը վերադարձնում է սխալ»։ Այդ դեպքում մենք մեր էներգիան չենք վատնի՝ փորձելով համաձայնության գալ «ուժեղ» բառի բազմակի իմաստների շուրջ։ Կամ, նույնիսկ ավելի վատ՝ տերմինաբանության պատճառով մենք հայտնվում ենք չլուծված թյուրիմացությունների մեջ:


Շատ դեպքերում համացանցում «ուժեղ» և «թույլ» տերմինները կոնկրետ մարդկանց անորոշ և վատ սահմանված կարծիքներ են: Դրանք օգտագործվում են լեզվին «վատ» կամ «լավ» անվանելու համար, և այս կարծիքը վերածվում է տեխնիկական ժարգոնի։



Ուժեղ մուտքագրում. Տիպային համակարգ, որը ես սիրում և հարմարավետ եմ զգում:

Թույլ մուտքագրում. Տիպային համակարգ, որն ինձ անհանգստացնում է կամ ինձ հարմար չէ:

Աստիճանական մուտքագրում

Հնարավո՞ր է դինամիկ լեզուներին ստատիկ տիպեր ավելացնել: Որոշ դեպքերում՝ այո։ Մյուսների դեպքում դա դժվար է կամ անհնար: Առավել ակնհայտ խնդիրը eval-ի և այլ նմանատիպ դինամիկ լեզվական հատկանիշների հետ է: Python-ում 1 + eval("2") անելը տալիս է 3: Բայց ի՞նչ է տալիս 1 + eval(read_from_the_network()): Դա կախված է նրանից, թե ինչ է ցանցում կատարման պահին: Եթե ​​ստանում ենք թիվ, ապա արտահայտությունը ճիշտ է։ Եթե ​​լար, ապա ոչ: Գործարկումից առաջ իմանալու ոչ մի միջոց չկա, ուստի անհնար է վերլուծել տեսակը ստատիկ կերպով:


Գործնականում անբավարար լուծում է eval() արտահայտությունը սահմանել Any-ի տիպի, որը նման է Object-ին որոշ օբյեկտի վրա հիմնված ծրագրավորման լեզուներում կամ ինտերֆեյսի() Go-ում. սա մի տեսակ է, որը բավարարում է ցանկացած արժեք:


Any տիպի արժեքները ոչնչով սահմանափակված չեն, ուստի տիպային համակարգը մեզ ոչ մի կերպ չի կարող օգնել էվալ կոդով: Լեզուները, որոնք ունեն և՛ eval, և՛ տիպային համակարգ, պետք է հրաժարվեն տիպի անվտանգությունից ամեն անգամ, երբ օգտագործվում է eval-ը:


Որոշ լեզուներ ունեն ընտրովի կամ աստիճանական մուտքագրում. դրանք լռելյայն դինամիկ են, բայց թույլ են տալիս ավելացնել որոշ ստատիկ ծանոթագրություններ: Python-ը վերջերս ավելացրել է կամընտիր տեսակներ; TypeScript-ը JavaScript-ի հավելում է, որն ունի ընտրովի տեսակներ; Flow-ը կատարում է հին լավ JavaScript կոդի ստատիկ վերլուծություն:


Այս լեզուները ապահովում են ստատիկ մուտքագրման որոշ առավելություններ, բայց դրանք երբեք չեն տրամադրի այն բացարձակ երաշխիքը, որ իսկապես ստատիկ լեզուներն են տալիս: Որոշ գործառույթներ ստատիկ կերպով մուտքագրվելու են, իսկ որոշները՝ դինամիկ: Ծրագրավորողը միշտ պետք է իմանա և զգուշանա տարբերությունից:

Ստատիկ մուտքագրված կոդի կազմում

Երբ ստատիկ տպագրված կոդը կազմվում է, նախ ստուգվում է շարահյուսությունը, ինչպես ցանկացած կոմպիլյատորում: Այնուհետեւ ստուգվում են տեսակները։ Սա նշանակում է, որ ստատիկ լեզուն կարող է նախ բողոքել մեկ շարահյուսական սխալի մասին, իսկ այն շտկելուց հետո բողոքել 100 տպագրական սխալների մասին։ Շարահյուսական սխալի շտկումը չստեղծեց այդ 100 մուտքագրման սխալները: Կազմողն ուղղակի տիպային սխալները հայտնաբերելու միջոց չուներ, քանի դեռ շարահյուսությունը չի ուղղվել:


Ստատիկ լեզվի կոմպիլյատորները սովորաբար կարող են ավելի արագ կոդ ստեղծել, քան դինամիկ լեզվի կոմպիլյատորները: Օրինակ, եթե կոմպիլյատորը գիտի, որ add ֆունկցիան ընդունում է ամբողջ թվեր, ապա նա կարող է օգտագործել պրոցեսորի բնիկ ADD հրահանգը։ Դինամիկ լեզուն կստուգի գործարկման ժամանակ՝ ընտրելով բազմաթիվ ավելացման գործառույթներից մեկը՝ կախված տեսակներից (ավելացնելով ամբողջ թվեր կամ լողացողներ, կամ միացնելով տողեր կամ միգուցե ցուցակներ): Կամ պետք է որոշել, որ սխալ է տեղի ունեցել, իսկ տեսակները՝ ոչ: համընկնում. Այս բոլոր ստուգումները ժամանակ են պահանջում: Դինամիկ լեզուները օպտիմիզացման համար օգտագործում են տարբեր հնարքներ, օրինակ՝ JIT (ճիշտ ժամանակի) կոմպիլյացիան, որտեղ ծածկագիրը վերակոմպիլացվում է գործարկման ժամանակ՝ տեսակների մասին բոլոր անհրաժեշտ տեղեկությունները ստանալուց հետո: Այնուամենայնիվ, ոչ մի դինամիկ լեզու չի կարող համընկնել Rust-ի նման լեզվով կոկիկ գրված ստատիկ կոդի արագությանը:

Փաստարկներ ստատիկ և դինամիկ տիպերի համար

Ստատիկ տիպի համակարգի կողմնակիցները նշում են, որ առանց տիպային համակարգի պարզ սխալները կարող են հանգեցնել արտադրության խնդիրների: Սա, իհարկե, ճիշտ է։ Յուրաքանչյուր ոք, ով օգտագործել է դինամիկ լեզու, դա զգացել է առաջին ձեռքից:


Դինամիկ լեզուների կողմնակիցները նշում են, որ դինամիկ լեզուներով կոդավորումն ավելի հեշտ է թվում: Սա, անկասկած, ճիշտ է որոշ տեսակի կոդերի համար, որոնք մենք ժամանակ առ ժամանակ գրում ենք, օրինակ՝ էվալ կոդը: Սա հակասական որոշում է կանոնավոր աշխատանքի համար, և այստեղ իմաստ ունի հիշել «հեշտ» անորոշ բառը: Ռիչ Հիկին մեծ աշխատանք է կատարել «հեշտ» բառի և «պարզ» բառի հետ դրա առնչության հարցում: Այս ռեպորտաժը դիտելուց հետո կհասկանաք, որ հեշտ չէ ճիշտ օգտագործել «հեշտ» բառը։ Զգուշացեք «թեթևությունից».


Ստատիկ ընդդեմ դինամիկ տիպի համակարգերի դրական և բացասական կողմերը դեռևս վատ են հասկացվում, բայց դրանք միանշանակ կախված են լեզվից և կոնկրետ լուծվող խնդրից:


JavaScript-ը փորձում է շարունակել, նույնիսկ եթե դա նշանակում է անիմաստ փոխակերպում (ինչպես «a» + 1, որը տալիս է «a1»): Մյուս կողմից, Python-ը փորձում է լինել պահպանողական և հաճախ վերադարձնում է սխալներ, ինչպես «a» + 1-ի դեպքում:


Անվտանգության տարբեր մակարդակներով տարբեր մոտեցումներ կան, բայց Python-ը և JavaScript-ը երկուսն էլ դինամիկ մուտքագրված լեզուներ են:



Haskell-ը ձեզ թույլ չի տա նախապես ավելացնել ամբողջ թիվ և float առանց հստակ փոխակերպման: C-ն և Haskell-ը երկուսն էլ ստատիկ կերպով տպագրված են՝ չնայած այս մեծ տարբերություններին:


Կան դինամիկ և ստատիկ լեզուների բազմաթիվ տարբերակներ: Ցանկացած անհիմն հայտարարություն, ինչպիսին է «ստատիկ լեզուները ավելի լավն են, քան դինամիկ լեզուները, երբ խոսքը վերաբերում է X-ին», գրեթե երաշխավորված է անհեթեթություն: Սա կարող է ճիշտ լինել կոնկրետ լեզուների համար, բայց հետո ավելի լավ է ասել «Haskell-ը ավելի լավ է, քան Python-ը, երբ խոսքը վերաբերում է X-ին»:

Ստատիկ տիպի համակարգերի բազմազանություն

Եկեք նայենք ստատիկ տպագրված լեզուների երկու հայտնի օրինակներին՝ Go և Haskell: Go-ի տիպային համակարգում չկան ընդհանուր տիպեր, այլ տեսակների «պարամետրերով» տիպեր։ Օրինակ, դուք կարող եք ստեղծել ձեր սեփական տեսակը MyList ցուցակների համար, որը կարող է պահել մեզ անհրաժեշտ ցանկացած տվյալ: Մենք ցանկանում ենք, որպեսզի կարողանանք ստեղծել ամբողջ թվերի MyList, տողերի MyList և այլն՝ առանց MyList-ի սկզբնական կոդը փոխելու: Կոմպիլյատորը պետք է հոգ տանի մուտքագրելու մասին. եթե կա ամբողջ թվերի MyList, և մենք պատահաբար դրան ավելացնենք տող, ապա կոմպիլյատորը պետք է մերժի ծրագիրը:


Go-ն հատուկ նախագծված էր այնպես, որ անհնար էր սահմանել այնպիսի տեսակներ, ինչպիսիք են MyList-ը: Լավագույնը, որ դուք կարող եք անել, «դատարկ միջերեսների» MyList-ի ստեղծումն է. MyList-ը կարող է պարունակել օբյեկտներ, բայց կոմպիլյատորը պարզապես չգիտի դրանց տեսակը: Երբ մենք առբերում ենք օբյեկտները MyList-ից, մենք պետք է կոմպիլյատորին ասենք, թե դրանք ինչ տեսակ են: Եթե ​​ասենք «Ես վերցնում եմ տող», բայց իրականում արժեքը թիվ է, ապա գործարկման ժամանակի սխալ կլինի, ինչպես դա դինամիկ լեզուների դեպքում է։


Go-ին բացակայում են նաև այսօրվա ստատիկ տպագրված լեզուներում (կամ նույնիսկ 1970-ականների որոշ համակարգերում) հայտնաբերված շատ այլ հատկանիշներ: Go-ի ստեղծողները այս որոշումների համար ունեին իրենց սեփական պատճառները, սակայն կողմնակի մարդկանց կարծիքներն այս հարցում երբեմն կարող են կոպիտ հնչել:


Հիմա համեմատենք Haskell-ի հետ, որն ունի շատ հզոր տիպային համակարգ։ Եթե ​​տեսակը դրված է MyList-ի վրա, ապա «թվերի ցանկ» տեսակը պարզապես MyList Integer է: Haskell-ը թույլ չի տալիս մեզ պատահաբար տող ավելացնել ցանկին և համոզվում է, որ մենք ցուցակից որևէ տարր չենք դնում տողային փոփոխականի մեջ:


Haskell-ը կարող է շատ ավելի բարդ գաղափարներ արտահայտել ուղղակի տիպերի հետ։ Օրինակ, Num a => MyList a նշանակում է «Արժեքների MyList, որոնք նույն թվային տիպի են»: Այն կարող է լինել ամբողջ թվերի, լողացողների կամ ֆիքսված ճշգրտությամբ տասնորդականների ցանկ, բայց հաստատ երբեք չի լինի տողերի ցուցակ, որը ստուգվում է կոմպիլյացիայի ժամանակ։


Դուք կարող եք գրել ավելացնել ֆունկցիա, որն աշխատում է ցանկացած թվային տիպի հետ: Այս ֆունկցիան կունենա Num a => (a -> a -> a) տեսակը: Դա նշանակում է:

  • a-ն կարող է լինել ցանկացած թվային տեսակ (Num a =>):
  • Ֆունկցիան վերցնում է a տիպի երկու արգումենտ և վերադարձնում a տեսակը (a -> a -> a):

Վերջին օրինակը. Եթե ​​ֆունկցիայի տեսակը String -> String է, ապա այն վերցնում է տող և վերադարձնում տող: Բայց եթե դա String -> IO String է, ապա այն նաև կատարում է որոշ I/O: Սա կարող է լինել սկավառակ մուտք գործելը, ցանց մուտք գործելը, տերմինալից կարդալը և այլն:


Եթե ​​ֆունկցիան ունի տեսակ Ոչ IO, ապա մենք գիտենք, որ այն չի կատարում որևէ I/O գործողություն: Վեբ հավելվածում, օրինակ, դուք կարող եք պարզել, թե արդյոք ֆունկցիան փոխում է տվյալների բազան միայն նայելով դրա տեսակը: Ոչ մի դինամիկ և գրեթե ոչ մի ստատիկ լեզու չի կարող դա անել: Սա ամենահզոր տպագրական համակարգով լեզուների առանձնահատկությունն է:


Լեզուների մեծ մասում մենք պետք է գործ ունենանք ֆունկցիայի և բոլոր գործառույթների հետ, որոնք կանչվում են այնտեղից և այլն՝ փորձելով գտնել մի բան, որը փոխում է տվյալների բազան: Դա հոգնեցուցիչ գործընթաց է, և հեշտ է սխալվել: Իսկ Haskell տիպի համակարգը կարող է պարզ ու հուսալի պատասխանել այս հարցին։


Համեմատեք այս հզորությունը Go-ի հետ, որն ի վիճակի չէ արտահայտել MyList-ի պարզ գաղափարը, էլ չասած «մի ֆունկցիա, որը վերցնում է երկու արգումենտ՝ և՛ թվային, և՛ նույն տեսակի, և որը կատարում է I/O»։


Go մոտեցումը հեշտացնում է Go ծրագրավորման գործիքներ գրելը (մասնավորապես, կոմպիլյատորի իրականացումը կարող է պարզ լինել): Բացի այդ, սովորելու ավելի քիչ հասկացություններ կան: Թե ինչպես են այս առավելությունները համեմատվում էական սահմանափակումների հետ, սուբյեկտիվ հարց է: Այնուամենայնիվ, չի կարելի պնդել, որ Haskell-ն ավելի դժվար է սովորել, քան Go-ն, և որ Haskell-ի տիպային համակարգը շատ ավելի հզոր է, և որ Haskell-ը կարող է կանխել բազմաթիվ այլ տեսակի սխալների կազմավորումը:


Go-ն և Haskell-ը այնքան տարբեր լեզուներ են, որ դրանք խմբավորելը «ստատիկ լեզուների» նույն դասի մեջ կարող է ապակողմնորոշիչ լինել, չնայած տերմինը ճիշտ է օգտագործվում: Անվտանգության գործնական առավելությունների առումով Go-ն ավելի մոտ է դինամիկ լեզուներին, քան Haskell-ին:


Մյուս կողմից, որոշ դինամիկ լեզուներ ավելի ապահով են, քան որոշ ստատիկ լեզուներ: (Python-ը սովորաբար համարվում է շատ ավելի անվտանգ, քան C-ն): Երբ խոսքը վերաբերում է ստատիկ կամ դինամիկ լեզուների վերաբերյալ ընդհանրացումներին՝ որպես խմբերի, մի մոռացեք լեզուների միջև եղած հսկայական տարբերությունների մասին:

Տիպային համակարգերի հնարավորությունների տարբերությունների կոնկրետ օրինակներ

Ավելի հզոր տիպային համակարգերում դուք կարող եք սահմանել սահմանափակումներ ավելի փոքր մակարդակներում: Ահա մի քանի օրինակներ, բայց մի կախեք դրանցից, եթե շարահյուսությունը պարզ չէ:


Go-ում կարող եք ասել «ավելացնել գործառույթը վերցնում է երկու ամբողջ թիվ և վերադարձնում է մի ամբողջ թիվ»:


func add(x int, y int) int (վերադարձ x + y)

Haskell-ում կարող եք ասել «գործառույթը վերցնում է ցանկացածթվային տիպ և վերադարձնում է նույն տիպի մի շարք».


f:: Num a => a -> a -> a ավելացնել x y = x + y

Idris-ում կարող եք ասել «ֆունկցիան վերցնում է երկու ամբողջ թիվ» և վերադարձնում է ամբողջ թիվ, բայց առաջին արգումենտը պետք է փոքր լինի երկրորդ արգումենտից»:


ավելացնել՝ (x: Nat) -> (y: Nat) -> (ավտո փոքր՝ LT x y) -> Nat ավելացնել x y = x + y

Եթե ​​փորձեք կանչել ավելացնել 2 1 ֆունկցիան, որտեղ առաջին արգումենտը մեծ է երկրորդից, ապա կոմպիլյատորը կմերժի ծրագիրը։ կազմման ժամանակ. Անհնար է գրել ծրագիր, որտեղ առաջին արգումենտն ավելի մեծ է, քան երկրորդը: Հազվագյուտ լեզուն ունի այս հնարավորությունը: Լեզուների մեծ մասում այս ստուգումը տեղի է ունենում գործարկման ժամանակ. մենք կգրեինք նման բան, եթե x >= y. բարձրացնել SomeError() .


Haskell-ում չկա համարժեք Idris օրինակի տիպին վերևում, իսկ Go-ում չկա ոչ Haskell օրինակին, ոչ էլ Idris օրինակին: Արդյունքում, Իդրիսը կարող է կանխել բազմաթիվ սխալներ, որոնք Haskell-ը չի կարող կանխել, իսկ Haskell-ը կարող է կանխել բազմաթիվ սխալներ, որոնք Go-ն չի նկատի: Երկու դեպքում էլ անհրաժեշտ են տիպային համակարգի լրացուցիչ հնարավորություններ՝ լեզուն ավելի բարդացնելու համար:

Որոշ ստատիկ լեզուների տիպային համակարգեր

Ահա որոշ լեզուների տիպային համակարգերի մոտավոր ցուցակը՝ ըստ հզորության աճի։ Այս ցուցակը ձեզ ընդհանուր պատկերացում կտա համակարգերի հզորության մասին, ձեզ հարկավոր չէ այն ընդունել որպես բացարձակ ճշմարտություն: Մեկ խմբում հավաքված լեզուները կարող են շատ տարբեր լինել միմյանցից: Յուրաքանչյուր տիպի համակարգ ունի իր տարօրինակությունները, և դրանցից շատերը շատ բարդ են:

  • C (1972), Go (2009)Այս համակարգերը ամենևին էլ հզոր չեն, առանց ընդհանուր տեսակների աջակցության: Հնարավոր չէ MyList տիպը դնել «ամբողջ թվերի ցուցակ», «տողերի ցուցակ» և այլն: Փոխարենը, դուք ստիպված կլինեք կատարել «չստորագրված արժեքների ցուցակ»: Ծրագրավորողը պետք է ձեռքով ասի «սա տողերի ցուցակ է» ամեն անգամ, երբ տողը վերցվում է ցանկից, և դա կարող է հանգեցնել գործարկման ժամանակի սխալի:
  • Java (1995), C# (2000)Երկու լեզուներն էլ աջակցում են ընդհանուր, այնպես որ կարող եք ասել MyList և ստացեք տողերի ցանկ, որոնց մասին գիտի կոմպիլյատորը և կարող է կիրառել՝ հակառակ տեսակի կանոնների: Ցանկի տարրերը կլինեն String տիպի, կոմպիլյատորը սովորականի պես կպարտադրի կանոնները, այնպես որ գործարկման ժամանակի սխալները քիչ հավանական են:
  • Haskell (1990), Rust (2010), Swift (2014)Այս բոլոր լեզուներն ունեն մի քանի առաջադեմ առանձնահատկություններ, ներառյալ ընդհանուր տեսակները, հանրահաշվական տվյալների տեսակները (ADTs) և տիպի դասերը կամ նմանատիպ որևէ այլ բան (համապատասխանաբար դասի տեսակներ, հատկություններ և արձանագրություններ): Rust-ը և Swift-ն ավելի հայտնի են, քան Haskell-ը, և դրանք գովազդվում են խոշոր կազմակերպությունների կողմից (համապատասխանաբար Mozilla և Apple):
  • Agda (2007), Իդրիս (2011)Այս լեզուներն աջակցում են կախյալ տիպերին, ինչը թույլ է տալիս ստեղծել այնպիսի տեսակներ, ինչպիսին է «գործառույթը, որը վերցնում է երկու ամբողջ թվեր x և y, որտեղ y-ը x-ից մեծ է»: Նույնիսկ «y-ը x-ից մեծ է» սահմանափակումն ուժի մեջ է մտնում կոմպիլյացիայի ժամանակ: Երբ կատարվի, y-ը երբեք փոքր կամ հավասար չի լինի x-ից, անկախ նրանից, թե ինչ կլինի: Շատ նուրբ, բայց կարևոր համակարգի հատկությունները կարելի է ստատիկ կերպով ստուգել այս լեզուներով: Շատ քիչ ծրագրավորողներ են ուսումնասիրում դրանք, բայց նրանք շատ են ոգևորված այս լեզուներով:

Հստակ շարժում կա դեպի ավելի հզոր տիպային համակարգեր, հատկապես, երբ չափվում է լեզուների ժողովրդականությամբ, այլ ոչ միայն լեզուների գոյության փաստով: Հատկանշական բացառություն է Go-ն, որը բացատրում է, թե ինչու են ստատիկ լեզուների շատ կողմնակիցներ այն համարում հետընթաց քայլ:


Երկրորդ խումբը (Java և C#) հիմնական լեզուներ են, հասուն և լայնորեն օգտագործվող:


Երրորդ խումբը հիմնական հոսք մտնելու շեմին է՝ Mozilla-ի (Rust) և Apple-ի (Swift) մեծ աջակցությամբ:


Չորրորդ խումբը (Իդրիսը և Աղդան) հեռու են հիմնական հոսքից, բայց դա կարող է փոխվել ժամանակի ընթացքում: Երրորդ խմբի լեզուները տասը տարի առաջ հեռու էին հիմնական հոսքից: