আপনি কীভাবে সি # - বা জাভা-জাতীয় ভাষায় বীজগণিত ডেটা টাইপগুলি এনকোড করবেন?


58

কিছু সমস্যা রয়েছে যা সহজেই বীজগণিত ডেটা টাইপগুলির দ্বারা সমাধান করা হয়, উদাহরণস্বরূপ একটি তালিকার ধরণটি খুব সংক্ষিপ্তভাবে হিসাবে প্রকাশ করা যেতে পারে:

data ConsList a = Empty | ConsCell a (ConsList a)

consmap f Empty          = Empty
consmap f (ConsCell a b) = ConsCell (f a) (consmap f b)

l = ConsCell 1 (ConsCell 2 (ConsCell 3 Empty))
consmap (+1) l

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

দেখা যাচ্ছে যে ওও-স্টাইলের সাব টাইপিংয়ের একটি সুস্পষ্ট ম্যাপিং রয়েছে: ডেটাটাইপ একটি বিমূর্ত বেস শ্রেণিতে পরিণত হয় এবং প্রতিটি ডেটা কনস্ট্রাক্টর একটি কংক্রিট সাবক্লাসে পরিণত হয়। স্কালায় এখানে একটি উদাহরণ রয়েছে:

sealed abstract class ConsList[+T] {
  def map[U](f: T => U): ConsList[U]
}

object Empty extends ConsList[Nothing] {
  override def map[U](f: Nothing => U) = this
}

final class ConsCell[T](first: T, rest: ConsList[T]) extends ConsList[T] {
  override def map[U](f: T => U) = new ConsCell(f(first), rest.map(f))
}

val l = (new ConsCell(1, new ConsCell(2, new ConsCell(3, Empty)))
l.map(1+)

নিখরচায় সাবক্লাসিংয়ের বাইরে কেবলমাত্র প্রয়োজনীয় জিনিসটি ক্লাসগুলি সিল করার একটি উপায় , অর্থাৎ শ্রেণিবিন্যাসে সাবক্লাস যুক্ত করা অসম্ভব করার একটি উপায়।

সি # বা জাভার মতো ভাষায় আপনি কীভাবে এই সমস্যার কাছে যাবেন? সি # তে বীজগণিত ডেটা টাইপ ব্যবহার করার চেষ্টা করার সময় আমি যে দুটি হোঁচট খেয়েছি তা হ'ল:

  • সি # তে নীচের প্রকারটি কী বলা হয় তা আমি বুঝতে পারি না (যেমন আমি কী লিখব তা বুঝতে পারি না class Empty : ConsList< ??? >)
  • সিল করার কোনও উপায় আমি বের করতে পারিনি ConsListযাতে শ্রেণিবিন্যাসে কোনও সাবক্ল্যাস যুক্ত করা যায় না

সি # এবং / অথবা জাভাতে বীজগণিত ডেটা টাইপগুলি প্রয়োগ করার সর্বাধিক অভিজাত পদ্ধতি কী হবে? অথবা, যদি এটি সম্ভব না হয় তবে অহংকার প্রতিস্থাপনটি কী হবে?


4
আগ্রহের বিষয়: সি #
আকাশম

3
সি # হ'ল ওওপি ভাষা। OOP ব্যবহার করে সমস্যার সমাধান করুন। অন্য কোনও দৃষ্টান্ত ব্যবহার করার চেষ্টা করবেন না।
ইউফোরিক

7
@ ইউফোরিক সি # সি # 3.0 এর সাথে একটি ব্যবহারযোগ্য কার্যকর কার্যকরী ভাষায় পরিণত হয়েছে। প্রথম শ্রেণীর ফাংশন, অন্তর্নির্মিত সাধারণ ক্রিয়ামূলক ক্রিয়াকলাপ, মনড।
মৌরিসিও শেফার

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

3
@ ইউফোরিক, দৃষ্টান্ত, স্কামারাদিগম, কে চিন্তা করে? এডিটিগুলি কার্যকরী প্রোগ্রামিংয়ের (বা ওওপি বা অন্য যে কোনও কিছুই) অরথোগোনাল। যে কোনও ভাষার একটি এএসটি এনকোড করা শালীন ADTs সমর্থন ব্যতীত বেশ ব্যথা এবং সেই ভাষাটি সংকলন করা অন্য একটি দৃষ্টান্ত-অজানস্টিক বৈশিষ্ট্য, প্যাটার্ন মেলানো ব্যতীত একটি ব্যথা।
এসকে-লজিক

উত্তর:


42

জাভাতে ক্লাস সিল করার জন্য একটি সহজ, তবে বয়লারপ্লেট ভারী উপায় রয়েছে। আপনি বেস ক্লাসে একটি প্রাইভেট কনস্ট্রাক্টর রেখেছিলেন তারপরে সাবক্ল্যাসগুলি এর ইনার ক্লাস করে নিন।

public abstract class List<A> {

   // private constructor is uncallable by any sublclasses except inner classes
   private List() {
   }

   public static final class Nil<A> extends List<A> {
   }

   public static final class Cons<A> extends List<A> {
      public final A head;
      public final List<A> tail;

      public Cons(A head, List<A> tail) {
         this.head = head;
         this.tail = tail;
      }
   }
}

প্রেরণের জন্য দর্শনার্থীর প্যাটার্নটি নিয়ে যান।

আমার প্রকল্প jADT: জাভা বীজগণিত ডেটা টাইপস আপনার জন্য সমস্ত বয়লারপ্লেট উত্পন্ন করে https://github.com/JamesIry/jADT


2
কোনওভাবে আমি এখানে আপনার নাম পপ আপ দেখে অবাক হই না! ধন্যবাদ, আমি এই প্রতিমাটি জানতাম না।
Jörg ডব্লু মিটাগ

4
আপনি যখন "বয়লারপ্লেট ভারী" বলেছেন তখন আমি আরও খারাপ কিছুর জন্য প্রস্তুত ছিলাম ;-) জাভা বয়লারপ্লেট সহ কখনও কখনও খারাপ হতে পারে।
জোচিম সউর

তবে এটি রচনা করে না: একটি কাস্টের মাধ্যমে এটি চাপানো ছাড়া আপনার টাইপ এ এর ​​বিশেষায়িত করার কোনও উপায় নেই (আমি মনে করি)
নিকোলাস

দুর্ভাগ্যক্রমে এটি আরও কয়েকটি জটিল যোগফলের প্রতিনিধিত্ব করতে অক্ষম বলে মনে হয়, যেমন Eitherআমার প্রশ্নটি
জোয়ে হিউল

20

আপনি দর্শনার্থীর প্যাটার্ন ব্যবহার করে এটি অর্জন করতে পারেন , যা প্যাটার্ন মিলের পরিপূরক করবে। উদাহরণ স্বরূপ

data List a = Nil | Cons { value :: a, sublist :: List a }

জাভা হিসাবে লেখা যেতে পারে

interface List<T> {
    public <R> R accept(Visitor<T,R> visitor);

    public static interface Visitor<T,R> {
        public R visitNil();
        public R visitCons(T value, List<T> sublist);
    }
}

final class Nil<T> implements List<T> {
    public Nil() { }

    public <R> R accept(Visitor<T,R> visitor) {
        return visitor.visitNil();
    }
}
final class Cons<T> implements List<T> {
    public final T value;
    public final List<T> sublist;

    public Cons(T value, List<T> sublist) {
        this.value = value;
        this.sublist = sublist;
    }

    public <R> R accept(Visitor<T,R> visitor) {
        return visitor.visitCons(value, sublist);
    }
}

সিলিং Visitorক্লাস দ্বারা অর্জন করা হয় । এর প্রতিটি পদ্ধতিতে ঘোষণা করা হয় যে কীভাবে একটি সাবক্লাসটি ডিকনস্ট্রাক্ট করতে হয়। আপনি আরও সাবক্ল্যাস যুক্ত করতে পারেন, তবে এটি প্রয়োগ acceptকরতে হবে এবং একটি visit...পদ্ধতি কল করে, সুতরাং এটির মতো আচরণ করতে হবে Consবা পছন্দ করতে হবে Nil


13

আপনি যদি সি # নামের প্যারামিটারগুলিকে অপব্যবহার করেন (সি # 4.0 তে প্রবর্তিত), আপনি বীজগণিত ডেটা ধরণের তৈরি করতে পারেন যা সহজেই মিলতে পারে:

Either<string, string> e = MonthName(2);

// Match with no return value.
e.Match
(
    Left: err => { Console.WriteLine("Could not convert month: {0}", err); },
    Right: name => { Console.WriteLine("The month is {0}", name); }
);

// Match with a return value.
string monthName =
    e.Match
    (
        Left: err => null,
        Right: name => name
    );
Console.WriteLine("monthName: {0}", monthName);

এখানে Eitherশ্রেণীর বাস্তবায়ন :

public abstract class Either<L, R>
{
    // Subclass implementation calls the appropriate continuation.
    public abstract T Match<T>(Func<L, T> Left, Func<R, T> Right);

    // Convenience wrapper for when the caller doesn't want to return a value
    // from the match expression.
    public void Match(Action<L> Left, Action<R> Right)
    {
        this.Match<int>(
            Left: x => { Left(x); return 0; },
            Right: x => { Right(x); return 0; }
        );
    }
}

public class Left<L, R> : Either<L, R>
{
    L Value {get; set;}

    public Left(L Value)
    {
        this.Value = Value;
    }

    public override T Match<T>(Func<L, T> Left, Func<R, T> Right)
    {
        return Left(Value);
    }
}

public class Right<L, R> : Either<L, R>
{
    R Value { get; set; }

    public Right(R Value)
    {
        this.Value = Value;
    }

    public override T Match<T>(Func<L, T> Left, Func<R, T> Right)
    {
        return Right(Value);
    }
}

আমি আগে এই কৌশলটির একটি জাভা সংস্করণ দেখেছি, তবে ল্যাম্বডাস এবং নামযুক্ত প্যারামিটারগুলি এটিকে এত পাঠযোগ্য করে তোলে। +1 টি!
ডোভাল

1
আমি মনে করি যে এখানে সমস্যাটি হ'ল রাইট ত্রুটির ধরণের ক্ষেত্রে জেনেরিক নয়। এর মতো কিছু: class Right<R> : Either<Bot,R>যেখানে হয় কোভেরিয়েন্ট (আউট) টাইপ পরামিতিগুলির সাথে একটি ইন্টারফেসে পরিবর্তিত হয় এবং বট হ'ল নীচের প্রকার (অবজেক্টের বিপরীতে প্রতিটি অন্যান্য ধরণের উপপ্রকার)। আমি মনে করি না সি # এর নীচের ধরণ রয়েছে।
ক্রয়েড

5

সি # তে, আপনি এই Emptyধরণের থাকতে পারবেন না , কারণ, পুনঃসংশোধনের কারণে, বেস সদস্যগুলি বিভিন্ন সদস্যের জন্য আলাদা। আপনি কেবল থাকতে পারেন Empty<T>; যে দরকারী না।

জাভাতে, আপনার Empty : ConsListমুছে ফেলা টাইপের কারণে হতে পারে, তবে টাইপ চেকার কোথাও চিৎকার করবে না কিনা তা আমি নিশ্চিত নই।

তবে যেহেতু উভয় ভাষারই ভাষা রয়েছে তাই nullআপনি তাদের সমস্ত রেফারেন্সের ধরনগুলি "যাই হোক না কেন | নাল" বলে মনে করতে পারেন । সুতরাং nullএটি থেকে কী উত্পন্ন হয়েছে তা নির্দিষ্ট করে না দেওয়ার জন্য আপনি কেবল "খালি" হিসাবে ব্যবহার করবেন ।


সমস্যাটি nullহ'ল এটি খুব সাধারণ: এটি কোনও কিছুর অনুপস্থিতি অর্থাৎ সাধারণভাবে শূন্যতার প্রতিনিধিত্ব করে, তবে আমি তালিকার উপাদানগুলির উপস্থিতি, বিশেষত একটি খালি তালিকা উপস্থাপন করতে চাই। একটি খালি তালিকা এবং একটি খালি গাছে আলাদা ধরণের থাকতে হবে। এছাড়াও, খালি তালিকাটির একটি আসল মান হওয়া দরকার কারণ এটির নিজস্ব আচরণ এখনও রয়েছে তাই এটির নিজস্ব পদ্ধতি থাকা দরকার। তালিকাটি তৈরি করতে [1, 2, 3], আমি বলতে চাই Empty.prepend(3).prepend(2).prepend(1)(বা ডান-অ্যাসোসিয়েটিভ অপারেটরগুলির ভাষায় 1 :: 2 :: 3 :: Empty), তবে আমি বলতে পারি না null.prepend …
জার্গ ডব্লু মিট্টাগ

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

কিছু কৌতূহল বর্ধনের পদ্ধতি নালগুলিতে কল করতে 'পদ্ধতি' কল করতে পারে (অবশ্যই এটি সমস্ত সত্যই স্থির)
জে কে।

আপনি একটি থাকতে পারে Emptyএবং Empty<>এবং অন্তর্নিহিত রূপান্তর অপারেটার অপব্যবহার মোটামুটি ব্যবহারিক সিমুলেশন করার অনুমতি, যদি আপনি চান। মূলত, আপনি Emptyকোড ব্যবহার করেন তবে সমস্ত ধরণের স্বাক্ষর ইত্যাদি কেবল জেনেরিক রূপগুলি ব্যবহার করে।
ইমন নেরবোন

3

নিখরচায় সাবক্লাসিংয়ের বাইরে কেবলমাত্র প্রয়োজনীয় জিনিসটি ক্লাসগুলি সিল করার একটি উপায়, অর্থাৎ শ্রেণিবিন্যাসে সাবক্লাস যুক্ত করা অসম্ভব করার একটি উপায়।

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

আমি জানি না যদিও এটি আপনার আসল সমস্যাটির সমাধান করবে কিনা ...


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

@ জার্গডব্লিউমিট্যাগ - আচ্ছা জাভা স্পষ্টভাবে সেটিকে সমর্থন করে না ... আপনি যে দৃ strong় অর্থে চান যে তারা চাইছে। অবশ্যই, রানটাইমে অযাচিত সাব টাইপিং ব্লক করতে আপনি বিভিন্ন জিনিস করতে পারেন, তবে তারপরে আপনি "রানটাইম ত্রুটিগুলি যা আপনি প্রত্যাশা করেন না" পান।
স্টিফেন সি

3

ডেটা টাইপ ConsList<A>একটি ইন্টারফেস হিসাবে উপস্থাপন করা যেতে পারে। ইন্টারফেসটি একটি একক deconstructপদ্ধতি উন্মোচিত করে যা আপনাকে সেই ধরণের একটি মান "ডিকনস্ট্রাক্ট" করতে দেয় - যা প্রতিটি সম্ভাব্য কনস্ট্রাক্টরকে পরিচালনা করতে পারে। কোনও deconstructপদ্ধতির কল case ofহ্যাসেল বা এমএল ফর্মের সাথে সাদৃশ্যপূর্ণ ।

interface ConsList<A> {
  <R> R deconstruct(
    Function<Unit, R> emptyCase,
    Function<Pair<A,ConsList<A>>, R> consCase
  );
}

deconstructপদ্ধতি এডিটি প্রতিটি কন্সট্রাকটর জন্য একটি "কলব্যাক" ফাংশন লাগে। আমাদের ক্ষেত্রে এটি খালি তালিকা মামলার জন্য এবং "কনস সেল" কেসের জন্য অন্য একটি ফাংশন গ্রহণ করে।

প্রতিটি কলব্যাক ফাংশন আর্গুমেন্ট হিসাবে কনস্ট্রাক্টর দ্বারা গৃহীত মানগুলি হিসাবে গ্রহণ করে। সুতরাং "খালি তালিকা" কেসটি কোনও আর্গুমেন্ট নেয় না, তবে "কনস সেল" কেস দুটি আর্গুমেন্ট গ্রহণ করে: তালিকার মাথা এবং লেজ।

Tupleক্লাস ব্যবহার করে বা কার্চিং ব্যবহার করে আমরা এই "একাধিক যুক্তি" এনকোড করতে পারি । এই উদাহরণে, আমি একটি সাধারণ Pairক্লাস ব্যবহার করা বেছে নিয়েছি ।

ইন্টারফেস প্রতিটি নির্মাণকারীর জন্য একবার প্রয়োগ করা হয়। প্রথমত, আমাদের "খালি তালিকার" জন্য বাস্তবায়ন রয়েছে। deconstructবাস্তবায়ন কেবল কল emptyCaseকলব্যাক ফাংশন।

class ConsListEmpty<A> implements ConsList<A> {
  public ConsListEmpty() {}

  public <R> R deconstruct(
    Function<Unit, R> emptyCase,
    Function<Pair<A,ConsList<A>>, R> consCase
  ) {
    return emptyCase.apply(new Unit());
  }
}

তারপরে আমরা "কনস সেল" কেসটি একইভাবে প্রয়োগ করি। এবার ক্লাসে বৈশিষ্ট্য রয়েছে: খালি নয় খালি তালিকার মাথা এবং লেজ। ইন deconstructবাস্তবায়ন, যাদের বৈশিষ্ট্য প্রেরণ করা হয় consCaseকলব্যাক ফাংশন।

class ConsListConsCell<A> implements ConsList<A> {
  private A head;
  private ConsList<A> tail;

  public ConsListCons(A head, ConsList<A> tail) {
    this.head = head;
    this.tail = tail;
  }

  public <R> R deconstruct(
    Function<Unit, R> emptyCase,
    Function<Pair<A,ConsList<A>>, R> consCase
  ) {
    return consCase.apply(new Pair<A,ConsList<A>>(this.head, this.tail));
  }
}

এখানে এডিটিগুলির এই এনকোডিংটি ব্যবহার করার উদাহরণ রয়েছে: আমরা একটি reduceফাংশন লিখতে পারি যা তালিকাগুলির উপরে সাধারণ ভাঁজ।

<T> T reduce(Function<Pair<T,A>,T> reducer, T initial, ConsList<T> l) {
  return l.deconstruct(
    ((unit) -> initial),
    ((t) -> reduce(reducer, reducer.apply(initial, t.v1), t.v2))
  );
}

এটি হাসকেলে এই প্রয়োগের সাথে সাদৃশ্যপূর্ণ:

reduce reducer initial l = case l of
  Empty -> initial
  Cons t_v1 t_v2  -> reduce reducer (reducer initial t_v1) t_v2

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

2

নিখরচায় সাবক্লাসিংয়ের বাইরে কেবলমাত্র প্রয়োজনীয় জিনিসটি ক্লাসগুলি সিল করার একটি উপায়, অর্থাৎ শ্রেণিবিন্যাসে সাবক্লাস যুক্ত করা অসম্ভব করার একটি উপায়।

সি # বা জাভার মতো ভাষায় আপনি কীভাবে এই সমস্যার কাছে যাবেন?

এটি করার কোনও ভাল উপায় নেই তবে আপনি যদি কোনও ঘৃণ্য হ্যাক নিয়ে বেঁচে থাকতে ইচ্ছুক হন তবে আপনি বিমূর্ত বেস শ্রেণীর নির্মাণকারীর সাথে কিছু স্পষ্টত ধরণের চেক যোগ করতে পারেন। জাভাতে, এটি এমন কিছু হবে

protected ConsList() {
    Class<?> clazz = getClass();
    if (clazz != Empty.class && clazz != ConsCell.class) throw new Exception();
}

সি # তে এটি পুনরায় সংশোধিত জেনেরিকগুলির কারণে আরও জটিল - সহজ পদ্ধতির মধ্যে ধরণটি স্ট্রিংয়ে রূপান্তর করা এবং ম্যাঙ্গলেটি হতে পারে।

নোট করুন যে জাভাতেও এই প্রক্রিয়াটি তাত্ত্বিকভাবে এমন কাউকে বাইপাস করা যেতে পারে যিনি সত্যিকার অর্থে সিরিয়ালাইজেশন মডেল বা মাধ্যমে যেতে চান sun.misc.Unsafe


1
এটি সি # তে আরও জটিল হবে না:Type type = this.GetType(); if (type != typeof(Empty<T>) && type != typeof(ConsCell<T>)) throw new Exception();
সুইভ

@ স্পটিক, ভাল পর্যবেক্ষণ বেস টাইপটি প্যারামিটারাইজড হবে তা আমি বিবেচনা করছিলাম না।
পিটার টেলর

উজ্জ্বল! আমার ধারণা, "ম্যানুয়াল স্ট্যাটিক টাইপ চেকিং" করার জন্য এটি যথেষ্ট ভাল। আমি দূষিত অভিপ্রায়ের চেয়ে সৎ প্রোগ্রামিং ত্রুটিগুলি অপসারণের দিকে বেশি নজর দিচ্ছি।
জার্গ ডব্লু মিট্টাগ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.