फ़ंक्शन संरचना (कंप्यूटर विज्ञान): Difference between revisions
No edit summary |
|||
Line 11: | Line 11: | ||
==फ़ंक्शन कॉल कंपोज़ करना== | ==फ़ंक्शन कॉल कंपोज़ करना== | ||
उदाहरण के लिए, मान लीजिए हमारे पास दो फ़ंक्शन | उदाहरण के लिए, मान लीजिए हमारे पास दो फ़ंक्शन {{mvar|f}} और {{mvar|g}}, {{math|1=''z'' = ''f''(''y'')}} और {{math|1=''y'' = ''g''(''x'')}}. के रूप में हैं। उन्हें लिखने का तात्पर्य है कि हम पहले {{math|1=''y'' = ''g''(''x'')}} की गणना करते हैं और पुनः {{math|1=''z'' = ''f''(''y'')}} उपयोग करके {{mvar|y}} की गणना करते हैं। यहाँ C प्रोग्रामिंग लैंग्वेज में उदाहरण दिया गया है: | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c"> | ||
Line 19: | Line 19: | ||
z = f(y); | z = f(y); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
यदि हम मध्यवर्ती परिणाम को कोई नाम नहीं देते हैं तो चरणों को | यदि हम मध्यवर्ती परिणाम को कोई नाम नहीं देते हैं तो भिन्न-भिन्न चरणों को कम्पोज़ किया जा सकता है: | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c"> | ||
z = f(g(x)); | z = f(g(x)); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
लंबाई में अंतर के | लंबाई में अंतर के बाद भी, ये दोनों फंक्शन एक ही परिणाम की गणना करते हैं। दूसरे कार्यान्वयन के लिए कोड के केवल एक पंक्ति की आवश्यकता होती है और इसे बोलचाल की भाषा में "हाईली कम्पोज़" रूप कहा जाता है। उच्चतर संयुक्त रूपों का एक लाभ है पठनीयता और इससे आरक्षणीयता, क्योंकि इनमें कम कोड लाइनों की आवश्यकता होती है, जो किसी प्रोग्राम की "सरफेस एरिया" को कम करता है।<ref>{{harvtxt|Cox|1986}}, pp. 15–17</ref> डेमार्को और लिस्टर अनुभवजन्य रूप से सरफेस एरिया और रखरखाव के मध्य एक विपरीत संबंध को सत्यापित करते हैं।<ref>{{harvtxt|DeMarco|Lister|1995}}, pp. 133–135.</ref> दूसरी ओर, अत्यधिक कम्पोज़ फॉर्म का अति प्रयोग संभव हो सकता है। अत्यधिक फ़ंक्शंस के नेस्टिंग का विपरीत प्रभाव हो सकता है, जिससे कोड कम रखरखाव योग्य हो जाता है। | ||
[[स्टैक-आधारित भाषा]] में, | [[स्टैक-आधारित भाषा|स्टैक-आधारित लैंग्वेज]] में, फंक्शनल कॉम्पोजिसन और भी अधिक स्वाभाविक है: यह संयोजन द्वारा किया जाता है, जो सामान्यतः प्रोग्राम डिज़ाइन की प्राथमिक विधि है। [[फोर्थ (प्रोग्रामिंग भाषा)|फोर्थ प्रोग्रामिंग लैंग्वेज]] में निम्नलिखित उदाहरण प्रदर्शित किया गया है: | ||
g f | |||
जो | यह पोस्टफ़िक्स संयोजन टिप्पणी उस संबंधीय गणितीय नोटेशन के लिए है जो स्टैक पर पहले था, उसे लागू करती है, फिर f को लागू करती है, और परिणाम को स्टैक पर स्थापित कर देती है। | ||
==फ़ंक्शंस की संरचना का नामकरण== | ==फ़ंक्शंस की संरचना का नामकरण== |
Revision as of 10:31, 26 July 2023
कंप्यूटर विज्ञान में, फ़ंक्शन कॉम्पोजिसन, अधिक जटिल सबरूटीन बनाने के लिए सरल सबरूटीन्स को संयोजित करने की एक प्रक्रिया है। इस विधि में फ़ंक्शनों का संयोजन गणित में होने वाले फ़ंक्शनों के सामान्य संयोजन की तरह होता है, जहां प्रत्येक फ़ंक्शन के परिणाम को अगले फ़ंक्शन के तर्क के रूप में पारित किया जाता है और आख़िरी फ़ंक्शन का परिणाम, समूल विधि का परिणाम होता है।
प्रोग्रामर प्रायः फ़ंक्शंस को अन्य फ़ंक्शंस के परिणामों पर लागू करते हैं, और लगभग सभी प्रोग्रामिंग लैंग्वेज इसकी अनुमति देते हैं। कुछ विषयों में, फ़ंक्शंस का स्ट्रक्चर स्वयं में एक फ़ंक्शन के रूप में प्रदर्शित होता है, जिसका उपयोग बाद में किया जा सकता है। ऐसे फ़ंक्शन को सदैव परिभाषित किया जा सकता है परंतु प्रथम श्रेणी फ़ंक्शन वाले लैंग्वेज इसे सरल बनाती हैं।
फ़ंक्शन को सरलता से संयोजित करने की क्षमता रखरखाव और कोड के पुन: उपयोग के लिए फैक्टरिंग सबरूटीन्स को प्रोत्साहित करती है। अधिक सामान्यतः, संपूर्ण प्रोग्रामों का संयोजन करके बड़े सिस्टम निर्मित किए जा सकते हैं।
संक्षेप में कहें तो, फ़ंक्शन कॉम्पोजिसन उन फ़ंक्शंस पर लागू होती है जो डेटा की एक सीमित मात्रा पर काम करते हैं, प्रत्येक चरण इसे अगले को सौंपने से पहले क्रमिक रूप से संसाधित करता है। संभावित अनंत डेटा या अन्य कोडाटा पर कार्य करने वाले फ़ंक्शन को फ़िल्टर के रूप में जाना जाता है, और इसके अतिरिक्त एक पाइपलाइन सॉफ़्टवेयर में जुड़े होते हैं, जो फ़ंक्शन कॉम्पोजिसन के अनुरूप होता है और कनक्यूरेंट कंप्यूटिंग को निष्पादित कर सकता है ।
फ़ंक्शन कॉल कंपोज़ करना
उदाहरण के लिए, मान लीजिए हमारे पास दो फ़ंक्शन f और g, z = f(y) और y = g(x). के रूप में हैं। उन्हें लिखने का तात्पर्य है कि हम पहले y = g(x) की गणना करते हैं और पुनः z = f(y) उपयोग करके y की गणना करते हैं। यहाँ C प्रोग्रामिंग लैंग्वेज में उदाहरण दिया गया है:
float x, y, z;
// ...
y = g(x);
z = f(y);
यदि हम मध्यवर्ती परिणाम को कोई नाम नहीं देते हैं तो भिन्न-भिन्न चरणों को कम्पोज़ किया जा सकता है:
z = f(g(x));
लंबाई में अंतर के बाद भी, ये दोनों फंक्शन एक ही परिणाम की गणना करते हैं। दूसरे कार्यान्वयन के लिए कोड के केवल एक पंक्ति की आवश्यकता होती है और इसे बोलचाल की भाषा में "हाईली कम्पोज़" रूप कहा जाता है। उच्चतर संयुक्त रूपों का एक लाभ है पठनीयता और इससे आरक्षणीयता, क्योंकि इनमें कम कोड लाइनों की आवश्यकता होती है, जो किसी प्रोग्राम की "सरफेस एरिया" को कम करता है।[1] डेमार्को और लिस्टर अनुभवजन्य रूप से सरफेस एरिया और रखरखाव के मध्य एक विपरीत संबंध को सत्यापित करते हैं।[2] दूसरी ओर, अत्यधिक कम्पोज़ फॉर्म का अति प्रयोग संभव हो सकता है। अत्यधिक फ़ंक्शंस के नेस्टिंग का विपरीत प्रभाव हो सकता है, जिससे कोड कम रखरखाव योग्य हो जाता है।
स्टैक-आधारित लैंग्वेज में, फंक्शनल कॉम्पोजिसन और भी अधिक स्वाभाविक है: यह संयोजन द्वारा किया जाता है, जो सामान्यतः प्रोग्राम डिज़ाइन की प्राथमिक विधि है। फोर्थ प्रोग्रामिंग लैंग्वेज में निम्नलिखित उदाहरण प्रदर्शित किया गया है:
g f
यह पोस्टफ़िक्स संयोजन टिप्पणी उस संबंधीय गणितीय नोटेशन के लिए है जो स्टैक पर पहले था, उसे लागू करती है, फिर f को लागू करती है, और परिणाम को स्टैक पर स्थापित कर देती है।
फ़ंक्शंस की संरचना का नामकरण
अब मान लीजिए कि g() के परिणाम पर f() को कॉल करने का संयोजन प्रायः उपयोगी होता है, और जिसे हम foo() नाम देना चाहते हैं ताकि इसे अपने आप में एक फ़ंक्शन के रूप में उपयोग किया जा सके।
अधिकांश भाषाओं में, हम रचना द्वारा कार्यान्वित एक नए फ़ंक्शन को परिभाषित कर सकते हैं। C (प्रोग्रामिंग भाषा) में उदाहरण:
float foo(float x) {
return f(g(x));
}
(मध्यवर्ती के साथ लंबा फॉर्म भी काम करेगा।) फोर्थ में उदाहरण (प्रोग्रामिंग भाषा):
: फू जी एफ ;
सी (प्रोग्रामिंग भाषा) जैसी भाषाओं में, एक नया फ़ंक्शन बनाने का एकमात्र तरीका इसे प्रोग्राम स्रोत में परिभाषित करना है, जिसका अर्थ है कि फ़ंक्शन को रन टाइम (प्रोग्राम जीवनचक्र चरण) पर नहीं बनाया जा सकता है। हालाँकि, पूर्वनिर्धारित कार्यों की मनमानी संरचना का मूल्यांकन संभव है:
#include <stdio.h>
typedef int FXN(int);
int f(int x) { return x+1; }
int g(int x) { return x*2; }
int h(int x) { return x-3; }
int eval(FXN *fs[], int size, int x)
{
for (int i=0; i<size; i++) x = (*fs[i])(x);
return x;
}
int main()
{
// ((6+1)*2)-3 = 11
FXN *arr[] = {f,g,h};
printf("%d\n", eval(arr, 3, 6));
// ((6-3)*2)+1 = 7
arr[2] = f; arr[0] = h;
printf("%d\n", eval(arr, 3, 6));
}
प्रथम श्रेणी की रचना
कार्यात्मक प्रोग्रामिंग भाषाओं में, फ़ंक्शन कॉम्पोजिसन को स्वाभाविक रूप से उच्च-क्रम फ़ंक्शन या ऑपरेटर के रूप में व्यक्त किया जा सकता है। अन्य प्रोग्रामिंग भाषाओं में आप फ़ंक्शन कॉम्पोजिसन निष्पादित करने के लिए अपना स्वयं का तंत्र लिख सकते हैं।
हास्केल
हास्केल (प्रोग्रामिंग भाषा) में, उदाहरण foo = f ∘ g ऊपर दिया गया है:
फू = एफ . जी
अंतर्निहित कंपोजिशन ऑपरेटर (.) का उपयोग करके जिसे जी के बाद एफ के रूप में पढ़ा जा सकता है या एफ के साथ जी बनाया जा सकता है।
रचना संचालक ∘ को लैम्ब्डा कैलकुलस का उपयोग करके हास्केल में परिभाषित किया जा सकता है:
(.) :: (b -> c) -> (a -> b) -> a -> c
f . g = \x -> f (g x)
पहली पंक्ति (.) के प्रकार का वर्णन करती है - यह कार्यों की एक जोड़ी लेती है, f, g और एक फ़ंक्शन लौटाता है (दूसरी पंक्ति पर लैम्ब्डा अभिव्यक्ति)। ध्यान दें कि हास्केल को एफ और जी के सटीक इनपुट और आउटपुट प्रकारों के विनिर्देश की आवश्यकता नहीं है; ए, बी, सी, और एक्स प्लेसहोल्डर हैं; केवल बीच का संबंध f, g मायने रखता है (एफ को स्वीकार करना होगा कि जी क्या लौटाता है)। यह (.) को एक बहुरूपता (कंप्यूटर विज्ञान) ऑपरेटर बनाता है।
लिस्प
लिस्प (प्रोग्रामिंग भाषा) के वेरिएंट, विशेष रूप से स्कीम (प्रोग्रामिंग भाषा), कार्यों के उपचार के साथ समलिंगी एक विविध रचनाकार ऑपरेटर की पुनरावर्ती परिभाषा के लिए खुद को बहुत अच्छी तरह से उधार देते हैं।
(define (compose . fs)
(if (null? fs) (lambda (x) x) ; if no argument is given, evaluates to the identity function
(lambda (x) ((car fs) ((apply compose (cdr fs)) x)))))
; examples
(define (add-a-bang str)
(string-append str "!"))
(define givebang
(compose string->symbol add-a-bang symbol->string))
(givebang 'set) ; ===> set!
; anonymous composition
((compose sqrt negate square) 5) ; ===> 0+5i
एपीएल
एपीएल (प्रोग्रामिंग भाषा) की कई बोलियाँ प्रतीक का उपयोग करके फ़ंक्शन कॉम्पोजिसन में निर्मित होती हैं ∘
.
यह उच्च-क्रम फ़ंक्शन फ़ंक्शन कॉम्पोजिसन को बाईं ओर के फ़ंक्शन के Arity#Binary अनुप्रयोग तक विस्तारित करता है A f∘g B
है A f g B
.
foo←f∘g
इसके अतिरिक्त, आप फ़ंक्शन कॉम्पोजिसन को परिभाषित कर सकते हैं:
o←{⍺⍺ ⍵⍵ ⍵}
ऐसी बोली में जो ब्रेसिज़ का उपयोग करके इनलाइन परिभाषा का समर्थन नहीं करती है, पारंपरिक परिभाषा उपलब्ध है:
∇ r←(f o g)x
r←f g x
∇
राकू
हास्केल (प्रोग्रामिंग भाषा) की तरह राकू (प्रोग्रामिंग भाषा) में एक अंतर्निहित फ़ंक्शन कंपोज़िशन ऑपरेटर है, मुख्य अंतर यह है कि इसे इस प्रकार लिखा जाता है ∘
या o
.
my &foo = &f ∘ &g;
इसके अलावा हास्केल (प्रोग्रामिंग भाषा) की तरह आप ऑपरेटर को स्वयं परिभाषित कर सकते हैं। वास्तव में निम्नलिखित Raku कोड है जिसका उपयोग Rakudo कार्यान्वयन में इसे परिभाषित करने के लिए किया जाता है।
# the implementation has a slightly different line here because it cheats
proto sub infix:<∘> (&?, &?) is equiv(&[~]) is assoc<left> {*}
multi sub infix:<∘> () { *.self } # allows `[∘] @array` to work when `@array` is empty
multi sub infix:<∘> (&f) { &f } # allows `[∘] @array` to work when `@array` has one element
multi sub infix:<∘> (&f, &g --> Block) {
(&f).count > 1
?? -> |args { f |g |args }
!! -> |args { f g |args }
}
# alias it to the "Texas" spelling ( everything is bigger, and ASCII in Texas )
my &infix:<o> := &infix:<∘>;
पायथन
पायथन (प्रोग्रामिंग भाषा) में, फ़ंक्शंस के किसी भी समूह के लिए संरचना को परिभाषित करने का एक तरीका, फ़ोल्ड (उच्च-क्रम फ़ंक्शन) फ़ंक्शन का उपयोग करना है (पायथन 3 में functools.reduce का उपयोग करें):
# Available since Python v2.6
from functools import reduce
def compose(*funcs) -> int:
"""Compose a group of functions (f(g(h(...)))) into a single composite func."""
return reduce(lambda f, g: lambda x: f(g(x)), funcs)
# Example
f = lambda x: x + 1
g = lambda x: x * 2
h = lambda x: x - 3
# Call the function x=10 : ((x-3)*2)+1 = 15
print(compose(f, g, h)(10))
जावास्क्रिप्ट
जावास्क्रिप्ट में हम इसे एक फ़ंक्शन के रूप में परिभाषित कर सकते हैं जो दो फ़ंक्शन f और g लेता है, और एक फ़ंक्शन उत्पन्न करता है:
function o(f, g) {
return function(x) {
return f(g(x));
}
}
// Alternatively, using the rest operator and lambda expressions in ES2015
const compose = (...fs) => (x) => fs.reduceRight((acc, f) => f(acc), x)
सी#
C_Sharp_(प्रोग्रामिंग_भाषा)|C# में हम इसे एक एक्सटेंशन विधि के रूप में परिभाषित कर सकते हैं जो Funcs f और g लेता है, और एक नया Func तैयार करता है:
// Call example:
// var c = f.ComposeWith(g);
//
// Func<int, bool> g = _ => ...
// Func<bool, string> f = _ => ...
public static Func<T1, T3> ComposeWith<T1, T2, T3>(this Func<T2, T3> f, Func<T1, T2> g) => x => f(g(x));
रूबी
रूबी (प्रोग्रामिंग भाषा) जैसी भाषाएँ आपको स्वयं एक बाइनरी ऑपरेटर बनाने देती हैं:
class Proc
def compose(other_fn)
->(*as) { other_fn.call(call(*as)) }
end
alias_method :+, :compose
end
f = ->(x) { x * 2 }
g = ->(x) { x ** 3 }
(f + g).call(12) # => 13824
हालाँकि, रूबी 2.6 में एक मूल फ़ंक्शन कंपोज़िशन ऑपरेटर पेश किया गया था:[3]
f = proc{|x| x + 2}
g = proc{|x| x * 3}
(f << g).call(3) # -> 11; identical to f(g(3))
(f >> g).call(3) # -> 15; identical to g(f(3))
अनुसंधान सर्वेक्षण
रचनाशीलता और रचनाशीलता के सिद्धांत सहित रचना की धारणाएं इतनी सर्वव्यापी हैं कि अनुसंधान के कई पहलू अलग-अलग विकसित हुए हैं। निम्नलिखित उस प्रकार के शोध का एक नमूना है जिसमें रचना की धारणा केंद्रीय है।
- Steele (1994) हास्केल (प्रोग्रामिंग भाषा) में 'मोनैड (कार्यात्मक प्रोग्रामिंग)' के रूप में जाने जाने वाले बिल्डिंग ब्लॉक्स के संयोजन में फ़ंक्शन कॉम्पोजिसन को सीधे लागू किया जाता है।
- Meyer (1988) ने कंपोजिबिलिटी के संदर्भ में कोड के पुन: उपयोग की समस्या को संबोधित किया।
- Abadi & Lamport (1993) औपचारिक रूप से कार्यात्मक संरचना के लिए एक प्रमाण नियम परिभाषित किया गया है जो प्रोग्राम की सुरक्षा और जीवंतता का आश्वासन देता है।
- Kracht (2001) इसे कम्प्यूटेशनल सांकेतिकता प्रणाली में रखकर और कम्प्यूटेशनल भाषाविज्ञान में प्रायः सामने आने वाली संरचनात्मक अस्पष्टता की समस्या पर लागू करके संरचना के एक मजबूत रूप की पहचान की।
- van Gelder & Port (1993) प्राकृतिक भाषा प्रसंस्करण के अनुरूप पहलुओं में संरचना की भूमिका की जांच की गई।
- द्वारा एक समीक्षा के अनुसार Gibbons (2002), रचना का औपचारिक उपचार जावा (प्रोग्रामिंग भाषा) भाषा के लिए आईबीएम की विज़ुअल एज जैसी दृश्य प्रोग्रामिंग भाषाओं में घटक असेंबली के सत्यापन को रेखांकित करता है।
बड़े पैमाने की रचना
संपूर्ण प्रोग्राम या सिस्टम को फ़ंक्शंस के रूप में माना जा सकता है, जिन्हें आसानी से बनाया जा सकता है यदि उनके इनपुट और आउटपुट अच्छी तरह से परिभाषित हों।[4] फ़िल्टर (सॉफ़्टवेयर) की आसान संरचना की अनुमति देने वाली पाइपलाइन (सॉफ़्टवेयर) इतनी सफल रहीं कि वे ऑपरेटिंग सिस्टम की पाइपलाइन (सॉफ़्टवेयर) बन गईं।
साइड इफेक्ट्स के साथ अनिवार्य प्रोग्रामिंग संदर्भात्मक पारदर्शिता का उल्लंघन करती है और इसलिए साफ-सुथरी रचना योग्य नहीं है। हालाँकि, यदि कोई कोड को इनपुट और आउटपुट के रूप में चलाने से पहले और बाद में दुनिया की स्थिति पर विचार करता है, तो उसे एक क्लीन फ़ंक्शन मिलता है। ऐसे कार्यों की संरचना प्रक्रियाओं को एक के बाद एक चलाने से मेल खाती है। मोनाड (कार्यात्मक प्रोग्रामिंग) औपचारिकता इस विचार का उपयोग साइड इफेक्ट्स और इनपुट/आउटपुट (आई/ओ) को कार्यात्मक भाषाओं में शामिल करने के लिए करती है।
यह भी देखें
- करी बनाना
- कार्यात्मक अपघटन
- कार्यान्वयन विरासत
- वंशानुक्रम शब्दार्थ
- पुनरावृति
- पाइपलाइन (यूनिक्स)
- रचनात्मकता का सिद्धांत
- आभासी विरासत
टिप्पणियाँ
- ↑ Cox (1986), pp. 15–17
- ↑ DeMarco & Lister (1995), pp. 133–135.
- ↑ "Ruby 2.6.0 Released". www.ruby-lang.org. Retrieved 2019-01-04.
- ↑ Raymond (2003)
संदर्भ
- Abadi, Martín; Lamport, Leslie (1993), "Composing specifications" (PDF), ACM Transactions on Programming Languages and Systems, 15 (1): 73–132, doi:10.1145/151646.151649.
- Cox, Brad (1986), Object-oriented Programming, an Evolutionary Approach, Reading, MA: Addison-Wesley, ISBN 978-0-201-54834-1.
- Daume, Hal, III, Yet Another Haskell Tutorial
{{citation}}
: CS1 maint: multiple names: authors list (link). - DeMarco, Tom; Lister, Tim (1995), "Software development: state of the art vs. state of the practice", in DeMarco, Tom (ed.), Why Does Software Cost So Much, and other puzzles of the Information Age, New York, NY: Dorset House, ISBN 0-932633-34-X.
- van Gelder, Timothy; Port, Robert (1993), "Beyond symbolic: prolegomena to a Kama-Sutra of compositionality", in Honavar, Vasant; Uhr, Leonard (eds.), Symbol Processing and Connectionist Models in Artificial Intelligence and Cognition: Steps Toward Integration, Academic Press.
- Gibbons, Jeremy (2002), Arbab, Farhad; Talcott, Carolyn (eds.), Proc. 5th International Conference on Coordination Models and Languages (PDF), Lecture Notes in Computer Science, vol. 2315, Springer-Verlag, pp. 339–350, doi:10.1007/3-540-46000-4\_18.
- Korn, Henry; Liberi, Albert (1974), An Elementary Approach to Functions, New York, NY: McGraw-Hill, ISBN 0-07-035339-5.
- Kracht, Marcus (2001), "Strict compositionality and literal movement grammars", Proc. 3rd International Conference on Logical Aspects of Computational Linguistics, Lecture Notes in Computer Science, vol. 2014, Springer-Verlag, pp. 126–143, doi:10.1007/3-540-45738-0_8.
- Meyer, Bertrand (1988), Object-oriented Software Construction, New York, NY: Prentice Hall, pp. 13–15, ISBN 0-13-629049-3.
- Miller, George A. (1956), "The magical number seven, plus or minus two: some limits on our capacity for processing information", Psychological Review, 63 (2): 81–97, doi:10.1037/h0043158, hdl:11858/00-001M-0000-002C-4646-B, PMID 13310704, archived from the original on 2010-06-19, retrieved 2010-05-02.
- Pierce, Benjamin C.; Turner, David N. (2000), "Pict: A programming language based on the pi-calculus", Proof, Language, and Interaction: Essays in Honour of Robin Milner, Foundations Of Computing Series, Cambridge, MA: MIT Press, pp. 455–494, ISBN 0-262-16188-5.
- Raymond, Eric S. (2003), "1.6.3 Rule of Composition: Design programs to be connected with other programs", The Art of Unix Programming, Addison-Wesley, pp. 15–16, ISBN 978-0-13-142901-7.
- Steele, Guy L., Jr. (1994), "Building interpreters by composing monads", Proc. 21st ACM Symposium on Principles of Programming Languages, pp. 472–492, doi:10.1145/174675.178068
{{citation}}
: CS1 maint: multiple names: authors list (link).