मैं F # में एक लाइब्रेरी डिजाइन करने की कोशिश कर रहा हूं। पुस्तकालय F # और C # दोनों से उपयोग के लिए अनुकूल होना चाहिए ।
और यह वह जगह है जहां मैं थोड़ा फंस गया हूं। मैं इसे F # फ्रेंडली बना सकता हूं, या मैं इसे C # फ्रेंडली बना सकता हूं, लेकिन समस्या यह है कि इसे दोनों के लिए फ्रेंडली कैसे बनाया जाए।
यहाँ एक उदाहरण है। कल्पना कीजिए कि मेरे पास F # में निम्न कार्य है:
let compose (f: 'T -> 'TResult) (a : 'TResult -> unit) = f >> a
यह F # से पूरी तरह से प्रयोग करने योग्य है:
let useComposeInFsharp() =
let composite = compose (fun item -> item.ToString) (fun item -> printfn "%A" item)
composite "foo"
composite "bar"
C # में, compose
फ़ंक्शन में निम्न हस्ताक्षर हैं:
FSharpFunc<T, Unit> compose<T, TResult>(FSharpFunc<T, TResult> f, FSharpFunc<TResult, Unit> a);
लेकिन निश्चित रूप से, मैं FSharpFunc
हस्ताक्षर नहीं करना चाहता , जो मैं चाहता हूं Func
और Action
इसके बजाय, इस तरह है:
Action<T> compose2<T, TResult>(Func<T, TResult> f, Action<TResult> a);
इसे प्राप्त करने के लिए, मैं compose2
इस तरह से फंक्शन बना सकता हूं :
let compose2 (f: Func<'T, 'TResult>) (a : Action<'TResult> ) =
new Action<'T>(f.Invoke >> a.Invoke)
अब, यह C # में पूरी तरह से प्रयोग करने योग्य है:
void UseCompose2FromCs()
{
compose2((string s) => s.ToUpper(), Console.WriteLine);
}
लेकिन अब हमें compose2
F # का उपयोग करने में समस्या है ! अब मैं रैप करने के लिए सभी मानक एफ # है funs
में Func
और Action
इस तरह,:
let useCompose2InFsharp() =
let f = Func<_,_>(fun item -> item.ToString())
let a = Action<_>(fun item -> printfn "%A" item)
let composite2 = compose2 f a
composite2.Invoke "foo"
composite2.Invoke "bar"
प्रश्न: हम F # और F # दोनों उपयोगकर्ताओं के लिए F # में लिखी गई लाइब्रेरी के लिए प्रथम श्रेणी का अनुभव कैसे प्राप्त कर सकते हैं?
अब तक, मैं इन दो दृष्टिकोणों से बेहतर कुछ भी नहीं कर सका:
- दो अलग-अलग असेंबली: एक एफ # उपयोगकर्ताओं के लिए लक्षित है, और दूसरा सी # उपयोगकर्ताओं के लिए।
- एक असेंबली लेकिन अलग नामस्थान: एक एफ # उपयोगकर्ताओं के लिए, और दूसरा सी # उपयोगकर्ताओं के लिए।
पहले दृष्टिकोण के लिए, मैं ऐसा कुछ करूंगा:
F # प्रोजेक्ट बनाएं, इसे FooBarFs कहें और FooBarFs.dll में संकलित करें।
- लाइब्रेरी को विशुद्ध रूप से F # उपयोगकर्ताओं को लक्षित करें।
- .Fsi फ़ाइलों से अनावश्यक सब कुछ छिपाएँ।
एक और F # प्रोजेक्ट बनाएं, FooBarCs को कॉल करें और FooFar.dll में संकलित करें
- स्रोत स्तर पर पहले F # प्रोजेक्ट का पुन: उपयोग करें।
- .Fsi फ़ाइल बनाएँ जो उस परियोजना से सब कुछ छुपाता है।
- .Fsi फ़ाइल बनाएँ जो C # तरीके से लाइब्रेरी को उजागर करती है, नाम, नामस्थान आदि के लिए C # मुहावरों का उपयोग करते हुए।
- ऐसे रैपर बनाएं जो कोर लाइब्रेरी को सौंपते हैं, जहां आवश्यक हो रूपांतरण करते हैं।
मुझे लगता है कि नामस्थान के साथ दूसरा दृष्टिकोण उपयोगकर्ताओं को भ्रमित कर सकता है, लेकिन फिर आपके पास एक विधानसभा है।
प्रश्न: इनमें से कोई भी आदर्श नहीं है, शायद मुझे किसी प्रकार के संकलक ध्वज / स्विच / विशेषता या किसी प्रकार की चाल याद आ रही है और ऐसा करने का एक बेहतर तरीका है?
सवाल: क्या किसी और ने भी कुछ ऐसा ही हासिल करने की कोशिश की है और अगर आपने ऐसा किया तो कैसे किया?
संपादित करें: स्पष्ट करने के लिए, सवाल केवल फ़ंक्शंस और प्रतिनिधियों के बारे में नहीं है, बल्कि F # लाइब्रेरी के साथ C # उपयोगकर्ता का समग्र अनुभव है। इसमें नाम स्थान, नामकरण परंपराएँ, मुहावरे और ऐसे प्रकार शामिल हैं जो C # के मूल निवासी हैं। मूल रूप से, एक C # उपयोगकर्ता को यह पता लगाने में सक्षम नहीं होना चाहिए कि पुस्तकालय F # में लेखक था। और इसके विपरीत, एक F # उपयोगकर्ता को C # लाइब्रेरी से निपटने का मन करना चाहिए।
संपादित करें 2:
मैं अब तक के उत्तरों और टिप्पणियों से देख सकता हूं कि मेरे प्रश्न में आवश्यक गहराई का अभाव है, शायद ज्यादातर केवल एक उदाहरण के उपयोग के कारण जहां एफ # और सी # के बीच अंतर की समस्याएँ उत्पन्न होती हैं, फ़ंक्शन मानों का मुद्दा। मुझे लगता है कि यह सबसे स्पष्ट उदाहरण है और इसलिए इसने मुझे सवाल पूछने के लिए इसका उपयोग करने के लिए प्रेरित किया, लेकिन उसी टोकन ने यह धारणा दी कि यह एकमात्र मुद्दा है जिससे मैं चिंतित हूं।
मुझे और अधिक ठोस उदाहरण प्रदान करते हैं। मैंने सबसे उत्कृष्ट F # घटक डिज़ाइन दिशानिर्देश दस्तावेज़ (इसके लिए कई धन्यवाद @gradbot!) के माध्यम से पढ़ा है । दस्तावेज़ में दिए गए दिशानिर्देश, यदि उपयोग किए जाते हैं, तो कुछ मुद्दों को संबोधित करते हैं, लेकिन सभी नहीं।
दस्तावेज़ को दो मुख्य भागों में विभाजित किया गया है: 1) एफ # उपयोगकर्ताओं को लक्षित करने के लिए दिशानिर्देश; और # 2 सी # उपयोगकर्ताओं को लक्षित करने के लिए दिशानिर्देश। कहीं भी यह ढोंग करने का प्रयास नहीं करता है कि एक समान दृष्टिकोण रखना संभव है, जो वास्तव में मेरे प्रश्न को प्रतिध्वनित करता है: हम एफ # को लक्षित कर सकते हैं, हम सी # को लक्षित कर सकते हैं, लेकिन दोनों को लक्षित करने के लिए व्यावहारिक समाधान क्या है?
याद दिलाने के लिए, लक्ष्य F # में एक लाइब्रेरी है, और जिसका उपयोग F # और C # दोनों भाषाओं में मुहावरेदार तरीके से किया जा सकता है ।
यहां कीवर्ड मुहावरेदार है । मुद्दा सामान्य अंतर नहीं है जहां विभिन्न भाषाओं में पुस्तकालयों का उपयोग करना संभव है।
अब उदाहरण के लिए, जिसे मैं सीधे एफ # घटक डिज़ाइन दिशानिर्देशों से लेता हूं ।
मॉड्यूल + फ़ंक्शन (F #) बनाम नामस्थान + प्रकार + फ़ंक्शन
F #: अपने प्रकार और मॉड्यूल शामिल करने के लिए नामस्थान या मॉड्यूल का उपयोग करें। मुहावरों का उपयोग मॉड्यूल में कार्य करने के लिए होता है, जैसे:
// library module Foo let bar() = ... let zoo() = ... // Use from F# open Foo bar() zoo()
C #: वैनिला .NET API के लिए अपने घटकों के लिए प्राथमिक संगठनात्मक संरचना के रूप में नामस्थान, प्रकार और सदस्यों का उपयोग करें।
यह F # दिशानिर्देश के साथ असंगत है, और उदाहरण के लिए C # उपयोगकर्ताओं को फिट करने के लिए फिर से लिखना होगा:
[<AbstractClass; Sealed>] type Foo = static member bar() = ... static member zoo() = ...
हालांकि ऐसा करने से, हम F # से मुहावरेदार उपयोग को तोड़ते हैं क्योंकि हम अब इसका उपयोग नहीं कर सकते हैं
bar
औरzoo
इसके साथ उपसर्ग किए बिनाFoo
।
टुपल्स का उपयोग
एफ #: वापसी मूल्यों के लिए उपयुक्त होने पर टुपल्स का उपयोग करें।
C #: वनीला .NET एपीआई में रिटर्न वैल्यू के रूप में ट्यूपल्स के उपयोग से बचें।
async
F #: F # API सीमाओं पर Async प्रोग्रामिंग के लिए Async का उपयोग करें।
C #: अतुल्यकालिक प्रोग्रामिंग मॉडल (BeginFoo, EndFoo) का उपयोग करके अतुल्यकालिक संचालन को उजागर करें, या F # Async ऑब्जेक्ट्स के बजाय .NET कार्य (टास्क) को वापस करने के तरीकों के रूप में करें।
का उपयोग
Option
F #: अपवाद बढ़ाने के बजाय रिटर्न प्रकारों के लिए विकल्प मानों का उपयोग करने पर विचार करें (F # -facing कोड के लिए)।
वैनिला .NET एपीआई में F # विकल्प मान (विकल्प) को वापस करने के बजाय TryGetValue पैटर्न का उपयोग करने पर विचार करें, और तर्क के रूप में F # विकल्प मान लेने से अधिक अधिभार विधि को प्राथमिकता दें।
भेदभाव रहित संघ
एफ #: वृक्ष-संरचित डेटा बनाने के लिए वर्ग पदानुक्रम के विकल्प के रूप में भेदभाव वाली यूनियनों का उपयोग करें
C #: इसके लिए कोई विशेष दिशानिर्देश नहीं है, लेकिन भेदभाव वाली यूनियनों की अवधारणा C # के लिए विदेशी है
करी कार्य
एफ #: करी कार्य एफ # के लिए मुहावरेदार हैं
सी #: वेनिला .NET एपीआई में मापदंडों की करी का उपयोग न करें।
अशक्त मूल्यों के लिए जाँच कर रहा है
F #: यह F # के लिए मुहावरेदार नहीं है
C #: वेनिला .NET एपीआई सीमाओं पर शून्य मानों के लिए जाँच पर विचार करें।
एफ # प्रकार के उपयोग
list
,map
,set
, आदिF #: इनको F # में इस्तेमाल करना मुहावरेदार है
C #: .NET संग्रह इंटरफ़ेस प्रकारों का उपयोग करने पर विचार करें मापदंडों के लिए IEnumerable और IDEDIA और वेनिला .NET एपीआई में मान लौटाएं। ( यानी उपयोग नहीं करते हैं एफ #
list
,map
,set
)
फ़ंक्शन प्रकार (स्पष्ट एक)
एफ #: मान के रूप में एफ # कार्यों का उपयोग एफ # के लिए मुहावरेदार है, जाहिर है
C #: वेनिला .NET API में F # फंक्शन टाइप्स को वरीयता में .NET डेलिगेट टाइप्स का उपयोग करें।
मुझे लगता है कि ये मेरे प्रश्न की प्रकृति को प्रदर्शित करने के लिए पर्याप्त होना चाहिए।
संयोग से, दिशानिर्देशों का आंशिक उत्तर भी है:
... वेनिला .NET पुस्तकालयों के लिए उच्च-क्रम के तरीकों को विकसित करते समय एक सामान्य कार्यान्वयन रणनीति एफ # फ़ंक्शन प्रकारों का उपयोग करके सभी कार्यान्वयन को लेखक के लिए है, और फिर वास्तविक एफ # कार्यान्वयन के ऊपर एक पतली बहाना के रूप में प्रतिनिधियों का उपयोग करके सार्वजनिक एपीआई बनाएं।
संक्षेपित करते हुए।
एक निश्चित उत्तर है: कोई संकलक चालें नहीं हैं जो मैंने याद किया ।
दिशानिर्देशों के अनुसार, ऐसा लगता है कि F # के लिए संलेखन और फिर .NET के लिए एक मुखौटा आवरण बनाना एक उचित रणनीति है।
इसके बाद व्यावहारिक कार्यान्वयन के बारे में सवाल बना रहता है:
अलग-अलग विधानसभाएं? या
विभिन्न नामस्थान?
यदि मेरी व्याख्या सही है, तो टॉमस का सुझाव है कि अलग नामस्थानों का उपयोग करना पर्याप्त होना चाहिए, और एक स्वीकार्य समाधान होना चाहिए।
मुझे लगता है कि मैं इस बात से सहमत हूं कि दिए गए नामस्थानों की पसंद ऐसी है कि यह .NET / C # उपयोगकर्ताओं को आश्चर्यचकित या भ्रमित नहीं करता है, जिसका अर्थ है कि उनके लिए नाम स्थान संभवतः यह देखना चाहिए कि यह उनके लिए प्राथमिक नाम स्थान है। F # उपयोगकर्ताओं को F # -specific नाम स्थान चुनने का भार उठाना होगा। उदाहरण के लिए:
FSharp.Foo.Bar -> F # लाइब्रेरी के लिए नामस्थान
Foo.Bar -> .NET रैपर के लिए नाम स्थान, C # के लिए मुहावरेदार