Functor
হাস্কেলের ধরণের শ্রেণি বিবেচনা করুন , যেখানে f
উচ্চতর ধরণের ধরণের পরিবর্তনশীল:
class Functor f where
fmap :: (a -> b) -> f a -> f b
কি এই ধরনের স্বাক্ষর বলছেন যে fmap একটি ধরণ প্যারামিটার পরিবর্তন হয় f
থেকে a
থেকে b
, কিন্তু পাতার f
যেমন ছিল। সুতরাং আপনি যদি fmap
কোনও তালিকার উপরে ব্যবহার করেন তবে আপনি একটি তালিকা পান, আপনি যদি এটি কোনও পার্সারের সাহায্যে ব্যবহার করেন তবে আপনি পার্সার পাবেন এবং আরও কিছু। এবং এগুলি স্থির , সংকলন-সময়ের গ্যারান্টি।
আমি এফ # জানি না, তবে আসুন আমরা যদি Functor
জাভা বা সি # এর মতো ভাষাতে উত্তরাধিকার এবং জেনেরিক সহ, তবে উচ্চতর ধরণের জেনেরিকগুলি প্রকাশ করার চেষ্টা না করি তবে কী ঘটে তা বিবেচনা করা যাক । প্রথম চেষ্টা:
interface Functor<A> {
Functor<B> map(Function<A, B> f);
}
এই প্রথম চেষ্টাটির সাথে সমস্যাটি হ'ল ইন্টারফেসের প্রয়োগের ফলে প্রয়োগ করা কোনও শ্রেণি ফেরত দেওয়া যায় Functor
। এমন কেউ লিখতে পারেন FunnyList<A> implements Functor<A>
যার map
পদ্ধতিতে ভিন্ন ধরণের সংগ্রহ পাওয়া যায়, বা এমন কোনও কিছু যা কোনও সংগ্রহ নয় তবে এটি এখনও একটি Functor
। এছাড়াও, আপনি যখন map
পদ্ধতিটি ব্যবহার করেন আপনি ফলাফলের উপর কোনও ধরণের টাইপ-নির্দিষ্ট পদ্ধতিতে আবেদন করতে পারবেন না যদি না আপনি এটি যে ধরণের প্রত্যাশা করছেন তার সাথে এটি বাদ দেন। সুতরাং আমাদের দুটি সমস্যা আছে:
- প্রকারের ব্যবস্থাটি আমাদের আক্রমণকারীটিকে প্রকাশ করতে দেয় না যে
map
পদ্ধতিটি সর্বদা Functor
রিসিভারের মতো একই সাবক্লাসটি দেয় returns
- অতএব,
Functor
ফলাফলের জন্য কোনও অ-পদ্ধতিতে প্রার্থনা করার জন্য কোনও স্ট্যাটিকালি টাইপ-নিরাপদ উপায় নেই map
।
আপনি চেষ্টা করতে পারেন এমন আরও অনেক জটিল পদ্ধতি রয়েছে তবে সেগুলির কোনওটিই বাস্তবে কার্যকর হয় না। উদাহরণস্বরূপ, আপনি Functor
ফলাফলের প্রকারকে সীমাবদ্ধ করে উপ-প্রকারের সংজ্ঞা দিয়ে প্রথম চেষ্টাটিকে বাড়িয়ে তোলার চেষ্টা করতে পারেন :
interface Collection<A> extends Functor<A> {
Collection<B> map(Function<A, B> f);
}
interface List<A> extends Collection<A> {
List<B> map(Function<A, B> f);
}
interface Set<A> extends Collection<A> {
Set<B> map(Function<A, B> f);
}
interface Parser<A> extends Functor<A> {
Parser<B> map(Function<A, B> f);
}
এটি সেই সংকীর্ণ ইন্টারফেসগুলির প্রয়োগকারীদের পদ্ধতি Functor
থেকে ভুল ধরণের প্রত্যাবর্তন করতে নিষেধ করতে সহায়তা করে map
, তবে যেহেতু Functor
আপনার কতগুলি বাস্তবায়ন হতে পারে তার সীমাবদ্ধতা নেই, আপনার কত সংকীর্ণ ইন্টারফেসের প্রয়োজন হবে তার সীমা নেই is
( সম্পাদনা করুন: এবং মনে রাখবেন এই শুধুমাত্র কাজ করে কারণ Functor<B>
প্রদর্শিত হয় ফলাফলের প্রকার, এবং তাই সন্তানের ইন্টারফেসগুলি এটা সংকীর্ণ পারেন তাই আমি যতদূর জানি আমরা উভয় ব্যবহারসমূহ সংকীর্ণ করতে পারবে না। Monad<B>
নিম্নলিখিত ইন্টারফেসে:
interface Monad<A> {
<B> Monad<B> flatMap(Function<? super A, ? extends Monad<? extends B>> f);
}
মধ্যে Haskell, উচ্চ-সারির টাইপ ভেরিয়েবল সঙ্গে, এই হল (>>=) :: Monad m => m a -> (a -> m b) -> m b
।)
তবুও আরেকটি চেষ্টা হ'ল পুনরাবৃত্ত জেনারিকগুলি ব্যবহার করার চেষ্টা করার জন্য এবং ইন্টারফেসটি সাব টাইপের ফলাফলের ধরণটি কেবল সাব টাইপের মধ্যে সীমাবদ্ধ করে রাখে। খেলনা উদাহরণ:
interface Semigroup<T extends Semigroup<T>> {
T append(T arg);
}
class Foo implements Semigroup<Foo> {
Foo append(Foo arg);
}
class Bar implements Semigroup<Bar> {
Semigroup<Bar> append(Semigroup<Bar> arg);
Semigroup<Foo> append(Bar arg);
Semigroup append(Bar arg);
Foo append(Bar arg);
}
তবে এই ধরণের কৌশল (যা আপনার ওপেন-মিল-র ওওপি বিকাশকারীর কাছে বরং তীরযুক্ত, আপনার রান-অফ-দ্য মিল-র কার্যকারিতা বিকাশকারীকেও হ্যাক করুন) এখনও কাঙ্ক্ষিত Functor
বাধা প্রকাশ করতে পারে না :
interface Functor<FA extends Functor<FA, A>, A> {
<FB extends Functor<FB, B>, B> FB map(Function<A, B> f);
}
সমস্যা এখানে এই সীমিত নয় FB
একই আছে F
হিসাবে FA
-so যখন আপনি একটি টাইপ ঘোষণা করছি যে List<A> implements Functor<List<A>, A>
, map
পদ্ধতি করতে এখনো একটি ফিরতি NotAList<B> implements Functor<NotAList<B>, B>
।
জাভাতে কাঁচা ধরণের (অপরিশোধিত পাত্রে) ব্যবহার করে চূড়ান্ত চেষ্টা করুন:
interface FunctorStrategy<F> {
F map(Function f, F arg);
}
এখানে F
কেবল List
বা এর মতো অপরিশোধিত প্রকারভেদে তাত্ক্ষণিকভাবে আসবে Map
। এটি গ্যারান্টি দেয় যে একজন FunctorStrategy<List>
কেবলমাত্র ফিরিয়ে দিতে পারে List
- তবে আপনি তালিকার উপাদানগুলির ধরনগুলি ট্র্যাক করতে টাইপ ভেরিয়েবলের ব্যবহার পরিত্যাগ করেছেন।
এখানে সমস্যার কেন্দ্রস্থল হ'ল জাভা এবং সি # এর মতো ভাষা টাইপ পরামিতিগুলিকে পরামিতি রাখতে দেয় না। জাভা, যদি T
একটি টাইপ পরিবর্তনশীল, আপনি লিখতে পারেন T
এবং List<T>
, কিন্তু না T<String>
। উচ্চ-ধরণের ধরণের ধরণগুলি এই বিধিনিষেধটি সরিয়ে দেয়, যাতে আপনার এমন কিছু থাকতে পারে (পুরোপুরি ভাবেননি):
interface Functor<F, A> {
<B> F<B> map(Function<A, B> f);
}
class List<A> implements Functor<List, A> {
<B> List<B> map(Function<A, B> f) {
}
}
এবং এই বিট বিশেষভাবে সম্বোধন:
(আমি মনে করি) আমি পেয়েছি যে পরিবর্তে myList |> List.map f
বা myList |> Seq.map f |> Seq.toList
উচ্চতর ধরণের আপনাকে কেবল লেখার অনুমতি দেয় myList |> map f
এবং এটি একটি ফিরে আসবে List
। এটি দুর্দান্ত (এটি সঠিক বলে ধরে নিচ্ছেন), তবে কি একরকম ছোটখাটো বলে মনে হচ্ছে? (এবং কেবল ফাংশন ওভারলোডিংকে অনুমতি দিয়ে এটি করা যায়নি?) আমি সাধারণত Seq
যেভাবেই রূপান্তর করি এবং তারপরে আমি যা চাই তা রূপান্তর করতে পারি।
এমন অনেকগুলি ভাষা রয়েছে যা এইভাবে map
ফাংশনটির ধারণাটিকে সাধারণীকরণ করে , একে মডেলিং করে যেমন হৃদয়ে, ম্যাপিংটি সিকোয়েন্সগুলি সম্পর্কে। পুলিশের এই মন্তব্য যে আত্মা রয়েছে: যদি আপনি একটি যে ধরনের এবং থেকে সমর্থন রূপান্তর আছে Seq
, আপনি পুনঃব্যবহার দ্বারা "বিনামূল্যে জন্য" মানচিত্র অপারেশন পেতে Seq.map
।
হাসকেলে অবশ্য Functor
ক্লাসটি তার চেয়ে বেশি সাধারণ; এটি অনুক্রমের ধারণার সাথে আবদ্ধ নয়। fmap
ক্রম IO
, পার্সার সংযুক্তকারী, ফাংশন ইত্যাদির মতো সিকোয়েন্সগুলিতে ভাল ম্যাপিং নেই এমন প্রকারের জন্য আপনি প্রয়োগ করতে পারেন . :
instance Functor IO where
fmap f action =
do x <- action
return (f x)
newtype Function a b = Function (a -> b)
instance Functor (Function a) where
fmap f (Function g) = Function (f . g)
"ম্যাপিং" ধারণাটি আসলেই অনুক্রমের সাথে আবদ্ধ নয়। ফান্টারের আইনগুলি বোঝা ভাল:
(1) fmap id xs == xs
(2) fmap f (fmap g xs) = fmap (f . g) xs
খুব অনানুষ্ঠানিকভাবে:
- প্রথম আইনটি বলে যে কোনও পরিচয় / নূপুর ফাংশন সহ ম্যাপিং কিছুই না করার মতো।
- দ্বিতীয় আইন বলছে যে আপনি দুবার ম্যাপিংয়ের মাধ্যমে যে কোনও ফলাফল তৈরি করতে পারেন, আপনি একবার ম্যাপিংয়ের মাধ্যমেও উত্পাদন করতে পারেন।
এ কারণেই আপনি fmap
টাইপটি সংরক্ষণ করতে চান — কারণ আপনি যেমন map
কোনও ভিন্ন ফলাফলের প্রকার উত্পাদন করে এমন অপারেশনগুলি পাওয়ার সাথে সাথে এটির গ্যারান্টি দেওয়া অনেক বেশি শক্ত হয়ে যায়।