swift - অপ্রত্যক্ষ কল থেকে ভুল বিশেষায়িত জেনেরিক ফাংশনটি সুইফ্ট 3-এ কল করা হয়



generics overriding (1)

আমার কোড রয়েছে যা সাধারণ নকশা অনুসরণ করে:

protocol DispatchType {}
class DispatchType1: DispatchType {}
class DispatchType2: DispatchType {}

func doBar<D:DispatchType>(value:D) {
    print("general function called")
}

func doBar(value:DispatchType1) {
    print("DispatchType1 called")
}

func doBar(value:DispatchType2) {
    print("DispatchType2 called")
}

যেখানে বাস্তবে DispatchType আসলে একটি ব্যাকএন্ড স্টোরেজ। doBar ফাংশনগুলি অনুকূলিত পদ্ধতি যা সঠিক স্টোরেজ ধরণের উপর নির্ভর করে। আমি যদি করি তবে সবকিছু ঠিকঠাক কাজ করে:

let d1 = DispatchType1()
let d2 = DispatchType2()

doBar(value: d1)    // "DispatchType1 called"
doBar(value: d2)    // "DispatchType2 called"

তবে, আমি যদি এমন কোনও ফাংশন করি যা doBar কল doBar :

func test<D:DispatchType>(value:D) {
    doBar(value: value)
}

এবং আমি একই কলিং প্যাটার্নটি চেষ্টা করি, আমি পেয়েছি:

test(value: d1)     // "general function called"
test(value: d2)     // "general function called"

এটি এমন কিছুর মতো লাগে যা সুইফ্ট হ্যান্ডেল করতে সক্ষম হওয়া উচিত কারণ সংকলনের সময় টাইপের সীমাবদ্ধতাগুলি নির্ধারণ করতে সক্ষম হওয়া উচিত। দ্রুত পরীক্ষা হিসাবে, আমি doBar লেখার চেষ্টা করেছি:

func doBar<D:DispatchType>(value:D) where D:DispatchType1 {
    print("DispatchType1 called")
}

func doBar<D:DispatchType>(value:D) where D:DispatchType2 {
    print("DispatchType2 called")
}

তবে একই ফলাফল পেতে।

কোনও সঠিক ধারণা যদি এটি সঠিক সুইফট আচরণ হয়, এবং যদি তাই হয় তবে এই আচরণটি ঘটার জন্য ভাল উপায়?

সম্পাদনা 1 : আমি কেন প্রোটোকল ব্যবহার এড়াতে চেষ্টা করছিলাম তার উদাহরণ। ধরুন আমার কাছে কোডটি রয়েছে (আমার আসল কোড থেকে ব্যাপকভাবে সরলীকৃত):

protocol Storage {
     // ...
}

class Tensor<S:Storage> {
    // ...
}

Tensor ক্লাসের জন্য আমার কাছে Tensor সঞ্চালিত হওয়া অপারেশনগুলির একটি বেস সেট রয়েছে। যাইহোক, অপারেশনগুলি স্টোরেজের ভিত্তিতে তাদের আচরণ পরিবর্তন করবে। বর্তমানে আমি এটি দিয়ে এটি সম্পাদন করছি:

func dot<S:Storage>(_ lhs:Tensor<S>, _ rhs:Tensor<S>) -> Tensor<S> { ... }

আমি এগুলি Tensor শ্রেণিতে রাখতে পারি এবং এক্সটেনশনগুলি ব্যবহার করতে পারি:

extension Tensor where S:CBlasStorage {
    func dot(_ tensor:Tensor<S>) -> Tensor<S> {
       // ...
    }
}

এর কয়েকটি পার্শ্ব প্রতিক্রিয়া রয়েছে যা আমি পছন্দ করি না:

  1. আমি মনে করি dot(lhs, rhs) lhs.dot(rhs) dot(lhs, rhs) চেয়ে ভাল। কাছাকাছি পেতে সুবিধার ফাংশনগুলি লেখা যেতে পারে তবে এটি কোডের বিশাল বিস্ফোরণ তৈরি করবে।

  2. এটি Tensor শ্রেণি একচেটিয়া হয়ে উঠবে। আমি এটিতে সর্বনিম্ন কোডের প্রয়োজনীয় কোডটি যুক্ত হওয়া পছন্দ করি এবং সহায়ক কার্যক্রমে এর কার্যকারিতা প্রসারিত করি।

  3. (2) এর সাথে সম্পর্কিত, এর অর্থ হ'ল যে কেউ নতুন কার্যকারিতা যুক্ত করতে চায় তাকে বেস বর্গটি স্পর্শ করতে হবে, আমি খারাপ নকশা বিবেচনা করি।

সম্পাদনা 2 : একটি বিকল্প হ'ল আপনি যদি সমস্ত কিছুর জন্য সীমাবদ্ধতা ব্যবহার করেন তবে জিনিসগুলি প্রত্যাশিতভাবে কাজ করে:

func test<D:DispatchType>(value:D) where D:DispatchType1 {
    doBar(value: value)
}

func test<D:DispatchType>(value:D) where D:DispatchType2 {
    doBar(value: value)
}

সঠিক doBar করার কারণ হবে। এটিও আদর্শ নয়, কারণ এতে প্রচুর অতিরিক্ত কোড লেখা যায়, তবে কমপক্ষে আমাকে আমার বর্তমান ডিজাইন রাখতে দেয়।

সম্পাদনা 3 : আমি জেনেরিকের সাথে static কীওয়ার্ড ব্যবহার করে ডকুমেন্টেশন জুড়ে এসেছি। এটি কমপক্ষে বিন্দু (1) এর সাথে সহায়তা করে:

class Tensor<S:Storage> {
   // ...
   static func cos(_ tensor:Tensor<S>) -> Tensor<S> {
       // ...
   }
}

আপনাকে লিখতে দেয়:

let result = Tensor.cos(value)

এবং এটি অপারেটর ওভারলোডিং সমর্থন করে:

let result = value1 + value2

এটিতে প্রয়োজনীয় Tensor যুক্ত ভারবসটি রয়েছে। এটি এর সাথে কিছুটা আরও উন্নত করতে পারে:

typealias T<S:Storage> = Tensor<S>

এটি প্রকৃতপক্ষে সঠিক আচরণ কারণ ওভারলোড রেজোলিউশন সংকলনের সময়ে ঘটে (এটি রানটাইমের সময় সঞ্চালনের জন্য বেশ ব্যয়বহুল অপারেশন হবে)। সুতরাং test(value:) মধ্যে test(value:) , কেবলমাত্র func doBar<D : DispatchType>(value: D) value সম্পর্কে জানে যে এটি DispatchType সাথে সামঞ্জস্য করে এমন এক ধরণের - সুতরাং এটি কেবলমাত্র ওভারলোড যা প্রেরণ করতে পারে তা func doBar<D : DispatchType>(value: D)

বিষয়গুলি আলাদা হবে যদি জেনেরিক ফাংশনগুলি সর্বদা সংকলক দ্বারা বিশেষীকরণ করা হয়, কারণ তারপরে test(value:) একটি বিশেষায়িত প্রয়োগকরণ test(value:) কংক্রিটের ধরণের value জানত এবং এইভাবে উপযুক্ত ওভারলোড চয়ন করতে সক্ষম হবে। তবে জেনেরিক ফাংশনগুলির বিশেষীকরণটি বর্তমানে কেবলমাত্র একটি অপ্টিমাইজেশন (ইনলাইনিং ব্যতীত এটি আপনার কোডে উল্লেখযোগ্য ফোলা যোগ করতে পারে), সুতরাং এটি পর্যবেক্ষিত আচরণটি পরিবর্তন করে না।

doBar() অনুমতি দেওয়ার জন্য একটি সমাধান হ'ল প্রোটোকল সাক্ষী সারণীটি লাভ করা (তাদের উপর এই দুর্দান্ত doBar() আলাপ দেখুন doBar() প্রোটোকলের প্রয়োজনীয়তা হিসাবে doBar() যুক্ত করে এবং প্রোটোকলের সাথে সামঞ্জস্যপূর্ণ সংশ্লিষ্ট শ্রেণিতে এর বিশেষায়িত বাস্তবায়ন বাস্তবায়ন , সাধারণ বাস্তবায়ন প্রোটোকল এক্সটেনশনের একটি অংশ হিসাবে।

এটি doBar() ডায়নামিক প্রেরণের অনুমতি দেবে, সুতরাং এটি test(value:) থেকে ডেকে আনতে এবং সঠিক বাস্তবায়ন ডেকে doBar()

protocol DispatchType {
    func doBar()
}

extension DispatchType {
    func doBar() {
        print("general function called")
    }
}

class DispatchType1: DispatchType {
    func doBar() {
        print("DispatchType1 called")
    }
}

class DispatchType2: DispatchType {
    func doBar() {
        print("DispatchType2 called")
    }
}

func test<D : DispatchType>(value: D) {
    value.doBar()
}

let d1 = DispatchType1()
let d2 = DispatchType2()

test(value: d1)    // "DispatchType1 called"
test(value: d2)    // "DispatchType2 called"




specialization