లూవాలో టైప్ ఇన్ఫెరెన్స్

2006 నుండి, రాబ్లాక్స్ డెవలపర్లు రాబ్లాక్స్లో గేమ్లు మరియు ఇంటరాక్టివ్ అనుభవాలను సృష్టించడానికి లూవా ప్రోగ్రామింగ్ భాషను ఉపయోగిస్తున్నారు. రాబ్లాక్స్ డెవలపర్లు అన్ని వర్గాల నుండి మరియు అన్ని స్థాయిల అనుభవం ఉన్నవారు, మరియు వారి సృష్టి కూడా అంతే విభిన్నంగా ఉంటాయి.
ఆ సృష్టిలలో కొన్ని నిజంగా అత్యంత అధునాతన సాఫ్ట్వేర్. చాలా వాటిలో పదివేల లైన్ల కోడ్ ఉంటుంది.
లువా ఒక అద్భుతమైన ప్రోగ్రామింగ్ భాష మరియు మాకు అది చాలా ఇష్టం అయినప్పటికీ, వెబ్ డెవలపర్ కమ్యూనిటీ నేర్చుకుంటున్న అదే పాఠాన్ని మేము కూడా నేర్చుకుంటున్నాము: డైనమిక్గా టైప్ చేయబడిన ప్రోగ్రామింగ్ భాషలతో పెద్ద అప్లికేషన్లను వ్రాయడం కష్టం!
ఈ అంతరాన్ని పూరించడానికి మేము లూవావును సృష్టించాము.
దాని మూలంలో, లూవావు మొదటగా ఒక టైప్ ఇన్ఫరెన్స్ ఇంజిన్ మరియు రెండవదిగా ఒక టైప్ చెకర్. మేము ఈ తత్వాన్ని OCaml మరియు TypeScript కంపైలర్ల వంటి గతంలో విజయవంతమైన పనుల నుండి స్వీకరించాము.
అయితే, లూవా నిజానికి OCaml లేదా జావాస్క్రిప్ట్ రెండింటిలా ఉండదు. లూవాను సాధ్యమైనంత గొప్పగా చేయడానికి, లూవాను ప్రత్యేకంగా చేసే అంశాలను మనం కచ్చితంగా నమూనా చేయడం చాలా ముఖ్యం.
మొదట, కొన్ని ప్రాథమిక విషయాలను వివరిద్దాం.
టైప్ ఇన్ఫరెన్స్ 101
టైప్ ఇన్ఫరెన్స్ అంటే దాదాపు ఎల్లప్పుడూ ఏదో ఒక టైప్ A, మరొక టైప్ Bతో సమానంగా ఉండాలని గమనించడం. ఇది ఒక సరళీకరణ, కానీ మనం అనుకున్నంత తక్కువ కాదు.
ఒక చిన్న ఉదాహరణతో ప్రారంభిద్దాం:
function id(x)
return x
end
local a = 5
local b = id(a)మనం `id` యొక్క టైప్ను తనిఖీ చేసినప్పుడు, మనం రెండు ప్లేస్హోల్డర్ టైప్లను సృష్టిస్తాము: `x` యొక్క టైప్ మరియు ఫంక్షన్ యొక్క రిటర్న్ టైప్.
ఈ ప్లేస్హోల్డర్లు కేవలం ఒక విషయాన్ని సూచిస్తాయి: వాటి గురించి మనకు ఏమీ తెలియదు.
ఆ తర్వాత మనం `return` స్టేట్మెంట్ను విశ్లేషిస్తాము. ఫంక్షన్ ఏ రకమైన టైప్ను తిరిగి ఇచ్చినా, అది `x` యొక్క టైప్తో సమానంగా ఉంటుందని మనం గమనిస్తాము. `x` పై వేరే ఎలాంటి పరిమితులు లేనందున, `id` యొక్క తుది ఇన్ఫర్డ్ టైప్ జెనరిక్ ఫంక్షన్ `(T) -> T` అని నిర్ధారించబడుతుంది.
(లువావు ఇంకా జెనరిక్ ఫంక్షన్ల కోసం సింటాక్స్ను అందించదు, కానీ అంతర్గత డేటా నిర్మాణాలు వాటిని సూచించగలవు.)
b కోసం ఒక టైప్ను నిర్ధారించాల్సిన సమయం వచ్చినప్పుడు, మనం id గురించిన మన జ్ఞానాన్ని ఉపయోగించుకోవచ్చు. మనం id యొక్క టైప్ను తీసుకుని, దాని పారామీటర్ టైప్ను మన వద్ద ఉన్న కాంక్రీట్ ఆర్గ్యుమెంట్ టైప్కు బైండ్ చేస్తాము. ఫంక్షన్ యొక్క రిటర్న్ టైప్ ఇప్పటికే దాని పారామీటర్ టైప్కు బైండ్ చేయబడినందున, (T) -> T అనేది (number) -> numberగా ఇన్స్టెన్షియేట్ చేయబడుతుంది. దీని నుండి, b యొక్క టైప్ number అని మనం గ్రహించవచ్చు.
లువావు చేసే దాదాపు అన్ని టైప్ చెకింగ్ పనులు ఈ ఆలోచనకు ఒక పొడిగింపు.
బహుళ రిటర్న్లు
విస్తృతంగా ఉపయోగంలో ఉన్న దాదాపు ప్రతి ప్రోగ్రామింగ్ భాషలో, ఫంక్షన్లు ఖచ్చితంగా ఒక విలువను మాత్రమే తిరిగి ఇస్తాయి. చాలా భాషలు (ముఖ్యంగా ఫంక్షనల్ భాషలు) బహుళ విలువలను తిరిగి ఇవ్వడాన్ని సులభంగా మరియు సౌకర్యవంతంగా చేయడానికి తేలికపాటి ట్యూపుల్స్ను అందిస్తాయి.
ప్రతి ఫంక్షన్ 0 లేదా అంతకంటే ఎక్కువ విలువలను తిరిగి ఇవ్వడానికి అనుమతించడం ద్వారా లూవా ఈ ధోరణికి విరుద్ధంగా ఉంది. ఇక్కడ ఎలాంటి ట్యూపు మెకానిజం పనిచేయదు, ఎందుకంటే మొత్తం ట్యూపును ఒకే పేరుతో బైండ్ చేయడానికి మార్గం లేదు.
function take_five(a, b, c, d, e)
print(a, b, c, d, e)
end
function get_five()
return 1, 2, 3, 4, 5
end
-- a receives 1. The other return values are discarded.
local a = get_five()
-- foo, bar, and baz receive 1, 2, and 3, respectively.
-- Arguments 4 and 5 are discarded.
local foo, bar, baz = get_five()
-- the 5 return values from get_five are passed as
-- parameters to take_five
take_five(get_five())ఇది కొన్ని కొత్త సవాళ్లను విసురుతుంది. కింద ఉన్న `compose` యొక్క రకం (type) ఏమిటి?
function compose(f, g)
return function(...)
return f(g(...))
end
endఇతర భాషలలో, g యొక్క రిటర్న్ టైప్ f కు అందించే ఆర్గ్యుమెంట్ టైప్తో సమానంగా ఉండాలని మనం నిర్ధారించగలం. లూయాలో, f, g ద్వారా తిరిగి ఇవ్వబడిన వాటితో సమానమైన ఆర్గ్యుమెంట్ సంఖ్య మరియు టైప్లను అంగీకరించాలి.
లువావులో, మేము దీనిని టైప్ ప్యాక్ అని పిలిచే దానితో సూచిస్తాము. ఇది ఒక టైప్ను వివరించడానికి మేము ఉపయోగించే డేటా స్ట్రక్చర్ను చాలా పోలి ఉంటుంది, కానీ ఇది 0 లేదా అంతకంటే ఎక్కువ టైప్లను సూచిస్తుంది.
టైప్ల వలె, టైప్ ప్యాక్లు ప్లేస్హోల్డర్ల భావనకు మద్దతు ఇస్తాయి, వీటిని తర్వాత ఇతర ప్యాక్లకు బైండ్ చేయవచ్చు. టైప్ ప్యాక్లకు మరికొన్ని లక్షణాలు ఉన్నాయి: వాటి పొడవులు తెలిసినవిగా లేదా తెలియనివిగా ఉండవచ్చు మరియు తెలిసినట్లయితే, అవి స్థిరమైన లేదా మారుతూ ఉండే పరిమాణాన్ని కలిగి ఉండవచ్చు.
లువావు ఫంక్షన్లను ఒక జత టైప్ ప్యాక్లుగా మోడల్ చేస్తుంది: ఒకటి ఆర్గ్యుమెంట్ జాబితా కోసం మరియు మరొకటి రిటర్న్ విలువల కోసం.
ఒక జనరిక్ టైప్ ప్యాక్ను సూచించడానికి మనం A... అనే సింటాక్స్ను పరిగణిస్తే, మనం compose కోసం ఒక టైప్ను ఇలా వ్రాయవచ్చు:
((B...) -> C..., (A...) -> B...) -> (A...) -> C...
(లౌవా కూడా ఈ సింటాక్స్కు ఇంకా మద్దతు ఇవ్వడం లేదు. త్వరలో వస్తుంది!)
టేబుల్స్
Lua రాసేటప్పుడు టేబుల్స్ చాలా ముఖ్యమైనవి. అవి మన అరేలు, హ్యాష్ మ్యాప్లు, మరియు ఆబ్జెక్ట్లు అన్నీ కలిపినట్లుగా ఉంటాయి. Lua కోడ్ నుండి టేబుల్ రకాలను సరిగ్గా ఊహించడం చాలా ముఖ్యం.
Luauలో, మనం టేబుల్స్ను 4 వర్గాలుగా విభజిస్తాము:
- మనకు ఖచ్చితమైన నిర్మాణం తెలిసిన టేబుల్స్
- ముక్కలుగా నిర్మించబడిన టేబుల్స్
- ఫంక్షన్ పారామితులుగా పంపబడే టేబుల్-వంటివి, మరియు
- Roblox API డేటా రకాలు
వాటిని మేము సీల్డ్ టేబుల్స్, అన్సీల్డ్ టేబుల్స్, జెనరిక్ టేబుల్స్, మరియు నేటివ్ క్లాస్లు అని పిలుస్తాము.
సీల్డ్ టేబుల్స్
మేము పట్టుకోవాలనుకుంటున్న ఒక సాధారణ బగ్, టేబుల్ ప్రాపర్టీ అసైన్మెంట్లో ప్రాపర్టీ పేరును తప్పుగా రాయడం.
local some_table = {some_property=0}
-- oops. I got the name of the property wrong
some_table.sone_property = 55టేబుల్స్ సాధారణంగా డిఫాల్ట్గా సీల్ చేయబడి ఉంటాయి.
సీల్ చేయని టేబుల్స్
ఇడియోమాటిక్ లువాతో సజావుగా పనిచేయడానికి, బహుళ స్టేట్మెంట్లలో టేబుల్స్ను నిర్మించే ఫంక్షన్లు మరియు మాడ్యూల్స్కు మద్దతు ఇవ్వడానికి మనకు ఒక మార్గం అవసరం.
local Counter = {}
Counter.value = 0
function Counter.increment()
Counter.value = Counter.value + 1
return Counter.value
endఈ ఉదాహరణలో, మొదటి లైన్ను చూసి, `{}` అనే రకాన్ని ఊహించి, రెండవ లైన్లో ఒక టైప్ ఎర్రర్ రావడానికి కారణం కావడం తెలివితక్కువతనం, ఎందుకంటే `Counter` ఎప్పటికీ ఖాళీగానే ఉంటుందని మనం ఆశిస్తున్నాము.
అందువల్ల మేము Counter అనేది ఒక అన్సీల్డ్ టేబుల్ అని భావిస్తాము. లూవావు ఈ టేబుల్ యొక్క కచ్చితమైన ఆకారాన్ని సులభంగా తెలుసుకోగలదు, కానీ మేము దానిని విస్తరణకు తెరిచి ఉన్నట్లుగా పరిగణిస్తాము.
ఒక టేబుల్ అన్సీల్ చేయడాన్ని మరీ సులభతరం చేయకూడదని మేము కోరుకుంటున్నాము, కాబట్టి మేము కొన్ని సాధారణ హ్యూరిస్టిక్స్ను వర్తింపజేస్తాము:
- ఒక టేబుల్ యొక్క రకం అది అక్షరాలతో కూడిన ఖాళీ టేబుల్తో ఇనిషియలైజ్ చేయబడినప్పుడు అన్సీల్డ్గా పరిగణించబడుతుంది, మరియు
- మేము ఒక ఫంక్షన్ సిగ్నేచర్లో అన్సీల్డ్ టేబుల్ను ఎదుర్కొన్నప్పుడల్లా, దానిని సీల్డ్ టేబుల్గా మార్చుతాము.
ఇది మాకు చాలా మంచి ఉపయోగపడగలదు.
function new_counter()
local Counter = {}
Counter.value = 0 -- OK. Counter is unsealed.
function Counter.increment()
Counter.value = Counter.value + 1
return Counter.value
end
return Counter
end
local c = new_counter()
c.value_ = 5 -- Not allowed. c is a sealed table here.జనరిక్ టేబుల్స్
యానోటేట్ చేయని ఫంక్షన్ పారామితులతో వ్యవహరించేటప్పుడు, టేబుల్-లాగా ఉపయోగించబడే ఒక ఆర్గ్యుమెంట్ యొక్క ఖచ్చితమైన ఆకృతిని నిర్ధారించడం చాలా అరుదుగా సాధ్యమవుతుంది:
local function print_point(p)
print(‘X =’, p.X, ‘Y =’, p.Y)
endpకు X మరియు Y ఉన్నాయని మనకు తెలుసు, కానీ print ఫంక్షన్ ఏదైనా ప్రింట్ చేయగలదు కాబట్టి, అంతవరకే. ఆ ప్రాపర్టీలకు ఏ టైప్ అయినా ఉండవచ్చు. మరే ఇతర ప్రాపర్టీలు ఎన్ని అయినా ఉండవచ్చు. అది నిజానికి ఒక టేబుల్ అవసరం కూడా లేదు; అది Vector3 వంటి Roblox API టైప్ కూడా కావచ్చు.
(రోబ్లాక్స్ API గురించి మరికొంత సమాచారం త్వరలో)
OCaml మరియు కొన్ని ఇతర ప్రోగ్రామింగ్ భాషల వలె, Luau టేబుల్ పారామితులు రో పాలిమార్ఫిక్. Luauలో, మనం వాటిని జెనరిక్ టేబుల్స్ అని పిలుస్తాము. ఒక జెనరిక్ టేబుల్లో ఉన్నాయని అంచనా వేయబడిన ఫీల్డ్లు, కాల్ చేసే వారిపై విధించబడిన అవసరాలు. అవసరమైన నిర్మాణం అక్కడ ఉన్నంత కాలం ఇతర ప్రాపర్టీలకు అనుమతి ఉంది:
local a = print_point({X=3, Y=4})
local c = print_point({X=4, Y=3, Name='The Best Point'})
local b = print_point(Vector3.new(3, 4, 0))రాబ్లాక్స్ API
రాబ్లాక్స్ API మన ప్రతిష్టాత్మక డెవలపర్ కమ్యూనిటీకి చాలా శక్తివంతమైన సాధనాలను అందిస్తుంది. ఈ APIలో చాలా C++ క్లాస్లు ఉన్నాయి, వీటిని Lua లోకి ప్రతిబింబింపజేశారు.
లువా ఈ API గురించి తెలుసుకోవలసిన అవసరం ఉందని స్పష్టంగా తెలుస్తోంది. ఆ అవగాహనలో ఒక భాగం, Roblox క్లాస్ ఇన్స్టాన్స్లు వాస్తవానికి లువా టేబుల్స్ కాదనే జ్ఞానం. ఉదాహరణకు, అంతర్నిర్మిత `pairs()` ఫంక్షన్ను Roblox API టైప్ యొక్క ప్రాపర్టీలపై ఇటరేట్ చేయడానికి ఉపయోగించలేము.
మేము ఇప్పటివరకు లూవా కోసం వివరించిన టైప్ సిస్టమ్ పూర్తిగా స్ట్రక్చరల్ టైప్ సిస్టమ్. లూవా (మరియు లూవావు) టేబుల్స్ను అవి కలిగి ఉన్న ప్రాపర్టీల సమితిగా మాత్రమే పరిగణిస్తాయి. రాబ్లాక్స్ API ఈ నమూనాకు సరిపోదు. C++ ఒక నామమాత్రపు (nominal) టైప్ సిస్టమ్ను అందిస్తుంది, ఇక్కడ ప్రతి క్లాస్కు దాని స్వంత "స్వయంత్వత" (self-ness) ఉంటుంది. ఖచ్చితంగా ఒకే నిర్మాణాన్ని పంచుకున్నప్పటికీ, రెండు వేర్వేరు క్లాస్లు ఉండటం చాలా సాధారణం. ఈ లక్షణాన్ని నెరవేర్చే వాస్తవ రాబ్లాక్స్ క్లాస్లు ఉన్నాయి మరియు లూవావు వాటిని సరిగ్గా మోడల్ చేయాలి.
బిల్ట్ఇన్ రాబ్లాక్స్ క్లాస్ ఇన్స్టాన్స్ల కోసం టేబుల్-వంటి టైప్ను పరిచయం చేయడం ద్వారా మేము దీన్ని పరిష్కరిస్తాము. క్లాస్ టైప్లు టేబుల్ టైప్ల నుండి భిన్నంగా ఉంటాయి, ఎందుకంటే అవి అన్నింటినీ ఒకే రకమైన టైప్లతో అదే పద్ధతులను సపోర్ట్ చేసినప్పటికీ, వాటిని వేరు చేసే గుర్తింపులను కలిగి ఉంటాయి. అవి వారసత్వం అనే భావనను కూడా సపోర్ట్ చేస్తాయి, అవి మోడల్ చేయడానికి రూపొందించబడిన C++ క్లాస్ల మాదిరిగానే.
ముగింపు
లువా వంటి డైనమిక్ భాష నుండి స్టాటిక్ టైప్లను బయటకు తీయడం చాలా సవాళ్లను విసురుతుంది. ఈ సవాళ్లలో చాలా వరకు లువాకు ప్రత్యేకమైనవి, కానీ వాటిని పరిష్కరించడం చాలా వరకు సాధ్యమే. ఇది చాలా బాగా పనిచేస్తుందని మేము భావిస్తున్నాము! 🙂
ఆండీ ఫ్రిసెన్, లూవావు టైప్ చెకర్కు టెక్నికల్ లీడ్. వీడియో గేమ్లు, డెవలపర్ టూల్స్ మరియు ప్రోగ్రామింగ్ భాషల కలయికలో పనిచేయడానికి అతను ఉత్సాహంగా ఉన్నాడు.
రాబ్లాక్స్ కార్పొరేషన్ గానీ, ఈ బ్లాగ్ గానీ ఏ కంపెనీని లేదా సేవను ఆమోదించవు లేదా మద్దతు ఇవ్వవు. అలాగే, ఈ బ్లాగ్లో ఉన్న సమాచారం యొక్క ఖచ్చితత్వం, విశ్వసనీయత లేదా సంపూర్ణతకు సంబంధించి ఎటువంటి హామీలు లేదా వాగ్దానాలు ఇవ్వబడవు.


