পাঠ ৩২ · ৫৮-এর মধ্যে · মডিউল ৭
Home / Courses / Concepts of Programming Languages & Compiler Design / স্ট্যাটিক বনাম ডায়নামিক টাইপিং

টাইপ সিস্টেম — স্ট্যাটিক বনাম ডায়নামিক টাইপিং

Type systems — static vs dynamic typing
৮ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • টাইপ সিস্টেমের সংজ্ঞা এবং L30-এর টাইপ-চেকিং-এর সাথে এর সম্পর্ক
  • স্ট্যাটিক টাইপিং কীভাবে কাজ করে এবং কোন ভাষাগুলো এটি ব্যবহার করে
  • ডায়নামিক টাইপিং কীভাবে কাজ করে এবং কোন ভাষাগুলো এটি ব্যবহার করে
  • একটি একই প্রোগ্রামে স্ট্যাটিক ও ডায়নামিক টাইপিং সত্যিকারভাবে ভিন্ন ফলাফল দেয় — কোড দিয়ে যাচাই

১ · টাইপ সিস্টেম কী

টাইপ সিস্টেমType Systemএকটি ভাষা তার এক্সপ্রেশন ও ভ্যালুগুলোর জন্য টাইপ অ্যাসাইন ও চেক করার জন্য যে নিয়মগুলো ব্যবহার করে তার সম্পূর্ণ সেট। হলো L30-এ দেখা বেসিক টাইপ-চেকিং নিয়মগুলোর (যেমন — যোগের দুই পাশ সংখ্যাসূচক হতে হবে) একটি পূর্ণাঙ্গ, ভাষা-ডিজাইন-স্তরের সাধারণীকরণ। প্রতিটি ভাষারই একটি টাইপ সিস্টেম আছে — প্রশ্ন হলো, সেই সিস্টেম কখন তার নিয়মগুলো প্রয়োগ করে। এই প্রশ্নের উত্তরই ভাষাগুলোকে দুই বড় গোত্রে ভাগ করে।

২ · স্ট্যাটিক টাইপিং — কম্পাইল-টাইমে চেক

স্ট্যাটিক টাইপিংStatic Typingপ্রতিটি এক্সপ্রেশনের টাইপ প্রোগ্রাম চলার আগেই, কম্পাইল-টাইমে নির্ধারণ ও যাচাই করা হয়।-এ প্রতিটি এক্সপ্রেশনের টাইপ প্রোগ্রাম চলার আগেই, কম্পাইল-টাইমে নির্ধারণ ও যাচাই করা হয়। L02-এর "hello" + 5-এর মতো একটি টাইপ-এরর প্রোগ্রাম এক্সিকিউট হওয়ার আগেই ধরা পড়ে — প্রোগ্রামটি আসলে কখনো চালানোই হয় না। বাস্তব উদাহরণ: C, Java, Rust। এই ভাষাগুলোতে প্রতিটি ভ্যারিয়েবলের একটি নির্দিষ্ট, স্থির টাইপ থাকে যা কম্পাইলার আগে থেকেই জানে।

৩ · ডায়নামিক টাইপিং — রান-টাইমে চেক

ডায়নামিক টাইপিংDynamic Typingটাইপ নির্ধারণ ও যাচাই হয় রান-টাইমে, প্রোগ্রাম এক্সিকিউট হওয়ার সময় — ভ্যারিয়েবলের নিজের কোনো স্থির টাইপ থাকে না, শুধু তার বর্তমান ভ্যালুর টাইপ থাকে।-এ টাইপ নির্ধারণ ও যাচাই হয় রান-টাইমে, প্রোগ্রাম এক্সিকিউট হওয়ার সময়। এখানে একটি গুরুত্বপূর্ণ ফাইন পয়েন্ট — একই ভ্যারিয়েবল ভিন্ন ভিন্ন সময়ে ভিন্ন ভিন্ন টাইপের ভ্যালু ধারণ করতে পারে (ভ্যারিয়েবলের নিজের কোনো স্থির টাইপ নেই, শুধু তার বর্তমান ভ্যালুর টাইপ আছে)। একটি টাইপ-এরর তখনই দেখা দেয় যখন সেই নির্দিষ্ট অপারেশনটি সত্যিই এক্সিকিউট হয় — তার আগে নয়। বাস্তব উদাহরণ: Python, JavaScript, Ruby।

স্ট্যাটিক (C, Java, Rust) সোর্স কোড কম্পাইল-টাইম টাইপ চেক — পুরো প্রোগ্রাম এরর? → এক্সিকিউশন শুরুই হয় না এরর নেই → এক্সিকিউশন ডায়নামিক (Python, JS) সোর্স কোড এক্সিকিউশন সরাসরি শুরু প্রতিটি অপারেশনে রান-টাইমে চেক এরর শুধু ঐ অপারেশন চললে দেখা দেয়
স্ট্যাটিক টাইপিং পুরো প্রোগ্রাম আগেভাগে চেক করে; ডায়নামিক টাইপিং শুধু যা আসলে এক্সিকিউট হয় তাই চেক করে — এই পার্থক্যের একটি বাস্তব পরিণতি নিচের কোড সেলে দেখা যাবে।

৪ · সৎ ট্রেড-অফ

স্ট্যাটিক টাইপিং
এরর আগেভাগে ধরা পড়ে (রিলায়াবিলিটি জয়), কম্পাইলার এক্স্যাক্ট টাইপ জেনে ভালো অপ্টিমাইজড কোড জেনারেট করতে পারে (M12-এর ফোরশ্যাডো) — কিন্তু বেশি আপফ্রন্ট অ্যানোটেশন প্রয়োজন, দ্রুত স্ক্রিপ্টিং-এর জন্য কিছুটা কম নমনীয়।
ডায়নামিক টাইপিং
লেখা দ্রুত ও নমনীয় (রাইটেবিলিটি জয়) — টাইপ অ্যানোটেশন লিখতে হয় না — কিন্তু এরর ডিটেকশন রান-টাইম পর্যন্ত পিছিয়ে যায়, একটি কম-ব্যবহৃত কোড পাথে টাইপ-এরর প্রোডাকশন পর্যন্ত লুকিয়ে থাকতে পারে।
L04-এর ডিজাইন-ট্রেড-অফ নীতির সরাসরি উদাহরণ

কোনো টাইপ সিস্টেমই সব দিক থেকে "সেরা" নয় — এটি L04-এর রিলায়াবিলিটি-বনাম-রাইটেবিলিটি টেনশনের একটি নিখুঁত বাস্তব উদাহরণ। স্ট্যাটিক টাইপিং রিলায়াবিলিটির পক্ষে ঝুঁকে, ডায়নামিক টাইপিং রাইটেবিলিটির পক্ষে — দুটোই ডেলিবারেট ডিজাইন সিদ্ধান্ত, কোনোটাই "ভুল" নয়।

৫ · কোড দিয়ে যাচাই — একই এরর, দুই ভিন্ন ফলাফল

নিচের কোড সেলে একটি ছোট্ট প্রোগ্রাম আছে যার একটি শাখায় (branch) টাইপ-মিসম্যাচ আছে, কিন্তু কন্ডিশনটি সেই শাখা কখনো এক্সিকিউট করে না। একটি স্ট্যাটিক চেকার পুরো প্রোগ্রাম (উভয় শাখা) আগেভাগে যাচাই করবে — এবং বাতিল করবে, শাখাটি চলুক বা না চলুক। একটি ডায়নামিক ইভালুয়েটর শুধু যে শাখা আসলে এক্সিকিউট হয় সেটিই চেক করবে — এবং যেহেতু মিসম্যাচড শাখাটি কখনো চলে না, এটি সফলভাবে সম্পন্ন হবে।

Python
# toy AST নোড: ('num', v) | ('str', v) | ('binop', op, left, right) | ('if', cond, then_branch, else_branch)

def static_type_of(node):
    """স্ট্যাটিক চেকার — পুরো ট্রি (উভয় if-শাখা সহ) আগেভাগে যাচাই করে, প্রোগ্রাম চলার আগেই।"""
    kind = node[0]
    if kind == 'num':
        return 'int'
    if kind == 'str':
        return 'str'
    if kind == 'binop':
        _, op, left, right = node
        lt = static_type_of(left)
        rt = static_type_of(right)
        if op == '+' and lt == 'int' and rt == 'int':
            return 'int'
        if op == '+' and lt == 'str' and rt == 'str':
            return 'str'
        raise TypeError(f"[স্ট্যাটিক] টাইপ মিসম্যাচ: '{op}' {lt} ও {rt}-এর মধ্যে প্রযোজ্য নয়")
    if kind == 'if':
        _, cond, then_branch, else_branch = node
        # গুরুত্বপূর্ণ: স্ট্যাটিক চেকার cond-এর ভ্যালু না জেনেই উভয় শাখা চেক করে!
        then_t = static_type_of(then_branch)
        else_t = static_type_of(else_branch)
        if then_t != else_t:
            raise TypeError(f"[স্ট্যাটিক] if-এর দুই শাখার টাইপ ভিন্ন: {then_t} বনাম {else_t}")
        return then_t
    raise ValueError(f"অজানা নোড: {node}")


def dynamic_eval(node):
    """ডায়নামিক ইভালুয়েটর — শুধু যে শাখা আসলে এক্সিকিউট হয় সেটিই চেক ও ইভালুয়েট করে।"""
    kind = node[0]
    if kind == 'num':
        return node[1]
    if kind == 'str':
        return node[1]
    if kind == 'binop':
        _, op, left, right = node
        lv = dynamic_eval(left)
        rv = dynamic_eval(right)
        if op == '+' and isinstance(lv, int) and isinstance(rv, int):
            return lv + rv
        if op == '+' and isinstance(lv, str) and isinstance(rv, str):
            return lv + rv
        raise TypeError(f"[ডায়নামিক] রানটাইম টাইপ এরর: {lv!r} + {rv!r}")
    if kind == 'if':
        _, cond, then_branch, else_branch = node
        # গুরুত্বপূর্ণ: শুধু cond অনুযায়ী নির্বাচিত শাখাটিই ইভালুয়েট হয় -- অন্যটি কখনো ছোঁয়াই হয় না!
        return dynamic_eval(then_branch) if cond else dynamic_eval(else_branch)
    raise ValueError(f"অজানা নোড: {node}")


# প্রোগ্রাম: if False then ("hello" + 5) [টাইপ-মিসম্যাচড, কিন্তু কখনো চলবে না] else (3 + 4)
program = ('if', False,
           ('binop', '+', ('str', "hello"), ('num', 5)),   # then-শাখা: মিসম্যাচড, অচল
           ('binop', '+', ('num', 3), ('num', 4)))          # else-শাখা: সঠিক, এটাই চলবে

print("প্রোগ্রাম: if False then (\"hello\"+5) else (3+4)\n")

print("--- স্ট্যাটিক চেকার (উভয় শাখা আগেভাগে চেক করে) ---")
try:
    t = static_type_of(program)
    print(f"গৃহীত, ফলাফল টাইপ: {t}")
except TypeError as e:
    print(f"বাতিল! {e}")

print("\n--- ডায়নামিক ইভালুয়েটর (শুধু চলমান শাখা চেক করে) ---")
try:
    v = dynamic_eval(program)
    print(f"সফলভাবে চলল, ফলাফল: {v}")
except TypeError as e:
    print(f"রানটাইম এরর! {e}")

    
লক্ষ্য করুন — static_type_of cond-এর ভ্যালু (True/False) একেবারেই দেখে না; এটি then ও else — দুটো শাখাই চেক করে, কারণ কম্পাইল-টাইমে কে জানে কোন শাখা রান-টাইমে চলবে? তাই মিসম্যাচড then-শাখাটি খুঁজে পেয়ে পুরো প্রোগ্রাম বাতিল করে। অন্যদিকে dynamic_eval cond-এর উপর ভিত্তি করে সিদ্ধান্ত নেয় কোন শাখায় যাবে — যেহেতু cond = False, এটি then-শাখাটি স্পর্শই করে না, তাই সেই শাখার টাইপ-এরর কখনো দেখা দেয় না।
মূল কথা · Key takeaway

স্ট্যাটিক টাইপিং প্রোগ্রামের সম্ভাব্য সব পথ আগেভাগে যাচাই করে — নিরাপদ কিন্তু কঠোর। ডায়নামিক টাইপিং শুধু বাস্তবে নেওয়া পথ যাচাই করে — নমনীয় কিন্তু ঝুঁকিপূর্ণ, কারণ একটি বাগ কম-ব্যবহৃত কোড পাথে লুকিয়ে থেকে প্রোডাকশনে হঠাৎ আঘাত হানতে পারে। L34-এ দেখা যাবে টাইপ ইনফারেন্স কীভাবে দুটোর সেরাটা একসাথে পাওয়ার চেষ্টা করে।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ উপরের কোড সেলে যদি প্রোগ্রামের cond-কে False-এর বদলে True করা হয়, তাহলে ডায়নামিক ইভালুয়েটরের আউটপুট কী হবে — এবং স্ট্যাটিক চেকারের আউটপুট কি পাল্টাবে?

cond = True করলে dynamic_eval এখন then-শাখা ("hello" + 5) এক্সিকিউট করবে, ফলে একটি রানটাইম TypeError ছুড়বে — এবার ডায়নামিক ইভালুয়েটরও ব্যর্থ হবে। কিন্তু স্ট্যাটিক চেকারের আউটপুট একেবারেই পাল্টাবে না — static_type_of কখনোই cond-এর ভ্যালু দেখে না, তাই উভয় ক্ষেত্রেই একই কারণে (then-শাখার মিসম্যাচ) বাতিল করবে। এটাই মূল পয়েন্ট — স্ট্যাটিক চেকারের সিদ্ধান্ত cond-এর মান থেকে সম্পূর্ণ স্বাধীন।

প্র ০২ "ডায়নামিক টাইপিং মানে টাইপ-চেকিং একেবারে হয় না" — এই ধারণাটি কেন ভুল?

ডায়নামিক টাইপিং টাইপ-চেকিং বাদ দেয় না — শুধু এর সময় পাল্টায়। উপরের dynamic_eval কোডেই প্রতিটি binop-এ isinstance() দিয়ে টাইপ চেক করা হচ্ছে — এটি এখনো একটি টাইপ চেক, শুধু এটি প্রতিটি অপারেশন এক্সিকিউট হওয়ার ঠিক মুহূর্তে ঘটে, কম্পাইল করার সময় নয়। তাই "no type checking" নয় — বরং "runtime type checking, per-operation।"

প্র ০৩ একটি প্রোডাকশন সিস্টেমে "ডায়নামিক টাইপিং-এর একটি টাইপ-এরর মাসের পর মাস অলক্ষিত থেকে যাওয়া" — এই ঝুঁকিটি কীভাবে বাস্তবে ঘটতে পারে, উপরের কোড সেলের উদাহরণের আলোকে?

কল্পনা করুন উপরের program-এর মতো একটি if-শাখা আছে যা শুধু কোনো বিরল এজ-কেসে (যেমন একটি নির্দিষ্ট এরর কন্ডিশন, বছরে হয়তো একবার ঘটে) সত্যি হয়। সেই শাখায় একটি টাইপ-মিসম্যাচ থাকতে পারে যা মাসের পর মাস কোনো সমস্যা তৈরি করে না — কারণ ডায়নামিক ইভালুয়েটর সেই শাখা কখনো ছোঁয়ইনি। যেদিন সেই বিরল কন্ডিশনটি অবশেষে সত্যি হয়, প্রোডাকশনে হঠাৎ একটি TypeError ক্র্যাশ ঘটবে। একটি স্ট্যাটিক চেকার এই একই বাগ ডেভেলপমেন্টের প্রথম দিনেই ধরে ফেলত — শাখাটি চলুক বা না চলুক।

অনুশীলন

  1. পরীক্ষা করুন: উপরের কোড সেলে program-এর else-শাখাটিকেও একটি মিসম্যাচড এক্সপ্রেশন (যেমন ('binop', '+', ('num', 3), ('str', "x"))) দিয়ে বদলে দিন এবং Run চাপুন — এখন cond = False থাকা সত্ত্বেও ডায়নামিক ইভালুয়েটরের কী হবে?

    এখন উভয় শাখাই টাইপ-মিসম্যাচড — then-শাখা ("hello"+5) ও else-শাখা (3+"x") দুটোই। cond = False হওয়ায় dynamic_eval এখন else-শাখা এক্সিকিউট করবে — যেহেতু এটিও মিসম্যাচড, ডায়নামিক ইভালুয়েটরও এবার একটি TypeError ছুড়বে। এটি প্রমাণ করে ডায়নামিক ইভালুয়েটরের ব্যর্থতা cond-এর উপর নির্ভরশীল, কিন্তু স্ট্যাটিক চেকারের ব্যর্থতা কখনোই নয় — সে দুই শাখাই সবসময় চেক করে।

  2. চিন্তা করুন: C ও Python — দুটো ভাষাতেই আপনি হয়তো কোড লিখেছেন। প্রতিটির জন্য একটি বাস্তব পরিস্থিতি চিন্তা করুন যেখানে সেই ভাষার টাইপিং পলিসি (স্ট্যাটিক বা ডায়নামিক) সরাসরি একটি সুবিধা দিয়েছে।

    C-এর মতো একটি বড়, দীর্ঘমেয়াদী প্রোডাকশন কোডবেসে স্ট্যাটিক টাইপিং রিফ্যাক্টরিং-এর সময় হাজারো সম্ভাব্য টাইপ-মিসইউজ কম্পাইল-টাইমেই ধরে দেয় — কোড রান না করেই। Python-এ একটি ছোট ডেটা-এক্সপ্লোরেশন স্ক্রিপ্ট বা প্রোটোটাইপে ডায়নামিক টাইপিং টাইপ অ্যানোটেশন ছাড়াই দ্রুত আইডিয়া টেস্ট করতে দেয় — লেখার গতি এখানে বেশি গুরুত্বপূর্ণ, কারণ কোডটি হয়তো একবারই চলবে।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

আগের পাঠ
L31 · সিনট্যাক্স-ডাইরেক্টেড ট্রান্সলেশন