इंटरफ़ेस
सबसे पहले, आप से पूछना होगा "मेरी आवश्यकताएं क्या हैं?"। सादे अंग्रेजी में चलो राज्य हम एक कैनवास क्या करना चाहते हैं क्या (ये मेरे अनुमान आपके प्रश्न पर आधारित हैं):
- कुछ कैनवस उन पर डाल आकार
- कुछ कैनवस पाठ उन पर
डाल दिया है सकते हैं हो सकता है
- कुछ कैनवस बदल क्या वे एक पेंट
- हम पेंट अभी तक क्या कर रहे हैं पता नहीं है के आधार पर करते हैं, लेकिन वे अब हम हास्केल में इन विचारों का अनुवाद विभिन्न कैनवस
के लिए अलग होगा। हास्केल एक "प्रकार-प्रथम" भाषा है, इसलिए जब हम आवश्यकताओं और डिजाइन के बारे में बात कर रहे हैं, तो हम शायद प्रकारों के बारे में बात कर रहे हैं।
- हास्केल में, जब हम प्रकारों के बारे में बात करते समय "कुछ" शब्द देखते हैं, तो हम प्रकार के वर्गों के बारे में सोचते हैं। उदाहरण के लिए,
show
वर्ग कहते हैं, "कुछ प्रकार के तार के रूप में प्रतिनिधित्व किया जा सकता है"।
- जब हम किसी चीज़ के बारे में बात करते हैं जिसे हम अभी तक नहीं जानते हैं, आवश्यकताओं के बारे में बात करते समय, यह एक प्रकार है जहां हम नहीं जानते कि यह अभी तक क्या है। यह एक प्रकार चर है।
- "उन्हें डालें" ऐसा लगता है कि हम एक कैनवास लेते हैं, उस पर कुछ डालते हैं, और फिर एक कैनवास लेते हैं।
अब हम इन आवश्यकताओं में से प्रत्येक के लिए कक्षाओं लिख सकते हैं:
class ShapeCanvas c where -- c is the type of the Canvas
draw :: Shape -> c -> c
class TextCanvas c where
write :: Text -> c -> c
class PaintCanvas p c where -- p is the type of Paint
load :: p -> c -> c
प्रकार चर c
केवल एक बार प्रयोग किया जाता है, c -> c
के रूप में प्रदर्शित।इससे पता चलता है कि हम को c
के साथ बदलकर इन अधिक सामान्य बना सकते हैं।
class ShapeCanvas c where -- c is the type of the canvas
draw :: Shape -> c
class TextCanvas c where
write :: Text -> c
class PaintCanvas p c where -- p is the type of paint
load :: p -> c
अब PaintCanvas
एक class
कि हास्केल में समस्याग्रस्त है जैसा दिखता है। यह के लिए प्रकार प्रणाली यह पता लगाने की क्या की तरह
class Implicitly a b where
convert :: b -> a
कक्षाओं में हो रहा है मैं PaintCanvas
बदलते TypeFamilies
विस्तार का लाभ लेने के द्वारा इस को कम करता हूँ मुश्किल है।
class PaintCanvas c where
type Paint c :: * -- (Paint c) is the type of Paint for canvases of type c
load :: (Paint c) -> c
अब, चलो हमारे इंटरफेस के लिए सब कुछ एक साथ रखा, आकार और पाठ के लिए अपने डेटा प्रकार सहित जाने (मेरे लिए समझ बनाने के लिए संशोधित):
{-# LANGUAGE TypeFamilies #-}
module Data.Canvas (
Point(..),
Shape(..),
Text(..),
ShapeCanvas(..),
TextCanvas(..),
PaintCanvas(..)
) where
data Point = Point Int Int
data Shape = Dot Point
| Box Point Point
| Path [Point]
data Text = Text Point String
class ShapeCanvas c where -- c is the type of the Canvas
draw :: Shape -> c
class TextCanvas c where
write :: Text -> c
class PaintCanvas c where
type Paint c :: * -- (Paint c) is the type of Paint for canvases of type c
load :: (Paint c) -> c
कुछ उदाहरण
यह अनुभाग उपयोगी कैनवास के लिए अतिरिक्त आवश्यकताएं पेश करेगा, इसके अलावा हमने पहले से ही काम किया है। कैनवास कक्षाओं में c
के साथ c -> c
को प्रतिस्थापित करते समय हमने जो खो दिया, उसका यह एनालॉग है।
चलो अपने पहले उदाहरण कोड, op
से शुरू करते हैं। हमारे नए इंटरफ़ेस के साथ यह बस है:
op :: (TextCanvas c) => c
op = write $ Text (Point 30 30) "Hi"
चलिए थोड़ा और जटिल उदाहरण बनाते हैं। एक "एक्स" खींचने वाली चीज़ के बारे में कैसे? हम "एक्स"
ex :: (ShapeCanvas c) => c
ex = draw $ Path [Point 10 10, Point 20 20]
के पहले स्ट्रोक कर सकते हैं लेकिन हम कोई रास्ता नहीं पार स्ट्रोक के लिए एक और Path
जोड़ने के लिए। हमें दो ड्राइंग चरणों को एक साथ रखने के लिए कुछ रास्ता चाहिए। टाइप c -> c -> c
के साथ कुछ सही होगा। सबसे आसान हास्केल क्लास मैं सोच सकता हूं कि यह Monoid a
के mappend :: a -> a -> a
प्रदान करता है। Monoid
को एक पहचान और सहयोगीता की आवश्यकता है। क्या यह मानने योग्य है कि कैनवस पर एक ड्राइंग ऑपरेशन है जो उन्हें छूता है? यह काफी उचित लगता है। क्या यह मानना उचित है कि एक ही क्रम में किए गए तीन ड्राइंग ऑपरेशन, वही काम करते हैं, भले ही पहले दो एक साथ किए जाते हैं, और फिर तीसरा, या यदि पहला प्रदर्शन किया जाता है, और फिर दूसरा और तीसरा एक साथ किया जाता है ? फिर, यह मेरे लिए काफी उचित लगता है। यह सुझाव हम ex
के रूप में लिख सकते हैं:
randomDrawing :: (MonadIO m, ShapeCanvas (m()), TextCanvas (m())) => m()
randomDrawing = do
index <- liftIO . getStdRandom $ randomR (0,2)
choices !! index
where choices = [op, ex, return()]
यह काफी काम है, क्योंकि हम डॉन नहीं है ':
ex :: (Monoid c, ShapeCanvas c) => c
ex = (draw $ Path [Point 10 10, Point 20 20]) `mappend` (draw $ Path [Point 10 20, Point 20 10])
अंत में, चलो कुछ इंटरैक्टिव, कि कुछ बाहरी के आधार पर आकर्षित करने के लिए क्या निर्णय लेता है पर विचार करते हैं टी (Monad m) => Monoid (m())
के लिए एक उदाहरण है ताकि ex
काम करेगा। हम reducers पैकेज से Data.Semigroup.Monad
का उपयोग कर सकते हैं, या खुद को जोड़ सकते हैं, लेकिन यह हमें अंतर्निहित उदाहरण भूमि में डाल देता है। यह करने के लिए पूर्व बदलने के लिए आसान हो जाएगा:
ex :: (Monad m, ShapeCanvas (m())) => m()
ex = do
draw $ Path [Point 10 10, Point 20 20]
draw $ Path [Point 10 20, Point 20 10]
लेकिन प्रकार प्रणाली काफी को समझ नहीं सकता है कि पहली draw
से इकाई दूसरे से इकाई के रूप में एक ही है।हमारी कठिनाई यहां अतिरिक्त आवश्यकताओं को पता चलता है, कि हम नहीं काफी में पहली पर हमारे उंगली डाल सकता है:
- कैनवस मौजूदा आपरेशन के दृश्यों का विस्तार, आदि ड्राइंग के लिए आपरेशन प्रदान करने पाठ लिखने,
चोरी सीधे http://www.haskellforall.com/2013/06/from-zero-to-cooperative-threads-in-33.html:
- जब आप "निर्देशों का अनुक्रम" सुनते हैं तो आपको यह सोचना चाहिए: "monad"।
- जब आप "विस्तार" के साथ अर्हता प्राप्त करते हैं तो आपको यह सोचना चाहिए: "मोनड ट्रांसफार्मर"।
अब हम महसूस करते हैं कि हमारे कैनवास कार्यान्वयन संभवतः एक मोनाड ट्रांसफॉर्मर होने जा रहा है। हम अपने इंटरफेस पर वापस जा सकते हैं, और इसे बदल सकते हैं ताकि प्रत्येक वर्ग एक मोनैड के लिए एक वर्ग हो, ट्रांसफार्मर के MonadIO
वर्ग और एमटीएल के मोनैड वर्गों के समान।
इंटरफेस,
{-# LANGUAGE TypeFamilies #-}
module Data.Canvas (
Point(..),
Shape(..),
Text(..),
ShapeCanvas(..),
TextCanvas(..),
PaintCanvas(..)
) where
data Point = Point Int Int
data Shape = Dot Point
| Box Point Point
| Path [Point]
data Text = Text Point String
class Monad m => ShapeCanvas m where -- c is the type of the Canvas
draw :: Shape -> m()
class Monad m => TextCanvas m where
write :: Text -> m()
class Monad m => PaintCanvas m where
type Paint m :: * -- (Paint c) is the type of Paint for canvases of type c
load :: (Paint m) -> m()
उदाहरण पर दोबारा गौर किया,
अब हमारे उदाहरण के सभी ड्राइंग संचालन किसी अज्ञात Monad
मीटर में कार्रवाई कर रहे हैं पर दोबारा गौर किया:
op :: (TextCanvas m) => m()
op = write $ Text (Point 30 30) "Hi"
ex :: (ShapeCanvas m) => m()
ex = do
draw $ Path [Point 10 10, Point 20 20]
draw $ Path [Point 10 20, Point 20 10]
randomDrawing :: (MonadIO m, ShapeCanvas m, TextCanvas m) => m()
randomDrawing = do
index <- liftIO . getStdRandom $ randomR (0,2)
choices !! index
where choices = [op, ex, return()]
हम कर सकते हैं पेंट का उपयोग करके एक उदाहरण भी बनाते हैं। जब से हम पेंट क्या उपलब्ध नहीं होगा पता नहीं है, वे सब बाहर से (उदाहरण के लिए तर्क के रूप में) उपलब्ध कराया जाना है:
checkerBoard :: (ShapeCanvas m, PaintCanvas m) => Paint m -> Paint m -> m()
checkerBoard red black =
do
load red
draw $ Box (Point 10 10) (Point 20 20)
draw $ Box (Point 20 20) (Point 30 30)
load black
draw $ Box (Point 10 20) (Point 20 30)
draw $ Box (Point 20 10) (Point 30 20)
एक कार्यान्वयन
आप करने के लिए अपने कोड काम कर सकते हैं अमूर्तता के बिना विभिन्न पेंट्स का उपयोग करके अंक, बक्से, रेखाएं और पाठ खींचें, हम इसे पहले खंड से इंटरफेस को लागू करने के लिए बदल सकते हैं।
'WinPaint' क्या है? क्या आपके पास एक ऐसा मुख्य भाग है जो कुछ चलाता है और करता है, इसलिए हम देख सकते हैं कि यह सब क्या करना है? अस्तित्व में क्वांटिफाइड प्रकार कहां है जिसे आप खत्म करना चाहते हैं? – Cirdec
विनपेंट केवल कुछ ऐसा है जो किसी प्रकार के प्लेटफॉर्म विशिष्ट ड्राइंग संदर्भ में एक पॉइंटर लपेटता है जिसमें अग्रभूमि, पृष्ठभूमि, फ़ॉन्ट इत्यादि शामिल हैं। मैंने स्पष्ट रूप से उन्हें प्रमाणित रूप से प्रमाणित नहीं किया है, लेकिन मैं अस्तित्व में कैनवास क्लास को कैनवास को प्रमाणित करता हूं। यहां "ओपन" मुख्य है, और इसमें केवल कुछ पाठ के साथ एक विंडो खोलनी है। यह कोड काम नहीं करता है, लेकिन मुझे उम्मीद है कि यह मेरा इरादा पूरा हो जाएगा। – Evan
"इस एपीआई को डिजाइन करने का सही तरीका क्या है?" programmers.stackexchange.com के लिए अधिक उपयुक्त लगता है। आपको उस प्रश्न के लिए काफी विविध प्रतिक्रियाएं मिलेंगी। मेरा, उदाहरण के लिए, "आईओ() से छुटकारा पाएं" या "चमक कैसे करता है" के साथ शुरू होगा, इनमें से कोई भी अस्तित्वत्मक मात्रा के साथ कुछ भी नहीं करना है। – Cirdec