প্রোটোকল নিজেকে মানায় না?


125

এই সুইফট কোডটি সংকলন করে না কেন?

protocol P { }
struct S: P { }

let arr:[P] = [ S() ]

extension Array where Element : P {
    func test<T>() -> [T] {
        return []
    }
}

let result : [S] = arr.test()

সংকলকটি বলে: " Pপ্রোটোকলের সাথে টাইপ করা যায় না P" (বা, সুইফ্টের পরবর্তী সংস্করণগুলিতে, "পি 'প্রোটোকল' পি 'এর সাথে সঙ্গতিপূর্ণ কংক্রিট টাইপ হিসাবে ব্যবহার করা সমর্থনযোগ্য নয়)")।

কেন না? এটি কোনওরকমে ভাষার গর্তের মতো অনুভব করে। আমি বুঝতে পারি যে সমস্যাটি arrঅ্যারেটিকে একটি প্রোটোকল ধরণের অ্যারে হিসাবে ঘোষণা করা থেকে শুরু করে , তবে এটি কি অযৌক্তিক কাজ নয়? আমি ভেবেছিলাম প্রোটোকলগুলি ঠিক তেমন কোনও ধরণের শ্রেণিবিন্যাসের সাথে স্ট্রাক্ট সরবরাহ করতে সহায়তা করে?


1
আপনি যখন let arrলাইনে টাইপ টিকাটি সরিয়ে ফেলেন , সংকলকটি টাইপটি [S]কোড এবং সংকলিত করে। দেখে মনে হচ্ছে যে কোনও প্রোটোকল টাইপ ক্লাস - সুপার ক্লাস সম্পর্ক হিসাবে একইভাবে ব্যবহার করা যায় না।
ভাদিয়ান

1
@ ভাদিয়ান সঠিক, এটিই আমি আমার প্রশ্নে উল্লেখ করছি যখন আমি বলেছিলাম "আমি বুঝতে পারি যে সমস্যাটি অ্যারের অ্যারেটিকে একটি প্রোটোকল টাইপের অ্যারে হিসাবে ঘোষণা করে না"। তবে, আমি আমার প্রশ্নে আরও বলতে গেলে, প্রোটোকলের পুরো পয়েন্টটি সাধারণত হয় যে সেগুলি ক্লাস হিসাবে একইভাবে ব্যবহার করা যেতে পারে - সুপারক্লাস সম্পর্ক! তারা হয় অভিপ্রেত structs এর জগতে হায়ারারকিকাল কাঠামো কেমন প্রদান। এবং তারা সাধারণত করে। প্রশ্নটি হচ্ছে, কেন এখানে কাজ করা উচিত নয় ?
ম্যাট

1
এখনও Xcode 7.1 কাজ করে না, কিন্তু ত্রুটি বার্তা এখন "প্রোটোকল 'P' দ্বারা সমর্থিত নয় অনুসারী একটি কংক্রিট ধরণ হিসেবে 'পি' ব্যবহার"
মার্টিন আর

1
@ মার্টিনআর এটি আরও ভাল ত্রুটির বার্তা। তবে এটি এখনও আমার কাছে ভাষার গর্তের মতো অনুভব করে।
ম্যাট

নিশ্চিত! এমনকি protocol P : Q { }, পি Q- এর সাথে সামঞ্জস্য করে না
মার্টিন আর

উত্তর:


66

সম্পাদনা: আরও আঠারো মাস কাজ করার জন্য ডাব্লু / সুইফ্ট, আরও একটি বড় রিলিজ (এটি একটি নতুন ডায়াগনস্টিক সরবরাহ করে), এবং @ আইবেবেয়ের একটি মন্তব্য আমাকে এই উত্তরটি পুনরায় লিখতে চায়। নতুন ডায়াগনস্টিকটি হ'ল:

"প্রোটোকল 'পি' এর সাথে সঙ্গতিপূর্ণ একটি কংক্রিট টাইপ হিসাবে 'পি' ব্যবহার করা সমর্থনযোগ্য নয়।"

এটি আসলে এই পুরো জিনিসটিকে অনেক পরিষ্কার করে তোলে। এই এক্সটেনশন:

extension Array where Element : P {

যখন Element == Pথেকে Pকোনও কংক্রিটের সাথে বিবেচনা করা হয় না তখন প্রয়োগ হয় না P। (নীচে "এটি একটি বাক্সে রাখুন" সমাধানটি এখনও সর্বাধিক সাধারণ সমাধান))


পুরানো উত্তর:

এটি মেটাটাটাইপের আরও একটি মামলা। স্যুইফ্ট সত্যিই আপনাকে বেশিরভাগ নন-তুচ্ছ জিনিসগুলির জন্য কংক্রিটের ধরণের কাছে যেতে চায়। [P]কংক্রিটের ধরণের নয় (আপনি পরিচিত আকারের মেমরির একটি ব্লক বরাদ্দ করতে পারবেন না P)। (আমি আসলে এটি সত্য বলে মনে করি না; আপনি একেবারে আকারের কিছু তৈরি করতে পারেন Pকারণ এটি ইন্ডিয়ারেশনের মাধ্যমে হয়ে গেছে )) আমি মনে করি না যে এটি "করা উচিত নয়" কাজ করার কোনও ঘটনা রয়েছে। এটি দেখতে অনেকটা তাদের "এখনও কাজ করে না" মামলার মতো। (দুর্ভাগ্যক্রমে অ্যাপলকে এই ক্ষেত্রেগুলির মধ্যে পার্থক্যের বিষয়টি নিশ্চিত করা প্রায় অসম্ভব)) সত্য যে Array<P>এটি একটি পরিবর্তনশীল প্রকার হতে পারে (যেখানেArrayপারে না) ইঙ্গিত দেয় যে তারা ইতিমধ্যে এই দিকটিতে কিছু কাজ করেছে, তবে সুইফট মেটাটাইপসে প্রচুর তীক্ষ্ণ প্রান্ত এবং অবিবাহিত কেস রয়েছে। আমি মনে করি না আপনি এর চেয়ে আরও ভাল "কেন" উত্তর পেতে চলেছেন। "কারণ সংকলক এটির অনুমতি দেয় না।" (অসন্তুষ্টি, আমি জানি। আমার পুরো সুইফট জীবন ...)

সমাধান প্রায় সবসময় একটি বাক্সে জিনিস রাখা। আমরা একটি টাইপ-ইরেজার তৈরি করি।

protocol P { }
struct S: P { }

struct AnyPArray {
    var array: [P]
    init(_ array:[P]) { self.array = array }
}

extension AnyPArray {
    func test<T>() -> [T] {
        return []
    }
}

let arr = AnyPArray([S()])
let result: [S] = arr.test()

যখন সুইফ্ট আপনাকে সরাসরি এটি করতে দেয় (যা আমি শেষ পর্যন্ত প্রত্যাশা করি), সম্ভবত এটি স্বয়ংক্রিয়ভাবে আপনার জন্য এই বাক্সটি তৈরি করে। পুনরাবৃত্তিকারী এনামগুলির ঠিক এই ইতিহাস ছিল। আপনার সেগুলি বাক্স করতে হয়েছিল এবং এটি অবিশ্বাস্যরকম বিরক্তিকর এবং সীমাবদ্ধ ছিল এবং তারপরে অবশেষে সংকলকটি indirectএকই কাজটি আরও বেশি স্বয়ংক্রিয়ভাবে করার জন্য যুক্ত হয়েছিল।


এই উত্তরে প্রচুর দরকারী তথ্য, তবে টমোহিরোর জবাবের আসল সমাধান এখানে উপস্থাপিত বক্সিং সমাধানের চেয়ে ভাল।
জাজাডলার

@ জাজাডলার এই সীমাবদ্ধতার চারপাশে কীভাবে কাজ করবেন তা প্রশ্ন ছিল না তবে সীমাবদ্ধতা কেন বিদ্যমান। প্রকৃতপক্ষে, ব্যাখ্যা হিসাবে যতদূর যায়, টমোহিরোর কাজের দিকটি উত্তরগুলির চেয়ে আরও বেশি প্রশ্ন উত্থাপন করে। যদি আমরা ==আমার অ্যারে উদাহরণে ব্যবহার করি তবে আমরা একটি ত্রুটি পাই, একই ধরণের প্রয়োজনীয়তা জেনেরিক প্যারামিটারকে 'এলিমেন্ট' অ-জেনেরিক করে তোলে "" কেন টমোহিরো ব্যবহার করেন না== একই ত্রুটি তৈরি ?
ম্যাট

@ রব নেপিয়ার আপনার প্রতিক্রিয়া শুনে আমি এখনও হতবাক আসল বনাম আপনার সমাধানে কীভাবে সুইফট আরও সংক্ষিপ্ততা দেখতে পাবে? আপনি দেখে মনে হয়েছিল যে কেবল কোনও কাঠামোতে জিনিসগুলি আবৃত করেছেন ... আইডিক সম্ভবত আমি সুইফট টাইপ সিস্টেমটি বোঝার জন্য লড়াই করছি তবে
এগুলি

@ আইবেবে আপডেট উত্তর।
রব নেপিয়ার

আপনাকে ধন্যবাদ অনেক ধন্যবাদ @ রবনাপিয়ার আমি আপনার জবাবগুলির গতি এবং সর্বদাই স্পষ্টভাবে কীভাবে আপনি যতটা লোককে সাহায্য করার জন্য সময় পেলেন তাতে অবাক হয়েছি। তবুও আপনার নতুন সম্পাদনাগুলি অবশ্যই এটিকে দৃষ্টিভঙ্গিতে ফেলেছে। আমি আরও একটি বিষয় উল্লেখ করতে চাই, ধরণের ক্ষয়ের বিষয়টিও আমাকে সহায়তা করেছিল। এই নিবন্ধটি বিশেষত একটি দুর্দান্ত কাজ করেছে: ক্রেন্ডেডেভ.আইও / ব্লগ / জেনেরিক- প্রোটোকলস- এবং- তৃতীয়-সংস্থাগুলি টিবিএইচ আইডিক এই জিনিসগুলির কিছু সম্পর্কে আমি কেমন অনুভব করি। দেখে মনে হচ্ছে আমরা ভাষার গর্তের জন্য অ্যাকাউন্টিং করছি তবে অ্যাডিক কীভাবে অ্যাপল এর মধ্যে কিছু তৈরি করবে
আইব্যাবে

109

প্রোটোকল কেন তাদের সাথে খাপ খায় না?

প্রোটোকলগুলিকে সাধারণ ক্ষেত্রে নিজেকে খাপ খাইয়ে দেওয়া মজাদার নয়। স্থিতিশীল প্রোটোকল প্রয়োজনীয়তাগুলির সাথে সমস্যাটি রয়েছে।

এর মধ্যে রয়েছে:

  • static পদ্ধতি এবং বৈশিষ্ট্য
  • Initialisers
  • সংযুক্ত প্রকারগুলি (যদিও বর্তমানে এটি প্রকৃত প্রকার হিসাবে প্রোটোকলের ব্যবহারকে বাধা দেয়)

আমরা জেনেরিক প্লেসোল্ডারের উপর এই প্রয়োজনীয়তাগুলি অ্যাক্সেস করতে পারি T যেখানে T : P- তবে আমরা প্রোটোকল টাইপেই সেগুলি অ্যাক্সেস করতে পারি না , কারণ সেখানে এগিয়ে যাওয়ার মতো কোনও কংক্রিট অনুসারে প্রকার নেই। অতএব আমরা দিতে পারি না Tহতে P

নিম্নলিখিতটি উদাহরণস্বরূপ কী ঘটবে তা বিবেচনা করুন যদি আমরা এই Arrayএক্সটেনশনটিকে প্রযোজ্য হতে পারি [P]:

protocol P {
  init()
}

struct S  : P {}
struct S1 : P {}

extension Array where Element : P {
  mutating func appendNew() {
    // If Element is P, we cannot possibly construct a new instance of it, as you cannot
    // construct an instance of a protocol.
    append(Element())
  }
}

var arr: [P] = [S(), S1()]

// error: Using 'P' as a concrete type conforming to protocol 'P' is not supported
arr.appendNew()

আমরা সম্ভবত কল করতে পারবেন না appendNew()একটি উপর [P], কারণP ( Element) একটি কংক্রিট প্রকার নয় এবং সেইজন্য instantiated করা যাবে না। এটি অবশ্যই কংক্রিট-টাইপযুক্ত উপাদানগুলির সাথে একটি অ্যারেতে কল করা উচিত , যেখানে সেই ধরণের অনুসারে P

স্থির পদ্ধতি এবং সম্পত্তির প্রয়োজনীয়তার সাথে এটি একটি অনুরূপ গল্প:

protocol P {
  static func foo()
  static var bar: Int { get }
}

struct SomeGeneric<T : P> {

  func baz() {
    // If T is P, what's the value of bar? There isn't one – because there's no
    // implementation of bar's getter defined on P itself.
    print(T.bar)

    T.foo() // If T is P, what method are we calling here?
  }
}

// error: Using 'P' as a concrete type conforming to protocol 'P' is not supported
SomeGeneric<P>().baz()

আমরা কথা বলতে পারি না SomeGeneric<P>। আমরা (কিভাবে আছে নোটিশ স্ট্যাটিক প্রোটোকল প্রয়োজনীয়তা কংক্রিট বাস্তবায়নের প্রয়োজন কোন এর বাস্তবায়নের foo()বা barউপরোক্ত উদাহরণের সংজ্ঞায়িত)। যদিও আমরা এই প্রয়োজনগুলির বাস্তবায়নকে কোনও Pএক্সটেনশনে সংজ্ঞায়িত করতে পারি তবে এগুলি কেবলমাত্র সেই ধরণের কংক্রিটের জন্যই সংজ্ঞায়িত করা হয় P- আপনি এখনও সেগুলিকে Pনিজে কল করতে পারবেন না ।

এ কারণে, সুইফট নিজেকে প্রোটোকলকে এমন এক প্রকারের মতো ব্যবহার করতে আমাদের সম্পূর্ণরূপে অস্বীকার করে যা নিজের মতো করে - কারণ যখন প্রোটোকলের স্থির প্রয়োজনীয়তা থাকে তখন তা হয় না।

আপনার অবশ্যই যেমন ইনস্ট্যান্স প্রোটোকল প্রয়োজনীয়তাগুলি সমস্যাযুক্ত নয় প্রোটোকলের সাথে সঙ্গতিপূর্ণ এমন একটি প্রকৃত উদাহরণে তাদের কল (এবং অবশ্যই প্রয়োজনীয়তা প্রয়োগ করা উচিত)। সুতরাং যেমন টাইপ করা কোনও প্রয়োজনে কল করার সময় P, আমরা কেবলমাত্র সেই কলটি সেই প্রয়োজনের অন্তর্নিহিত কংক্রিটের ধরণের প্রয়োগের দিকে ফরোয়ার্ড করতে পারি।

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

সম্পাদনা করুন: এবং নীচে অন্বেষণ করা হিসাবে, এটি সুইফট দল কী লক্ষ্য করে চলেছে তা দেখে মনে হচ্ছে না।


@objc প্রোটোকল

এবং প্রকৃতপক্ষে, ভাষা ঠিক একইভাবে আচরণ করে@objc প্রোটোকলগুলির করে। যখন তাদের স্থিতিশীল প্রয়োজনীয়তা নেই, তারা নিজেরাই খাপ খায়।

নিম্নলিখিত সংকলনগুলি ঠিক জরিমানা:

import Foundation

@objc protocol P {
  func foo()
}

class C : P {
  func foo() {
    print("C's foo called!")
  }
}

func baz<T : P>(_ t: T) {
  t.foo()
}

let c: P = C()
baz(c)

bazযে প্রয়োজন Tকে কনর্ফাম করে P; তবে আমরা এর Pজন্য বিকল্প স্থাপন করতে পারি Tকারণ Pস্থির প্রয়োজনীয়তা নেই। যদি আমরা এতে স্থিতিশীল প্রয়োজনীয়তা যুক্ত করি P, উদাহরণটি আর সংকলন করে না:

import Foundation

@objc protocol P {
  static func bar()
  func foo()
}

class C : P {

  static func bar() {
    print("C's bar called")
  }

  func foo() {
    print("C's foo called!")
  }
}

func baz<T : P>(_ t: T) {
  t.foo()
}

let c: P = C()
baz(c) // error: Cannot invoke 'baz' with an argument list of type '(P)'

সুতরাং এই সমস্যার সমাধানের জন্য একটি হ'ল আপনার প্রোটোকল তৈরি করা @objc। মঞ্জুর, এটি বেশিরভাগ ক্ষেত্রেই আদর্শ আদর্শ নয়, কারণ এটি আপনার অনুকরণীয় ধরণেরগুলি ক্লাস হতে বাধ্য করে, পাশাপাশি ওবজ-সি রানটাইম প্রয়োজন, অতএব লিনাক্স-এর মতো নন-অ্যাপল প্ল্যাটফর্মগুলিতে এটি কার্যকর করে তোলে না।

তবে আমি সন্দেহ করি যে ভাষাটি ইতিমধ্যে প্রোটোকলের জন্য 'স্থির প্রয়োজনীয়তা ছাড়াই প্রোটোকলটি নিজের কাছে খাপ খাইয়ে দেয়' এমন প্রাথমিক কারণগুলির মধ্যে অন্যতম (কারণ) @objc। তাদের চারপাশে লেখা জেনেরিক কোডটি কম্পাইলার দ্বারা উল্লেখযোগ্যভাবে সরল করা যায়।

কেন? কারণ @objcপ্রোটোকল-টাইপ করা মানগুলি কার্যকরভাবে কেবল শ্রেণীর রেফারেন্স যার প্রয়োজনীয়তা ব্যবহার করে প্রেরণ করা হয় objc_msgSend। ফ্লিপ দিকে, অ-@objc -প্রোটোকল-টাইপ করা মানগুলি আরও জটিল, কারণ এগুলি উভয়ই তাদের (সম্ভাব্য পরোক্ষভাবে সঞ্চিত) মোড়ানো মানের মেমরি পরিচালনা করতে এবং পৃথকগুলির জন্য কোন প্রয়োগগুলি কল করতে হবে তা নির্ধারণ করার জন্য মান এবং সাক্ষী সারণী উভয়কেই বহন করে carry প্রয়োজনীয়তা, যথাক্রমে।

@objcপ্রোটোকলের জন্য এই সরলীকৃত উপস্থাপনার কারণে, এই জাতীয় প্রোটোকল ধরণের Pএকটি মান কিছু জেনেরিক স্থানধারকের 'জেনেরিক মান' হিসাবে একই মেমরির উপস্থাপনা ভাগ করে নিতে পারে T : P, সম্ভবতঃ সুইফট টিমের পক্ষে স্ব-সংশ্লেষণের অনুমতি দেওয়া সহজ হয়। অ- @objcপ্রোটোকলগুলির ক্ষেত্রেও এটি একই সত্য নয় তবে যেমন জেনেরিক মানগুলি বর্তমানে মান বা প্রোটোকল সাক্ষীর সারণী বহন করে না।

তবে এই বৈশিষ্ট্যটি হয় ইচ্ছাকৃত ও বেসরকারি আউট ঘূর্ণিত করা আশা হয় @objc, প্রোটোকল যেমন সুইফট দলের সদস্য স্লাভা Pestov দ্বারা নিশ্চিত করা এসআর-55 এর মন্তব্য এটা (দ্বারা অনুরোধ জানানো সম্পর্কে আপনার প্রশ্নের জবাবে এই প্রশ্নের ):

ম্যাট নিউবুর্গ একটি মন্তব্য যুক্ত করেছেন - 7 সেপ্টেম্বর 2017 1:33 অপরাহ্ন

এটি সংকলন করে:

@objc protocol P {}
class C: P {}

func process<T: P>(item: T) -> T { return item }
func f(image: P) { let processed: P = process(item:image) }

সংযোজন @objcএটি সংকলন করে তোলে; এটি অপসারণ এটি আবার সংকলন করে না। স্ট্যাক ওভারফ্লোতে থাকা আমাদের মধ্যে কেউ কেউ এই আশ্চর্যজনক আবিষ্কার করে এবং তা জানতে ইচ্ছে করে বা বগি অ্যাড-কেস কিনা তা জানতে চাই।

স্লাভা পেস্তভ একটি মন্তব্য যুক্ত করেছেন - 7 সেপ্টেম্বর 2017 1:53 পিএম

এটি ইচ্ছাকৃত - এই নিষেধাজ্ঞাকে উত্তোলনই এই বাগটি সম্পর্কে। যেমন আমি বলেছিলাম এটি জটিল এবং আমাদের কাছে এখনও কোনও দৃ concrete় পরিকল্পনা নেই।

সুতরাং আশা করি এটি এমন কিছু যা ভাষা একদিন অ- @objcপ্রোটোকলগুলির পক্ষেও সমর্থন করবে ।

তবে নন- @objcপ্রোটোকলগুলির জন্য কী কী বর্তমান সমাধান রয়েছে ?


প্রোটোকল সীমাবদ্ধতার সাথে এক্সটেনশনগুলি কার্যকর করা

সুইফট ৩.১-এ, আপনি যদি কোনও সীমাবদ্ধতার সাথে কোনও এক্সটেনশন চান তবে প্রদত্ত জেনেরিক স্থানধারক বা সম্পর্কিত প্রকারটি অবশ্যই প্রদত্ত প্রোটোকল প্রকার (কেবলমাত্র একটি কংক্রিট নয় যা সেই প্রোটোকলের সাথে সঙ্গতিপূর্ণ) হতে পারে - আপনি কেবল কোনও ==সীমাবদ্ধতার সাথে এটি সংজ্ঞায়িত করতে পারেন ।

উদাহরণস্বরূপ, আমরা আপনার অ্যারে এক্সটেনশনটি এইভাবে লিখতে পারি:

extension Array where Element == P {
  func test<T>() -> [T] {
    return []
  }
}

let arr: [P] = [S()]
let result: [S] = arr.test()

অবশ্যই এটি এখন আমাদের এটির সাথে মেশানো কংক্রিট ধরণের উপাদানগুলির সাথে অ্যারেতে কল করতে বাধা দেয় P। আমরা কখন এটির জন্য একটি অতিরিক্ত এক্সটেনশন নির্ধারণ করে Element : Pএবং কেবল == Pএক্সটেনশনের দিকে এগিয়ে দিয়ে সমাধান করতে পারি :

extension Array where Element : P {
  func test<T>() -> [T] {
    return (self as [P]).test()
  }
}

let arr = [S()]
let result: [S] = arr.test()

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

সুইফট ৩.১ এর আগে রব তার উত্তরে যেমনটি অর্জন করার সর্বাধিক সাধারণ উপায়টি হ'ল এটির জন্য একটি মোড়কের ধরণটি তৈরি করা [P], যা আপনি নিজের এক্সটেনশন পদ্ধতিটি (গুলি) চালু করতে পারেন।


প্রোটোকল-টাইপ করা উদাহরণটি কোনও জড়িত জেনেরিক স্থানধারীর কাছে পাঠানো

নিম্নলিখিত (স্বীকৃত তবে অস্বাভাবিক নয়) বিবেচনা করুন:

protocol P {
  var bar: Int { get set }
  func foo(str: String)
}

struct S : P {
  var bar: Int
  func foo(str: String) {/* ... */}
}

func takesConcreteP<T : P>(_ t: T) {/* ... */}

let p: P = S(bar: 5)

// error: Cannot invoke 'takesConcreteP' with an argument list of type '(P)'
takesConcreteP(p)

আমরা বর্তমানে জেনেরিক স্থানধারীর বিকল্প নিতে পারি না বলে আমরা পাস pকরতে takesConcreteP(_:)পারি না । আসুন কয়েকটি দৃষ্টিতে যাচাই করে আমরা এই সমস্যাটি সমাধান করতে পারি atPT : P

1. অস্তিত্ব খোলার

বরং প্রতিস্থাপন করার প্রচেষ্টার চেয়ে Pজন্য T : P, কি যদি আমরা অন্তর্নিহিত কংক্রিট টাইপ মধ্যে খনন করতে পারে যে Pটাইপ মান মোড়ানো এবং বিকল্প পরিবর্তে ছিল? দুর্ভাগ্যক্রমে, এর জন্য একটি ভাষা বৈশিষ্ট্য প্রয়োজন যা খোলার অস্তিত্ব বলা হয় , যা বর্তমানে ব্যবহারকারীদের জন্য সরাসরি উপলব্ধ নয়।

যাইহোক, সুইফট করে পরোক্ষভাবে খোলা existentials (প্রোটোকল টাইপ মান) যখন তাদের উপর সদস্যদের অ্যাক্সেস (অর্থাত এটা রানটাইম টাইপ আউট digs এবং এটি একটি জেনেরিক স্থানধারক আকারে অ্যাক্সেসযোগ্য করুন)। প্রোটোকল এক্সটেনশনে আমরা এই সত্যটি কাজে লাগাতে পারি P:

extension P {
  func callTakesConcreteP/*<Self : P>*/(/*self: Self*/) {
    takesConcreteP(self)
  }
}

Selfএক্সটেনশন পদ্ধতিটি গ্রহণ করে এমন অন্তর্নিহিত জেনেরিক স্থানধারকটি নোট করুন , যা অন্তর্নিহিত selfপ্যারামিটারটি টাইপ করতে ব্যবহৃত হয় - সমস্ত প্রোটোকল এক্সটেনশন সদস্যদের সাথে পর্দার আড়ালে এটি ঘটে। প্রোটোকল টাইপ করা Pমানটিতে এই জাতীয় পদ্ধতি কল করার সময় , সুইফট অন্তর্নিহিত কংক্রিটের প্রকারটি বের করে, এবং Selfজেনেরিক স্থানধারককে সন্তুষ্ট করতে এটি ব্যবহার করে । এই জন্যই আমরা কল করতে পারবেন takesConcreteP(_:)সঙ্গে self- আমরা পরিতৃপ্ত করছি Tসঙ্গে Self

এর অর্থ হ'ল আমরা এখন বলতে পারি:

p.callTakesConcreteP()

এবং takesConcreteP(_:)এর জেনেরিক স্থানধারাকে Tঅন্তর্নিহিত কংক্রিটের ধরণের (এই ক্ষেত্রে S) সন্তুষ্ট হওয়ার সাথে ডাকা হয় । নোট করুন যে এটি "নিজেদের প্রতিপালনকারী প্রোটোকলগুলি" নয়, যেমনটি আমরা কংক্রিটের পরিবর্তে পরিবর্তিত করব P- প্রোটোকলে একটি স্ট্যাটিক প্রয়োজনীয়তা যুক্ত করার চেষ্টা করুন এবং যখন আপনি এটিকে ভিতরে থেকে কল করবেন তখন কী ঘটেছিল তা দেখার চেষ্টা করুন takesConcreteP(_:)

যদি সুইফট তাদের সাথে মানিয়ে নিতে প্রোটোকলগুলি বর্জন করতে থাকে তবে জেনেরিক ধরণের পরামিতিগুলির যুক্তি হিসাবে তাদের পাস করার চেষ্টা করার পরে পরবর্তী সেরা বিকল্পটি স্পষ্টতই অস্তিত্বের উদ্বোধন করবে - কার্যকরভাবে আমাদের প্রোটোকল এক্সটেনশন ট্রাম্পোলিন যা করেছে তা কেবল বয়লারপ্লেট ছাড়াই করে।

তবে মনে রাখবেন যে প্রোটোকলগুলি তাদের সাথে খাপ খায় না এমন সমস্যার উদ্বোধন কোনও সাধারণ সমাধান নয়। এটি প্রোটোকল-টাইপযুক্ত মানগুলির ভিন্নজাতীয় সংগ্রহগুলি নিয়ে কাজ করে না, যার সকলের অন্তর্নিহিত কংক্রিটের বিভিন্ন ধরণের থাকতে পারে। উদাহরণস্বরূপ, বিবেচনা করুন:

struct Q : P {
  var bar: Int
  func foo(str: String) {}
}

// The placeholder `T` must be satisfied by a single type
func takesConcreteArrayOfP<T : P>(_ t: [T]) {}

// ...but an array of `P` could have elements of different underlying concrete types.
let array: [P] = [S(bar: 1), Q(bar: 2)]

// So there's no sensible concrete type we can substitute for `T`.
takesConcreteArrayOfP(array) 

একই কারণে, একাধিক Tপরামিতিগুলির সাথে একটি ফাংশনও সমস্যাযুক্ত হবে, কারণ পরামিতিগুলি অবশ্যই একই ধরণের আর্গুমেন্ট গ্রহণ করতে পারে - তবে আমাদের যদি দুটি Pমান থাকে তবে সংকলনের সময় আমরা গ্যারান্টি দেওয়ার কোনও উপায় নেই যে উভয়েরই একই অন্তর্নিহিত কংক্রিট রয়েছে have টাইপ করুন।

এই সমস্যাটি সমাধান করার জন্য, আমরা একটি টাইপ ইরেজার ব্যবহার করতে পারি।

2. একটি টাইপ ইরেজার তৈরি করুন

রব যেমন বলেছিলেন , টাইপ ইরেজার হ'ল প্রোটোকলগুলির নিজের মতো না হওয়ার সমস্যাটির সর্বাধিক সাধারণ সমাধান। এগুলি আমাদের প্রোটোকল-টাইপ করা উদাহরণটিকে একটি কংক্রিট প্রকারে মোড়াতে দেয় যা সেই প্রোটোকলের সাথে সঙ্গতিপূর্ণ হয়, উদাহরণের প্রয়োজনীয়তাগুলি অন্তর্নিহিত উদাহরণটিতে প্রেরণ করে।

সুতরাং, আসুন একটি ধরণের মুছে ফেলা বাক্সটি তৈরি করুন যা Pএর অন্তর্নিহিত স্বেচ্ছাসেবী উদাহরণগুলিতে অনুসরণ করে যা এর প্রয়োজনীয়তাগুলি ফরওয়ার্ড করে P:

struct AnyP : P {

  private var base: P

  init(_ base: P) {
    self.base = base
  }

  var bar: Int {
    get { return base.bar }
    set { base.bar = newValue }
  }

  func foo(str: String) { base.foo(str: str) }
}

এখন আমরা কেবল AnyPপরিবর্তে পদে কথা বলতে পারি P:

let p = AnyP(S(bar: 5))
takesConcreteP(p)

// example from #1...
let array = [AnyP(S(bar: 1)), AnyP(Q(bar: 2))]
takesConcreteArrayOfP(array)

এখন, কেন আমাদের সেই বাক্সটি তৈরি করতে হয়েছিল তা এক মুহুর্তের জন্য বিবেচনা করুন। যেমনটি আমরা প্রথম দিকে আলোচনা করেছি, প্রফোটালের স্থির প্রয়োজনীয়তার ক্ষেত্রে সুইফ্টের একটি কংক্রিট ধরণের প্রয়োজন। Pস্থিতিশীল প্রয়োজন ছিল কিনা তা বিবেচনা করুন - আমাদের এটি প্রয়োগ করতে হবেAnyP । তবে এটি কী হিসাবে বাস্তবায়ন করা উচিত ছিল? আমরা Pএখানে মেনে চলতে থাকা স্বেচ্ছাসেবী দৃষ্টান্তগুলির সাথে মোকাবিলা করছি - আমরা জানি না যে তাদের অন্তর্নিহিত কংক্রিটের ধরণের স্ট্যাটিক প্রয়োজনীয়তা কীভাবে প্রয়োগ করা হয়, তাই আমরা অর্থপূর্ণভাবে এটি প্রকাশ করতে পারি না AnyP

সুতরাং, উদাহরণস্বরূপ প্রোটোকল প্রয়োজনীয়তার ক্ষেত্রে এই ক্ষেত্রে সমাধানটি কেবল সত্যই কার্যকর । সাধারণ ক্ষেত্রে, আমরা এখনও মেনে চলতে থাকা Pকংক্রিটের ধরণ হিসাবে আচরণ করতে পারি না P


2
সম্ভবত আমি কেবল ঘন হয়ে যাচ্ছি, তবে কেন স্থির ক্ষেত্রে বিশেষ তা আমি বুঝতে পারি না। আমরা (সংকলক) সংকলনের সময় প্রোটোটলের স্থিতিশীল সম্পত্তি সম্পর্কে ঠিক তত কম বা কম জানি যেমন আমরা কোনও প্রোটোকলের উদাহরণ সম্পত্তি সম্পর্কে জানি, যথা গ্রহণকারী এটি প্রয়োগ করবে। তাহলে পার্থক্য কী?
ম্যাট

1
@ ম্যাট একটি প্রোটোকল-টাইপ করা উদাহরণ (অর্থাত্ কংক্রিট-টাইপড উদাহরণটি অস্তিত্বের মধ্যে আবৃত P) ঠিক আছে কারণ আমরা কেবলমাত্র প্রয়োজনীয় প্রয়োজনগুলিকে অন্তর্নিহিত উদাহরণগুলিতে ফরোয়ার্ড করতে পারি। তবে, একটি প্রোটোকল নিজেই টাইপ করার জন্য (যেমন একটি P.Protocol, আক্ষরিকভাবে কেবল প্রোটোকলের বর্ণনা দেয়) - কোনও গ্রহণকারী নেই, সুতরাং স্থির প্রয়োজনীয়তাগুলিতে কল করার কিছুই নেই, যার কারণে উপরের উদাহরণে আমরা থাকতে পারি না SomeGeneric<P>(এটি P.Type(অস্তিত্বের মেটাটাইপ) এর জন্য আলাদা , যা এমন কোনও কিছুর একটি দৃ concrete় মেটাটাইপ বর্ণনা করে P- তবে এটি অন্য গল্প)
হামিশ

এই পৃষ্ঠার শীর্ষে আমি যে প্রশ্নটি জিজ্ঞাসা করব তা হ'ল কেন একজন প্রোটোকল-ধরণের গ্রহণকারী ভাল এবং প্রোটোকল টাইপটি নিজেই নয়। আমি বুঝতে পারি যে প্রোটোকল টাইপের জন্য নিজেই কোনও গ্রহণকারী নেই। - আমি যা বুঝতে পারি না তা হ'ল অ্যাডাপ্টিং টাইপের কাছে স্থির কলগুলি ফরোয়ার্ড করা কেন আরও শক্ত, কারণ দত্তক গ্রহণকারী ধরণের কলকে ফরোয়ার্ড করার চেয়ে। আপনি একটি যুক্তি দিচ্ছেন যে এখানে অসুবিধার কারণ বিশেষত স্থির প্রয়োজনীয়তার প্রকৃতির কারণ, তবে স্থিতির প্রয়োজনীয়তা উদাহরণের প্রয়োজনীয়তার চেয়ে কীভাবে আরও কঠিন তা আমি দেখতে পাই না।
ম্যাট

@ ম্যাট এটি স্থিতিশীল প্রয়োজনগুলির তুলনায় উদাহরণস্বরূপ "শক্ত" নয় - সংকলক উদাহরণগুলির জন্য অস্তিত্বের (যেমন উদাহরণস্বরূপ টাইপ করা হয়েছে P) এবং অস্তিত্বমূলক মেটাটাইপগুলি (অর্থাত্ P.Typeमेटाটাটাইপস) উভয়ই সূক্ষ্মভাবে পরিচালনা করতে পারে । সমস্যাটি জেনারিকদের জন্য - আমরা আসলে পছন্দের মতো তুলনা করি না। কখন Tহয় P, স্থির প্রয়োজনীয়তাগুলি ফরোয়ার্ড করার জন্য কোনও আন্ডারলিং কংক্রিট (মেটা) টাইপ নেই ( Tএটি একটি P.Protocol, একটি নয় P.Type) ....
হামিশ

1
আমি সত্যিই দৃ etc়তা ইত্যাদির বিষয়ে চিন্তা করি না, আমি কেবল অ্যাপ্লিকেশন লিখতে চাই এবং যদি মনে হয় এটির মতো কাজ করা উচিত ঠিক উচিত। ভাষাটি কেবল একটি সরঞ্জাম হতে হবে, কোনও পণ্য নয়। যদি এমন কিছু কেস থাকে যে এটি সত্যিই কাজ করে না তবে জরিমানা those ক্ষেত্রে এটিকে বাতিল করে দিন তবে প্রত্যেককে তার পক্ষে কাজ করা কেসগুলি ব্যবহার করতে দিন এবং তাদের অ্যাপ্লিকেশনগুলি লেখার সুযোগ দিন।
জোনাথন

17

আপনি যদি CollectionTypeপ্রোটোকলের পরিবর্তে প্রোটোকল Arrayএবং কংক্রিটের ধরণ হিসাবে প্রটোকল দ্বারা সীমাবদ্ধ করেন তবে আপনি আগের কোডটি নিম্নরূপে আবার লিখতে পারেন।

protocol P { }
struct S: P { }

let arr:[P] = [ S() ]

extension CollectionType where Generator.Element == P {
    func test<T>() -> [T] {
        return []
    }
}

let result : [S] = arr.test()

আমি মনে করি না সংগ্রহ বনাম অ্যারে এখানে প্রাসঙ্গিক, গুরুত্বপূর্ণ পরিবর্তন == Pবনাম ব্যবহার করা : P। সাথে == মূল উদাহরণটিও কার্যকর হয়। যদি আমি একটি তৈরি করুন: এবং একটি সম্ভাব্য সমস্যা (প্রসঙ্গ উপর নির্ভর করে) == সঙ্গে এটি বাদ উপ-প্রোটোকলের হয় protocol SubP: P, এবং তারপর সংজ্ঞায়িত arrহিসাবে [SubP]তারপর arr.test()আর কাজ করবে না (ত্রুটি: SubP ও P সমতুল্য হতে হবে)।
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.