वे डेटा अपडेट पर एक साफ अमूर्तता प्रदान करते हैं, और वास्तव में कभी भी "आवश्यक" नहीं होते हैं। वे आपको किसी समस्या के बारे में एक अलग तरीके से कारण बताते हैं।
सी जैसी कुछ अनिवार्य/"ऑब्जेक्ट उन्मुख" प्रोग्रामिंग भाषाओं में, आपके पास मूल्यों के कुछ संग्रह (चलो उन्हें "structs" कहते हैं) की परिचित अवधारणा है और संग्रह में प्रत्येक मान को लेबल करने के तरीके (लेबल आमतौर पर होते हैं) "फ़ील्ड" कहा जाता है)।
typedef struct { /* defining a new struct type */
float x; /* field */
float y; /* field */
} Vec2;
typedef struct {
Vec2 col1; /* nested structs */
Vec2 col2;
} Mat2;
फिर आप तो जैसे इस नव परिभाषित प्रकार के मूल्यों को बना सकते हैं:: यह इस तरह की एक परिभाषा की ओर जाता है
data Vec2 =
Vec2
{ vecX :: Float
, vecY :: Float
}
data Mat2 =
Mat2
{ matCol1 :: Vec2
, matCol2 :: Vec2
}
:
Vec2 vec = { 2.0f, 3.0f };
/* Reading the components of vec */
float foo = vec.x;
/* Writing to the components of vec */
vec.y = foo;
Mat2 mat = { vec, vec };
/* Changing a nested field in the matrix */
mat.col2.x = 4.0f;
हास्केल में इसी तरह, हम डेटा प्रकार है
इस डेटा प्रकार का उपयोग इस प्रकार किया जाता है:
let vec = Vec2 2 3
-- Reading the components of vec
foo = vecX vec
-- Creating a new vector with some component changed.
vec2 = vec { vecY = foo }
mat = Mat2 vec2 vec2
हालांकि, हास्केल में, डेटा संरचना में नेस्टेड फ़ील्ड को बदलने का कोई आसान तरीका नहीं है। ऐसा इसलिए है क्योंकि आपको उस मूल्य के चारों ओर सभी रैपिंग ऑब्जेक्ट्स को फिर से बनाने की आवश्यकता है, जिन्हें आप बदल रहे हैं, क्योंकि हास्केल मान अपरिवर्तनीय हैं। आप हास्केल में ऊपर की तरह एक मैट्रिक्स है, और मैट्रिक्स में ऊपरी दाएँ सेल बदलना चाहते हैं, तो आप इस लिखने के लिए है:
mat2 = mat { matCol2 = (matCol2 mat) { vecX = 4 } }
यह काम करता है, लेकिन यह अनाड़ी लग रहा है। तो, कोई भी किसके साथ आया, मूल रूप से यह है: यदि आप दो चीजों को एक साथ समूहित करते हैं: एक मूल्य के "गेटटर" (जैसे vecX
और matCol2
ऊपर) इसी कार्य के साथ, गेटटर से संबंधित डेटा संरचना को देखते हुए, क्या कर सकते हैं उस मान के साथ एक नई डेटा संरचना बनाएं, आप बहुत साफ चीजें करने में सक्षम हैं। उदाहरण के लिए:
data Data = Data { member :: Int }
-- The "getter" of the member variable
getMember :: Data -> Int
getMember d = member d
-- The "setter" or more accurately "updater" of the member variable
setMember :: Data -> Int -> Data
setMember d m = d { member = m }
memberLens :: (Data -> Int, Data -> Int -> Data)
memberLens = (getMember, setMember)
लेंस को लागू करने के कई तरीके हैं; इस पाठ के लिए, मान लें कि एक लेंस उपरोक्त की तरह है:
type Lens a b = (a -> b, a -> b -> a)
आईई। यह किसी प्रकार के a
के लिए गेटर और एक सेटटर का संयोजन है जिसमें b
प्रकार का क्षेत्र है, इसलिए ऊपर memberLens
Lens Data Int
होगा। यह हमें क्या करने देता है?
ठीक है, पहले दो सरल कार्यों है कि एक लेंस से getters और setters निकालने बनाते हैं:
getL :: Lens a b -> a -> b
getL (getter, setter) = getter
setL :: Lens a b -> a -> b -> a
setL (getter, setter) = setter
अब, हम सामान से अधिक सार संक्षेप शुरू कर सकते हैं। आइए स्थिति को फिर से लें, कि हम "दो कहानियों को गहरा" मान संशोधित करना चाहते हैं। हम एक और लेंस के साथ एक डेटा संरचना जोड़ें:
data Foo = Foo { subData :: Data }
subDataLens :: Lens Foo Data
subDataLens = (subData, \ f s -> f { subData = s }) -- short lens definition
अब, चलो एक समारोह है कि दो लेंस तैयार करता जोड़ें:
(#) :: Lens a b -> Lens b c -> Lens a c
(#) (getter1, setter1) (getter2, setter2) =
(getter2 . getter1, combinedSetter)
where
combinedSetter a x =
let oldInner = getter1 a
newInner = setter2 oldInner x
in setter1 a newInner
कोड तरह का जल्दी से लिखा है, लेकिन मुझे लगता है यह स्पष्ट है कि वह क्या करता : गेटर्स बस रचित हैं; आपको आंतरिक डेटा मान मिलता है, और फिर आप अपना क्षेत्र पढ़ते हैं। सेटर, जब इसे a
पर x
के नए आंतरिक फ़ील्ड मान के साथ कुछ मान बदलने के लिए माना जाता है, तो पहले पुरानी आंतरिक डेटा संरचना को पुनर्प्राप्त करता है, इसके आंतरिक क्षेत्र को सेट करता है, और फिर नई आंतरिक डेटा संरचना के साथ बाहरी डेटा संरचना को अपडेट करता है।
increment :: Lens a Int -> a -> a
increment l a = setL l a (getL l a + 1)
हम इस कोड है, तो यह स्पष्ट हो जाता कि वह क्या करता:
अब, चलो एक समारोह है कि बस एक लेंस के मूल्य वृद्धि कर देता है बनाते हैं,
d = Data 3
print $ increment memberLens d -- Prints "Data 4", the inner field is updated.
अब क्योंकि हम लेंस बना सकता है, हम भी ऐसा कर सकते हैं:
f = Foo (Data 5)
print $ increment (subDataLens#memberLens) f
-- Prints "Foo (Data 6)", the innermost field is updated.
क्या लेंस संकुल के सभी w करने के लिए अनिवार्य रूप से है लेंस की इस अवधारणा को रैप करें - एक "सेटर" और "गेटर" का समूह एक साफ पैकेज में है जो उन्हें उपयोग करना आसान बनाता है। एक विशेष लेंस कार्यान्वयन में, एक लिखने में सक्षम हो जाएगा:
with (Foo (Data 5)) $ do
subDataLens . memberLens $= 7
तो, आप बहुत कोड के सी संस्करण के करीब; डेटा संरचनाओं के पेड़ में नेस्टेड मानों को संशोधित करना बहुत आसान हो जाता है।
लेंस इस से कुछ भी अधिक नहीं हैं: कुछ डेटा के हिस्सों को संशोधित करने का एक आसान तरीका। क्योंकि उनके कारण कुछ अवधारणाओं के कारण यह बहुत आसान हो जाता है, वे उन परिस्थितियों में व्यापक उपयोग देखते हैं जहां आपके पास डेटा संरचनाओं के विशाल सेट होते हैं जिन्हें विभिन्न तरीकों से एक-दूसरे से बातचीत करना पड़ता है।
लेंस के पेशेवरों और विपक्ष के लिए, a recent question here on SO देखें।
आप एडवर्ड Kmett के [लेंस: एक कार्यात्मक इंपीरेटिव] (http://www.youtube.com/watch?v=efv0SQNde5Q) बात देखने का आनंद ले सकते हैं। यह स्कैला में प्रस्तुत किया गया है, लेकिन हास्केल में लेंस की उपयोगिता का अनुवाद स्पष्ट होना चाहिए। –