@ কার্লবিলিফেল্টের উত্তরে সম্প্রসারণের জন্য, এখানে ভেক্টরগুলি কীভাবে প্রয়োগ করা যায় তার একটি সম্পূর্ণ উদাহরণ - হাসকেলে স্ট্যাটিক্যালি পরিচিত সংখ্যার তালিকা রয়েছে। আপনার টুপি ধরুন ...
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
import Prelude hiding (foldr, zipWith)
import qualified Prelude
import Data.Type.Equality
import Data.Foldable
import Data.Traversable
আপনি LANGUAGE
নির্দেশের দীর্ঘ তালিকা থেকে দেখতে পাচ্ছেন , এটি কেবলমাত্র GHC এর সাম্প্রতিক সংস্করণ নিয়ে কাজ করবে।
আমাদের টাইপ সিস্টেমের মধ্যে দৈর্ঘ্যের প্রতিনিধিত্ব করার একটি উপায় প্রয়োজন। সংজ্ঞা অনুসারে, একটি প্রাকৃতিক সংখ্যা হয় শূন্য ( Z
) বা এটি অন্য কোনও প্রাকৃতিক সংখ্যার ( S n
) এর উত্তরসূরি । সুতরাং, উদাহরণস্বরূপ, 3 নম্বর লেখা হবে S (S (S Z))
।
data Nat = Z | S Nat
সঙ্গে DataKinds এক্সটেনশন , এই data
ঘোষণা একটি প্রবর্তন ধরনের বলা Nat
এবং দুই প্রকার কনস্ট্রাকটর নামক S
এবং Z
- অন্য কথায় আমরা আছে টাইপ পর্যায়ের স্বাভাবিক সংখ্যার। নোট করুন যে প্রকারগুলি S
এবং Z
কোনও সদস্যের মান নেই - কেবল প্রকারের ধরণের *
মানগুলি বাস করে।
এখন আমরা একটি GADT পরিচিত দৈর্ঘ্য সহ ভেক্টর প্রতিনিধিত্ব করে। উল্লেখ্য ধরনের স্বাক্ষর: Vec
একটি টাইপ প্রয়োজন ধরনেরNat
(যেমন একটি Z
অথবা একটি S
টাইপ) তার দৈর্ঘ্য প্রতিনিধিত্ব করতে।
data Vec :: Nat -> * -> * where
VNil :: Vec Z a
VCons :: a -> Vec n a -> Vec (S n) a
deriving instance (Show a) => Show (Vec n a)
deriving instance Functor (Vec n)
deriving instance Foldable (Vec n)
deriving instance Traversable (Vec n)
ভেক্টরগুলির সংজ্ঞা সংযুক্ত তালিকার সাথে মিল রয়েছে, এর দৈর্ঘ্য সম্পর্কে কিছু অতিরিক্ত টাইপ-স্তরের তথ্য রয়েছে। কোনও ভেক্টর হয় হয় VNil
, এর ক্ষেত্রে এর দৈর্ঘ্য Z
(এরো) হয়, বা এটি এমন একটি VCons
ঘর যা অন্য ভেক্টরের সাথে একটি আইটেম যুক্ত করে, এক্ষেত্রে এর দৈর্ঘ্য অন্যান্য ভেক্টর ( S n
) এর চেয়ে আরও একটি বেশি । মনে রাখবেন যে কোনও ধরণের নির্মাণকারীর যুক্তি নেই n
। দৈর্ঘ্যগুলি ট্র্যাক করার জন্য এটি কেবল সংকলনের সময়ে ব্যবহৃত হয়েছিল এবং সংকলক মেশিন কোড উত্পন্ন করার আগে মুছে ফেলা হবে।
আমরা একটি ভেক্টর টাইপ সংজ্ঞায়িত করেছি যা এর দৈর্ঘ্যের স্থির জ্ঞানের চারপাশে বহন করে। Vec
তারা কীভাবে কাজ করে তার জন্য অনুভূতি পেতে কয়েকজনের প্রকারের জিজ্ঞাসা করা যাক :
ghci> :t (VCons 'a' (VCons 'b' VNil))
(VCons 'a' (VCons 'b' VNil)) :: Vec ('S ('S 'Z)) Char -- (S (S Z)) means 2
ghci> :t (VCons 13 (VCons 11 (VCons 3 VNil)))
(VCons 13 (VCons 11 (VCons 3 VNil))) :: Num a => Vec ('S ('S ('S 'Z))) a -- (S (S (S Z))) means 3
বিন্দুর পণ্যটি কেবল তালিকার জন্য যেমন এগিয়ে যায়:
-- note that the two Vec arguments are declared to have the same length
vap :: Vec n (a -> b) -> Vec n a -> Vec n b
vap VNil VNil = VNil
vap (VCons f fs) (VCons x xs) = VCons (f x) (vap fs xs)
zipWith :: (a -> b -> c) -> Vec n a -> Vec n b -> Vec n c
zipWith f xs ys = fmap f xs `vap` ys
dot :: Num a => Vec n a -> Vec n a -> a
dot xs ys = foldr (+) 0 $ zipWith (*) xs ys
vap
, যা 'জিপ্লি' আর্গুমেন্টের ভেক্টরকে ফাংশনগুলির একটি ভেক্টর প্রয়োগ করে, এটি Vec
প্রয়োগকারী <*>
; আমি এটি একটি রাখা হয়নিApplicative
নজরে রাখিনি কারণ এটি অগোছালো হয়ে যায় । এছাড়াও নোট করুন যে আমি foldr
সংকলক-উত্পন্ন উদাহরণ থেকে ব্যবহার করছি Foldable
।
আসুন এটি ব্যবহার করে দেখুন:
ghci> let v1 = VCons 2 (VCons 1 VNil)
ghci> let v2 = VCons 4 (VCons 5 VNil)
ghci> v1 `dot` v2
13
ghci> let v3 = VCons 8 (VCons 6 (VCons 1 VNil))
ghci> v1 `dot` v3
<interactive>:20:10:
Couldn't match type ‘'S 'Z’ with ‘'Z’
Expected type: Vec ('S ('S 'Z)) a
Actual type: Vec ('S ('S ('S 'Z))) a
In the second argument of ‘dot’, namely ‘v3’
In the expression: v1 `dot` v3
গ্রেট! আপনি যখন dot
ভেক্টরগুলির দৈর্ঘ্য মেলে না তার চেষ্টা করার সময় আপনি একটি সংকলন-সময় ত্রুটি পান ।
ভেক্টরগুলিকে একসাথে জড়িত করার জন্য একটি অনুষ্ঠানে এই চেষ্টা করা হয়েছে:
-- This won't compile because the type checker can't deduce the length of the returned vector
-- VNil +++ ys = ys
-- (VCons x xs) +++ ys = VCons x (concat xs ys)
আউটপুট ভেক্টরের দৈর্ঘ্য দুটি ইনপুট ভেক্টরের দৈর্ঘ্যের যোগফল হবে । আমাদের কীভাবে Nat
একসাথে যুক্ত করতে হয় তা টাইপ পরীক্ষককে শেখাতে হবে । এর জন্য আমরা একটি টাইপ-স্তরের ফাংশন ব্যবহার করি :
type family (n :: Nat) :+: (m :: Nat) :: Nat where
Z :+: m = m
(S n) :+: m = S (n :+: m)
এই type family
ঘোষণাটি প্রকারভেদে ডাকে একটি ফাংশন প্রবর্তন করে:+:
- অন্য কথায়, এটি টাইপ চেকারের পক্ষে দুটি প্রাকৃতিক সংখ্যার যোগফল গণনা করার জন্য একটি রেসিপি। এটি পুনরাবৃত্তভাবে সংজ্ঞায়িত করা হয় - যখনই বাম অপারেণ্ড Z
এরোর চেয়ে বড় হয় আমরা আউটপুটে একটি যুক্ত করি এবং এটি পুনরাবৃত্তির কলগুলিতে একে একে হ্রাস করি। (টাইপ ফাংশনটি লিখতে এটি একটি ভাল অনুশীলন যা দুটি Nat
গুলিকে বহুগুণ করে )) এখন আমরা +++
সংকলন করতে পারি :
infixr 5 +++
(+++) :: Vec n a -> Vec m a -> Vec (n :+: m) a
VNil +++ ys = ys
(VCons x xs) +++ ys = VCons x (concat xs ys)
আপনি এটি কীভাবে ব্যবহার করবেন তা এখানে:
ghci> VCons 1 (VCons 2 VNil) +++ VCons 3 (VCons 4 VNil)
VCons 1 (VCons 2 (VCons 3 (VCons 4 VNil)))
এখন পর্যন্ত এত সহজ। আমরা যখন যুক্তিযুক্তির বিপরীত কাজ করতে এবং একটি ভেক্টরকে দুটিতে বিভক্ত করতে চাই তখন কী হবে? আউটপুট ভেক্টরগুলির দৈর্ঘ্য আর্গুমেন্টের রানটাইম মানের উপর নির্ভর করে। আমরা এরকম কিছু লিখতে চাই:
-- this won't work because there aren't any values of type `S` and `Z`
-- split :: (n :: Nat) -> Vec (n :+: m) a -> (Vec n a, Vec m a)
কিন্তু দুর্ভাগ্যক্রমে হাস্কেল আমাদের তা করতে দেয় না। অনুমতি মান এর n
যুক্তি প্রদর্শিত করতে রিটার্ন টাইপ (এই সাধারণত একটি বলা হয় নির্ভরশীল ফাংশন বা Pi টাইপ ,) "পুরো বর্ণালী" নির্ভরশীল ধরনের প্রয়োজন হবে যেহেতু DataKinds
আমাদের টাইপ কনস্ট্রাকটর উন্নীত দেয়। এটি অন্য উপায়ে রাখতে, টাইপ কনস্ট্রাক্টর S
এবং Z
মান স্তরে উপস্থিত হয় না। একটি নির্দিষ্ট সময়ের জন্য রান-টাইম উপস্থাপনের জন্য আমাদের সিঙ্গলটন মানগুলির নিষ্পত্তি করতে হবে Nat
*
data Natty (n :: Nat) where
Zy :: Natty Z -- pronounced 'zed-y'
Sy :: Natty n -> Natty (S n) -- pronounced 'ess-y'
deriving instance Show (Natty n)
প্রদত্ত প্রকারের জন্য n
(ধরণের সহ Nat
), ধরণের এক ধরণের অবিকল থাকে Natty n
। আমরা সিঙ্গলটন মানটি রান-টাইম সাক্ষী হিসাবে ব্যবহার করতে পারি n
: একটি সম্পর্কে শিখতে Natty
আমাদের এর n
বিপরীতটি শিখায় ।
split :: Natty n ->
Vec (n :+: m) a -> -- the input Vec has to be at least as long as the input Natty
(Vec n a, Vec m a)
split Zy xs = (Nil, xs)
split (Sy n) (Cons x xs) = let (ys, zs) = split n xs
in (Cons x ys, zs)
এটি একটি স্পিন জন্য নেওয়া যাক:
ghci> split (Sy (Sy Zy)) (VCons 1 (VCons 2 (VCons 3 VNil)))
(VCons 1 (VCons 2 VNil), VCons 3 VNil)
ghci> split (Sy (Sy Zy)) (VCons 3 VNil)
<interactive>:116:21:
Couldn't match type ‘'S ('Z :+: m)’ with ‘'Z’
Expected type: Vec ('S ('S 'Z) :+: m) a
Actual type: Vec ('S 'Z) a
Relevant bindings include
it :: (Vec ('S ('S 'Z)) a, Vec m a) (bound at <interactive>:116:1)
In the second argument of ‘split’, namely ‘(VCons 3 VNil)’
In the expression: split (Sy (Sy Zy)) (VCons 3 VNil)
প্রথম উদাহরণে, আমরা সফলভাবে একটি ত্রি-উপাদান ভেক্টর 2 পজিশনে বিভক্ত করেছি; তারপরে আমরা শেষের অবস্থানে কোনও ভেক্টরকে বিভক্ত করার চেষ্টা করার সময় আমরা একটি টাইপ ত্রুটি পেয়েছি। সিঙ্গলেটগুলি হ্যাস্কেলের কোনও মানের উপর নির্ভর করে একটি প্রকার তৈরির জন্য মানক কৌশল।
* singletons
লাইব্রেরিতেNatty
আপনার মতো সিঙ্গলটন মান উত্পন্ন করতে কিছু টেম্পলেট হাস্কেল সহায়ক রয়েছে ।
শেষ উদাহরণ। আপনি যখন আপনার ভেক্টরের মাত্রাটি স্থিরভাবে জানেন না তখন কী হবে? উদাহরণস্বরূপ, যদি আমরা একটি তালিকা আকারে রান-টাইম ডেটা থেকে একটি ভেক্টর তৈরি করার চেষ্টা করছি? ইনপুট তালিকার দৈর্ঘ্যের উপর নির্ভর করতে আপনার ভেক্টরের ধরণের প্রয়োজন । এটি অন্য উপায়ে বলতে গেলে , আমরা কোনও ভেক্টর তৈরি করতে ব্যবহার করতে পারি না কারণ আউটপুট ভেক্টরের ধরণটি ভাঁজের প্রতিটি পুনরাবৃত্তির সাথে পরিবর্তিত হয়। সংকলক থেকে আমাদের ভেক্টরের দৈর্ঘ্য একটি গোপন রাখতে হবে।foldr VCons VNil
data AVec a = forall n. AVec (Natty n) (Vec n a)
deriving instance (Show a) => Show (AVec a)
fromList :: [a] -> AVec a
fromList = Prelude.foldr cons nil
where cons x (AVec n xs) = AVec (Sy n) (VCons x xs)
nil = AVec Zy VNil
AVec
একটি অস্তিত্বমূলক প্রকার : টাইপ ভেরিয়েবল ডেটা কনস্ট্রাক্টরের n
রিটার্ন টাইপের ক্ষেত্রে উপস্থিত হয় না AVec
। আমরা এটি নির্ভরশীল জুটির অনুকরণের জন্য ব্যবহার করছি : fromList
আপনাকে ভেক্টরের দৈর্ঘ্য স্থিতিশীলভাবে বলতে পারি না, তবে এটি ভ্যাক্টরের দৈর্ঘ্য শিখতে প্যাটার্ন-ম্যাচ করে এমন কিছু ফিরিয়ে দিতে পারে - Natty n
টিউপলের প্রথম উপাদানটিতে । যেমন কনর ম্যাকব্রাইড এটিকে সম্পর্কিত উত্তরে রাখে , "আপনি এক জিনিস তাকান, এবং তাই করছেন, অন্য বিষয়ে জানার"।
এটি অস্তিত্বযুক্ত পরিমাণযুক্ত প্রকারের জন্য একটি সাধারণ কৌশল। যেহেতু আপনি এমন ডেটা দিয়ে আসলে কিছুই করতে পারবেন না যার জন্য আপনি টাইপটি জানেন না - একটি ফাংশন লেখার চেষ্টা করুন data Something = forall a. Sth a
- অস্তিত্বগুলি প্রায়শই জিএডিডি প্রমাণ সহ বান্ডিল হয়ে আসে যা আপনাকে প্যাটার্ন-ম্যাচিং টেস্টগুলি সম্পাদন করে মূল ধরণের পুনরুদ্ধার করতে দেয়। অস্তিত্বের জন্য অন্যান্য সাধারণ নিদর্শনগুলির মধ্যে রয়েছে আপনার প্রকার ( data AWayToGetTo b = forall a. HeresHow a (a -> b)
) প্রসেস করার জন্য প্যাকেজিং ফাংশনগুলি অন্তর্ভুক্ত যা প্রথম শ্রেণীর মডিউলগুলি করার একটি ঝরঝরে উপায়, বা বিল্ড-ইন একটি ধরণের শ্রেণীর অভিধান ( data AnOrd = forall a. Ord a => AnOrd a
) যা উপ -টাইপ পলিমারফিজম অনুকরণ করতে সহায়তা করতে পারে।
ghci> fromList [1,2,3]
AVec (Sy (Sy (Sy Zy))) (VCons 1 (VCons 2 (VCons 3 Nil)))
নির্ভরশীল জুড়ি দরকারী যখনই তথ্য স্থির বৈশিষ্ট্য গতিশীল তথ্যের উপর নির্ভর করে সংকলন সময়ে উপলব্ধ না। এখানে filter
ভেক্টরদের জন্য:
filter :: (a -> Bool) -> Vec n a -> AVec a
filter f = foldr (\x (AVec n xs) -> if f x
then AVec (Sy n) (VCons x xs)
else AVec n xs) (AVec Zy VNil)
করতে dot
দুই AVec
গুলি, আমরা GHC প্রমাণ করতে যে তাদের লেন্থ সমান প্রয়োজন। Data.Type.Equality
একটি GADT সংজ্ঞা দেয় যা কেবল তখনই তৈরি করা যায় যখন এর ধরণের আর্গুমেন্টগুলি একই হয়:
data (a :: k) :~: (b :: k) where
Refl :: a :~: a -- short for 'reflexivity'
আপনি যখন প্যাটার্ন-ম্যাচ চালু করেন Refl
, জিএইচসি তা জানে a ~ b
। আপনাকে এই ধরণের সাথে কাজ করতে সহায়তা করার জন্য কয়েকটি ফাংশন রয়েছে: আমরা gcastWith
সমতুল্য ধরণের মধ্যে রূপান্তর TestEquality
করতে এবং দুটি Natty
সমান কিনা তা নির্ধারণ করার জন্য ব্যবহার করব ।
দুই সমতা পরীক্ষা করার জন্য Natty
গুলি, আমরা সত্য যে যদি দুটি সংখ্যার সমান হয়, তখন তাদের উত্তরাধিকারী এছাড়াও সমান হয় করতে ব্যবহার করতে হবে যাচ্ছেন ( :~:
হয় সর্বসম উপর S
):
congSuc :: (n :~: m) -> (S n :~: S m)
congSuc Refl = Refl
Refl
বাম-পাশের প্যাটার্নের মিলটি জিএইচসিকে এটি জানতে দেয় n ~ m
। সেই জ্ঞানের সাথে, এটি তুচ্ছ S n ~ S m
, সুতরাং জিএইচসি আমাদের একটি নতুন ফিরিয়ে দিতে দেয়Refl
ফিরিয়ে দিতে দেয়।
এখন আমরা TestEquality
প্রত্যক্ষ পুনরাবৃত্তি দ্বারা একটি উদাহরণ লিখতে পারেন । উভয় সংখ্যা শূন্য হলে তারা সমান। যদি উভয় সংখ্যার পূর্বসূরি থাকে তবে পূর্বসূরীরা সমান হলে তারা সমান। (যদি তারা সমান না হয় তবে কেবল ফিরে আসুন Nothing
))
instance TestEquality Natty where
-- testEquality :: Natty n -> Natty m -> Maybe (n :~: m)
testEquality Zy Zy = Just Refl
testEquality (Sy n) (Sy m) = fmap congSuc (testEquality n m) -- check whether the predecessors are equal, then make use of congruence
testEquality Zy _ = Nothing
testEquality _ Zy = Nothing
এখন আমরা টুকরাগুলি dot
এক জোড়া AVec
অজানা দৈর্ঘ্যের এক সাথে রাখতে পারি।
dot' :: Num a => AVec a -> AVec a -> Maybe a
dot' (AVec n u) (AVec m v) = fmap (\proof -> gcastWith proof (dot u v)) (testEquality n m)
প্রথমে AVec
ভেক্টরগুলির দৈর্ঘ্যের একটি রানটাইম উপস্থাপনাটি বের করতে কনস্ট্রাক্টরের প্যাটার্ন ম্যাচ । এখন testEquality
সেই দৈর্ঘ্য সমান কিনা তা নির্ধারণ করতে ব্যবহার করুন । তারা যদি হয় তবে আমাদের থাকবে Just Refl
; তার সামঞ্জস্য অনুমানটি স্রাব করে ভাল-টাইপ করা gcastWith
হয়েছে তা নিশ্চিত করার জন্য সেই সাম্যতার প্রমাণ ব্যবহার করবে ।dot u v
n ~ m
ghci> let v1 = fromList [1,2,3]
ghci> let v2 = fromList [4,5,6]
ghci> let v3 = fromList [7,8]
ghci> dot' v1 v2
Just 32
ghci> dot' v1 v3
Nothing -- they weren't the same length
দ্রষ্টব্য, যেহেতু কোনও দৈর্ঘ্যের স্থির জ্ঞান ছাড়াই ভেক্টর মূলত একটি তালিকা, তাই আমরা কার্যকরভাবে এর তালিকা সংস্করণটি পুনরায় প্রয়োগ করেছি dot :: Num a => [a] -> [a] -> Maybe a
। পার্থক্যটি হ'ল এই সংস্করণটি ভেক্টরদের ক্ষেত্রে প্রয়োগ করা হয়েছে dot
। এখানে বিন্দু: সামনে টাইপ পরীক্ষক আপনাকে কল করার অনুমতি দেবে dot
, তোমাদের যাচাই করেছি আবশ্যক কিনা ইনপুট তালিকা একই দৈর্ঘ্য ব্যবহার আছে testEquality
। আমি if
স্টেস্টেমগুলি ভুল উপায়ে পেয়ে যাওয়ার ঝুঁকিপূর্ণ , কিন্তু নির্ভরশীল টাইপযুক্ত সেটিংয়ে নেই!
আপনি যখন রানটাইম ডেটা নিয়ে কাজ করছেন তখন আপনি আপনার সিস্টেমে প্রান্তে অস্তিত্বের মোড়ক ব্যবহার এড়াতে পারবেন না তবে আপনি যখন ইনপুট বৈধতাটি সম্পাদন করেন তখন আপনি আপনার সিস্টেমের অভ্যন্তরে সর্বত্র নির্ভরশীল প্রকারগুলি ব্যবহার করতে পারেন এবং অস্তিত্বের মোড়কে রাখতে পারেন।
যেহেতু Nothing
খুব তথ্যবহুল নয়, আপনি ব্যর্থতার ক্ষেত্রে দৈর্ঘ্য সমান নয় (প্রমাণ আকারে তাদের পার্থক্য 0 নয়) প্রমাণটিdot'
ফেরতের প্রকারটি আরও সংশোধন করতে পারেন । এটি সম্ভবত কোনও ত্রুটি বার্তা ফেরত দেওয়ার জন্য স্ট্যান্ডার্ড হাস্কেল প্রযুক্তির সাথে একই রকম , যদিও একটি স্ট্রিংয়ের চেয়ে প্রুফ টার্মটি আরও বেশি গণনামূলকভাবে কার্যকর!Either String a
নির্ভরশীল-টাইপড হাস্কেল প্রোগ্রামিংয়ে প্রচলিত কিছু কৌশলগুলির এই হুইসল-স্টপ ট্যুরটি শেষ হয়। হাস্কেলের মধ্যে এই জাতীয় প্রকারের সাথে প্রোগ্রামিং করা সত্যিই দুর্দান্ত তবে একই সময়ে সত্যই বিশ্রী। - উপস্থাপনা যা একই জিনিস মানে প্রচুর মধ্যে আপনার সমস্ত নির্ভরশীল ডেটা ব্রেকিং Nat
ধরন, Nat
ধরনের, Natty n
Singleton - boilerplate, সাহায্য করার জন্য কোড-জেনারেটর অস্তিত্ব সত্ত্বেও, বেশ কষ্টকর সত্যিই হয়। প্রকার স্তরে কী প্রচার করা যায় সে সম্পর্কে বর্তমানে সীমাবদ্ধতা রয়েছে। এটা যদিও tantalizing! সম্ভাবনাগুলিতে মন বিচলিত হয় - সাহিত্যে দৃ strongly়ভাবে টাইপ করা printf
, ডাটাবেস ইন্টারফেস, ইউআই লেআউট ইঞ্জিনগুলির হ্যাস্কেলের উদাহরণ রয়েছে ...
আপনি যদি আরও কিছু পড়াতে চান তবে নির্ভরশীলভাবে টাইপ করা হাস্কেল সম্পর্কে প্রকাশিত এবং স্ট্যাক ওভারফ্লোয়ের মতো সাইটে উভয়ই সাহিত্যের একটি বর্ধমান বর্ধন রয়েছে। একটি ভাল শুরু হয় Hasochism কাগজ - কাগজ (অন্যদের মধ্যে) এই খুব উদাহরণ মাধ্যমে যায়, কিছু বিস্তারিতভাবে বেদনাদায়ক অংশের আলোচনাতে রয়েছে। Singletons কাগজ (যেমন Singleton মূল্যবোধের কৌশল প্রমান )। সাধারণভাবে নির্ভর টাইপিং সম্পর্কে আরও তথ্যের জন্য, আগদা টিউটোরিয়ালটি শুরু করার জন্য একটি ভাল জায়গা; এছাড়াও, ইদ্রিস হ'ল উন্নয়নের একটি ভাষা যা "প্রায়শই হ'ল" নির্ভরশীল ধরণের সাথে হাস্কেল "হিসাবে নকশাকৃত।Natty