आपकी समस्या वास्तव में उस प्रश्न के समान नहीं है। आपके द्वारा लिंक किए गए प्रश्न में, डेरेक थर्न के पास एक समारोह था जिसे वह जानता था ने Set a
लिया, लेकिन पैटर्न-मिलान नहीं कर सका। आपके मामले में, आप एक फ़ंक्शन लिख रहे हैं जो a
लेगा जिसमें Show
का उदाहरण होगा; आप यह नहीं बता सकते कि आप रनटाइम पर किस प्रकार देख रहे हैं, और केवल उन कार्यों पर भरोसा कर सकते हैं जो किसी भी Show
सक्षम प्रकार के लिए उपलब्ध हैं। यदि आप एक समारोह करना चाहते हैं तो विभिन्न डेटा प्रकारों के लिए अलग-अलग चीजें करें, इसे एड-होक पॉलिमॉर्फिज्म के रूप में जाना जाता है, और Show
जैसे प्रकार वर्गों के साथ हास्केल में समर्थित है। (यह पैरामीट्रिक पॉलिमॉर्फिज्म के विपरीत है, जो कि जब आप head (x:_) = x
जैसे फ़ंक्शन लिखते हैं, जिसमें head :: [a] -> a
टाइप किया गया है; अनन्य सार्वभौमिक a
इसके बजाय पैरामीट्रिक बनाता है।) तो आप जो चाहते हैं उसे करने के लिए, आपको बनाना होगा अपनी खुद की टाइप क्लास, और जब आपको इसकी आवश्यकता होती है तो उसे तुरंत चालू करें। हालांकि, यह सामान्य से थोड़ा अधिक जटिल है, क्योंकि आप Show
का हिस्सा जो कुछ भी अपनी नई प्रकार की कक्षा का हिस्सा हैं, बनाना चाहते हैं। इसके लिए कुछ संभावित खतरनाक और शायद अनावश्यक रूप से शक्तिशाली जीएचसी एक्सटेंशन की आवश्यकता होती है। इसके बजाय, चीजों को सरल क्यों न करें? आप शायद उन प्रकारों का सबसेट निकाल सकते हैं जिन्हें आपको वास्तव में प्रिंट करने की आवश्यकता है। एक बार जब आप ऐसा करते हैं, आप कोड इस प्रकार लिख सकते हैं:
{-# LANGUAGE TypeSynonymInstances #-}
module GraphvizTypeclass where
import qualified Data.Map as M
import Data.Map (Map)
import Data.List (intercalate) -- For output formatting
surround :: String -> String -> String -> String
surround before after = (before ++) . (++ after)
squareBrackets :: String -> String
squareBrackets = surround "[" "]"
quoted :: String -> String
quoted = let replace '"' = "\\\""
replace c = [c]
in surround "\"" "\"" . concatMap replace
class GraphvizLabel a where
toGVItem :: a -> String
toGVLabel :: a -> String
toGVLabel = squareBrackets . ("label=" ++) . toGVItem
-- We only need to print Strings, Ints, Chars, and Maps.
instance GraphvizLabel String where
toGVItem = quoted
instance GraphvizLabel Int where
toGVItem = quoted . show
instance GraphvizLabel Char where
toGVItem = toGVItem . (: []) -- Custom behavior: no single quotes.
instance (GraphvizLabel k, GraphvizLabel v) => GraphvizLabel (Map k v) where
toGVItem = let kvfn k v = ((toGVItem k ++ "=" ++ toGVItem v) :)
in intercalate "," . M.foldWithKey kvfn []
toGVLabel = squareBrackets . toGVItem
इस सेटअप में, सब कुछ है जो हम Graphviz के उत्पादन GraphvizLabel
का एक उदाहरण है सकते हैं; toGVItem
फ़ंक्शन उद्धरण चीजें, और toGVLabel
पूरी चीज़ को तुरंत उपयोग के लिए स्क्वायर ब्रैकेट में रखता है। (मैंने आपके द्वारा इच्छित स्वरूपण में से कुछ को खराब कर दिया होगा, लेकिन वह हिस्सा सिर्फ एक उदाहरण है।) फिर आप घोषणा करते हैं कि GraphvizLabel
का उदाहरण क्या है, और इसे किसी आइटम में कैसे चालू करें। TypeSynonymInstances
ध्वज हमें instance GraphvizLabel [Char]
के बजाय instance GraphvizLabel String
लिखने देता है; यह हानिरहित है।
अब, अगर आप वास्तव में जरूरत एक Show
उदाहरण के साथ सब कुछGraphvizLabel
का एक उदाहरण के रूप में अच्छी तरह से हो सकता है, वहाँ एक रास्ता है। यदि आपको वास्तव में इसकी आवश्यकता नहीं है, तो इस कोड का उपयोग न करें! यदि आपको ऐसा करने की ज़रूरत है, तो आपको स्केली-नामित UndecidableInstances
और OverlappingInstances
भाषा एक्सटेंशन (और कम से कम FlexibleInstances
नामित) सहन करना होगा। इसका कारण यह है कि आपको यह कहना है कि सबकुछ है जो Show
सक्षम है GraphvizLabel
-लेकिन यह संकलक के बारे में बताना मुश्किल है। उदाहरण के लिए, यदि आप इस कोड का उपयोग करते हैं और GHCi प्रॉम्प्ट पर toGVLabel [1,2,3]
लिखते हैं, तो आपको एक त्रुटि मिलेगी, क्योंकि 1
में Num a => a
टाइप किया गया है, और Char
Num
का उदाहरण हो सकता है! इसे काम करने के लिए आपको स्पष्ट रूप से toGVLabel ([1,2,3] :: [Int])
निर्दिष्ट करना होगा। फिर, यह शायद आपकी समस्या पर सहन करने के लिए अनावश्यक रूप से भारी मशीनरी है। इसके बजाए, यदि आप उन चीज़ों को सीमित कर सकते हैं जो आपको लगता है कि लेबल में परिवर्तित हो जाएंगे, जो बहुत संभावना है, तो आप केवल उन चीज़ों को निर्दिष्ट कर सकते हैं!लेकिन अगर आप वास्तव में GraphvizLabel
क्षमता मतलब है Show
क्षमता चाहते हैं, यह आप क्या जरूरत है:
{-# LANGUAGE TypeSynonymInstances, FlexibleInstances
, UndecidableInstances, OverlappingInstances #-}
-- Leave the module declaration, imports, formatting code, and class declaration
-- the same.
instance GraphvizLabel String where
toGVItem = quoted
instance Show a => GraphvizLabel a where
toGVItem = quoted . show
instance (GraphvizLabel k, GraphvizLabel v) => GraphvizLabel (Map k v) where
toGVItem = let kvfn k v = ((toGVItem k ++ "=" ++ toGVItem v) :)
in intercalate "," . M.foldWithKey kvfn []
toGVLabel = squareBrackets . toGVItem
सूचना है कि अपने विशिष्ट मामलों (GraphvizLabel String
और GraphvizLabel (Map k v)
) एक ही रहना; आपने Int
और Char
मामलों को GraphvizLabel a
मामले में अभी तक ध्वस्त कर दिया है। याद रखें, UndecidableInstances
का अर्थ यह है कि यह वास्तव में क्या कहता है: संकलक नहीं बता सकता है अगर उदाहरण चेक करने योग्य हैं या इसके बजाय टाइपशेकर लूप बना देंगे! इस मामले में, मुझे उचित रूप से यकीन है कि यहां सबकुछ वास्तव में निर्णायक है (लेकिन यदि कोई नोटिस करता है कि मैं गलत हूं, कृपया मुझे बताएं)। फिर भी, UndecidableInstances
का उपयोग सावधानी से हमेशा संपर्क किया जाना चाहिए।