খুব তাড়াতাড়ি: একটি প্রতিস্থাপন "সাধারণভাবে স্বচ্ছ" যদি "বিকল্পের মতো প্রতিস্থাপনের মতো হয়" এবং কোনও ফাংশন "খাঁটি" হয় যদি এর সমস্ত প্রভাব তার প্রত্যাবর্তন মূল্যে থাকে। এগুলি উভয়ই সুনির্দিষ্ট করা যেতে পারে তবে এটি লক্ষণীয় যে এগুলি একরকম নয় বা এমনকি অন্যটিও বোঝায় না vital
এখন বন্ধ সম্পর্কে কথা বলা যাক।
বিরক্তিকর (বেশিরভাগ বিশুদ্ধ) "ক্লোজারগুলি"
বন্ধগুলি ঘটে কারণ আমরা ল্যাম্বডা শব্দটি মূল্যায়ন করার সাথে সাথে পরিবেশের অনুসন্ধান হিসাবে আমরা (আবদ্ধ) ভেরিয়েবলকে ব্যাখ্যা করি। সুতরাং, আমরা যখন কোনও মূল্যায়নের ফলাফল হিসাবে ল্যাম্বডা টার্মটি ফিরিয়ে দেব তখন এর অভ্যন্তরের ভেরিয়েবলগুলি সংজ্ঞায়িত হওয়ার পরে তারা যে মানগুলি নিয়েছিল তা "বন্ধ" হয়ে যাবে have
প্লেইন ল্যাম্বদা ক্যালকুলাসে এটি এক ধরণের তুচ্ছ এবং পুরো ধারণাটি কেবল অদৃশ্য হয়ে যায়। এটি প্রদর্শনের জন্য, এখানে তুলনামূলকভাবে কম লাইটওয়েট ল্যাম্বডা ক্যালকুলাস দোভাষী:
-- untyped lambda calculus values are functions
data Value = FunVal (Value -> Value)
-- we write expressions where variables take string-based names, but we'll
-- also just assume that nobody ever shadows names to avoid having to do
-- capture-avoiding substitutions
type Name = String
data Expr
= Var Name
| App Expr Expr
| Abs Name Expr
-- We model the environment as function from strings to values,
-- notably ignoring any kind of smooth lookup failures
type Env = Name -> Value
-- The empty environment
env0 :: Env
env0 _ = error "Nope!"
-- Augmenting the environment with a value, "closing over" it!
addEnv :: Name -> Value -> Env -> Env
addEnv nm v e nm' | nm' == nm = v
| otherwise = e nm
-- And finally the interpreter itself
interp :: Env -> Expr -> Value
interp e (Var name) = e name -- variable lookup in the env
interp e (App ef ex) =
let FunVal f = interp e ef
x = interp e ex
in f x -- application to lambda terms
interp e (Abs name expr) =
-- augmentation of a local (lexical) environment
FunVal (\value -> interp (addEnv name value e) expr)
addEnv
যখন আমরা একটি নতুন নাম দিয়ে পরিবেশকে বাড়িয়ে তুলি তখন গুরুত্বপূর্ণ অংশটি লক্ষ্য করা যায় । এই ফাংশনটিকে ব্যাখ্যা করা Abs
ট্র্যাকশন টার্মের (ল্যাম্বডা টার্ম) কেবল "ভিতরে" বলা হয় " যখনই আমরা কোনও Var
শব্দটির মূল্যায়ন করি তখনই পরিবেশ "আপ" হয়ে যায় এবং তাই Var
এর Name
মধ্যে Env
যেটি উল্লেখ করা হয়েছে তাতে Abs
সংশ্লেষটি এতে থাকা ট্রেস দ্বারা ধরা পড়ে Var
।
এখন, আবার, সরল এলসি পদে এটি বিরক্তিকর। এর অর্থ হ'ল বাউন্ড ভেরিয়েবলগুলি যতক্ষণ না কারও কাছে যত্নশীল ants তারা পরিবেশে যে মানগুলি বর্ণনাকৃতভাবে বিন্দুতে স্কোপ করা হয়েছে তেমনভাবে অবিলম্বে এবং তত্ক্ষণাত মূল্যায়ন করে।
এটিও (প্রায়) খাঁটি। আমাদের ল্যাম্বডা ক্যালকুলাসের যে কোনও পদটির একমাত্র অর্থ তার ফেরতের মান দ্বারা নির্ধারিত হয়। একমাত্র ব্যতিক্রম হ'ল অ-সমাপ্তির পার্শ্ব-প্রতিক্রিয়া যা ওমেগা শব্দটির দ্বারা প্রতিপন্ন হয়েছে:
-- in simple LC syntax:
--
-- (\x -> (x x)) (\x -> (x x))
omega :: Expr
omega = App (Abs "x" (App (Var "x")
(Var "x")))
(Abs "x" (App (Var "x")
(Var "x")))
আকর্ষণীয় (অপরিষ্কার) বন্ধ
এখন নির্দিষ্ট ব্যাকগ্রাউন্ডে উপরের প্লেইন এলসিতে বর্ণিত বন্ধগুলি বিরক্তিকর কারণ আমরা যে সমস্ত ভেরিয়েবলগুলি বন্ধ করে দিয়েছি তার সাথে ইন্টারঅ্যাক্ট করতে সক্ষম হওয়ার কোনও ধারণা নেই। বিশেষত, "ক্লোজার" শব্দটি নিম্নলিখিত জাভাস্ক্রিপ্টের মতো কোড চালিত করে
> function mk_counter() {
var n = 0;
return function incr() {
return n += 1;
}
}
undefined
> var c = mk_counter()
undefined
> c()
1
> c()
2
> c()
3
এটি প্রমাণ করে যে আমরা n
অভ্যন্তরীণ ফাংশনটিতে ভেরিয়েবলটি বন্ধ করে দিয়েছি incr
এবং কলিং incr
অর্থপূর্ণভাবে সেই ভেরিয়েবলের সাথে ইন্টারেক্ট করে। mk_counter
খাঁটি, তবে incr
এটি অশুচি (এবং রেফারেন্সিয়ালি স্বচ্ছ নয়)
এই দুটি দৃষ্টান্তের মধ্যে কী পার্থক্য রয়েছে?
"পরিবর্তনশীল" ধারণা
আমরা যদি সরল এলসি অর্থে প্রতিস্থাপন এবং বিমূর্ততাটির অর্থ কী তা যদি আমরা লক্ষ্য করি তবে আমরা লক্ষ্য করব যে সেগুলি নির্ধারিতভাবে সরল। ভেরিয়েবলগুলি আক্ষরিক অর্থে তাত্ক্ষণিক পরিবেশের অনুসন্ধান ছাড়া আর কিছুই নয় । লাম্বডা বিমূর্ততা আক্ষরিকভাবে অভ্যন্তরীণ প্রকাশকে মূল্যায়নের জন্য একটি বর্ধিত পরিবেশ তৈরি করা ছাড়া আর কিছুই নয় । আমরা যে ধরণের আচরণ mk_counter
/ আচরণ দেখেছি তার জন্য এই মডেলটিতে কোনও স্থান নেই incr
কারণ কোনও বৈচিত্রের অনুমতি নেই।
অনেকের কাছে এটি "ভেরিয়েবল" এর অর্থ হ'ল। প্রকরণ। তবে, শব্দার্থবিজ্ঞানীরা এলসিতে ব্যবহৃত ভেরিয়েবল এবং জাভাস্ক্রিপ্টে ব্যবহৃত "ভেরিয়েবল" ধরণের মধ্যে পার্থক্য করতে পছন্দ করেন। এটি করার জন্য, তারা পরবর্তীকে একটি "পরিবর্তনীয় সেল" বা "স্লট" বলে অভিহিত করে।
এই নামকরণটি গণিতের "পরিবর্তনশীল" এর দীর্ঘ historicalতিহাসিক ব্যবহার অনুসরণ করে যেখানে এটি "অজানা" এর মতো আরও কিছু বোঝায়: (গাণিতিক) এক্সপ্রেশন "x + x" x
সময়ের সাথে পরিবর্তিত হতে দেয় না , পরিবর্তে এর অর্থ নির্বিশেষে অর্থ বোঝাতে বোঝানো হয় এর (একক, ধ্রুবক) মান x
লাগে।
সুতরাং, আমরা "স্লট" বলি মানগুলিকে স্লটে রাখার এবং এগুলি নেওয়ার ক্ষমতাকে জোর দেওয়ার জন্য।
বিভ্রান্তিতে আরও যোগ করার জন্য, জাভাস্ক্রিপ্টে এই "স্লটগুলি" ভেরিয়েবলের মতো দেখতে পাওয়া যায়: আমরা লিখি
var x;
একটি তৈরি করতে এবং তারপরে যখন আমরা লিখি
x;
এটি আমাদের সেই স্লটে বর্তমানে সঞ্চিত মান সন্ধান করার ইঙ্গিত দেয়। এটি আরও পরিষ্কার করার জন্য, খাঁটি ভাষাগুলি স্লটকে (গাণিতিক, ল্যাম্বডা ক্যালকুলাস) নাম হিসাবে নেওয়ার কথা ভাবেন। এই ক্ষেত্রে আমাদের যখন স্লট পাওয়া বা রাখা হয় তখন আমাদের স্পষ্টভাবে লেবেল করতে হবে। এই জাতীয় স্বরলিপি দেখতে দেখতে ঝোঁক
-- create a fresh, empty slot and name it `x` in the context of the
-- expression E
let x = newSlot in E
-- look up the value stored in the named slot named `x`, return that value
get x
-- store a new value, `v`, in the slot named `x`, return the slot
put x v
এই স্বরলিপিটির সুবিধাটি হ'ল এখন আমাদের গাণিতিক পরিবর্তনশীল এবং পরিবর্তনীয় স্লটগুলির মধ্যে দৃ firm় পার্থক্য রয়েছে। ভেরিয়েবলগুলি স্লটগুলিকে তাদের মান হিসাবে গ্রহণ করতে পারে তবে একটি ভেরিয়েবল দ্বারা নির্দিষ্ট নির্দিষ্ট স্লটটি তার পুরো ক্ষেত্র জুড়ে স্থির থাকে।
এই স্বরলিপিটি ব্যবহার করে আমরা mk_counter
উদাহরণটি পুনরায় লিখতে পারি (এবার হাসকলের মতো সিনট্যাক্সে, যদিও সিদ্ধান্তহীনভাবে অ-হাস্কেলের মতো শব্দার্থক):
mkCounter =
let x = newSlot
in (\() -> let old = get x
in get (put x (old + 1)))
এক্ষেত্রে আমরা এমন পদ্ধতি ব্যবহার করছি যা এই পরিবর্তনযোগ্য স্লটটিকে ব্যবহার করে। এটি বাস্তবায়নের জন্য আমাদের কেবল নামের মতো ধ্রুবক পরিবেশকেই বন্ধ করতে হবে না x
তবে প্রয়োজনীয় সমস্ত স্লট যুক্ত একটি পরিবর্তনীয় পরিবেশও বন্ধ করতে হবে। এটি "ক্লোজার" লোকেদের এত ভালবাসার সাধারণ ধারণার কাছাকাছি।
আবার, mkCounter
খুব অপরিষ্কার। এটি খুব রেফারেন্সালি অস্বচ্ছও। তবে লক্ষ্য করুন যে নাম ক্যাপচার বা বন্ধ হওয়া থেকে পার্শ্ব-প্রতিক্রিয়াগুলি উত্থাপিত হয় না বরং পরিবর্তে পরিবর্তনীয় সেলটি ক্যাপচার করে এবং এর উপর পার্শ্ব-কার্যকারী অপারেশনগুলি পছন্দ করে get
এবং put
।
শেষ পর্যন্ত, আমি মনে করি এটিই আপনার প্রশ্নের চূড়ান্ত উত্তর: বিশুদ্ধতা (গাণিতিক) ভেরিয়েবল ক্যাপচার দ্বারা প্রভাবিত হয় না বরং পরিবর্তে ক্যাপচারযুক্ত ভেরিয়েবলগুলির দ্বারা নাম পরিবর্তনশীল স্লটগুলিতে পার্শ্ব-প্রতিক্রিয়াশীল অপারেশন দ্বারা।
এটি কেবলমাত্র সেই ভাষাগুলিতে যেগুলি এলসির নিকটবর্তী হওয়ার চেষ্টা করে না বা বিশুদ্ধতা বজায় রাখার চেষ্টা করে না যে এই দুটি ধারণাগুলি প্রায়শই বিভ্রান্তির কারণ হয়ে থাকে confusion