9

के बीच कनवर्ट करने का अच्छा तरीका मैं सोच रहा हूं कि विज्ञापन-हाक पॉलीमोर्फिक फ़ंक्शंस और पैरामीट्रिक पॉलिमॉर्फिक वाले लोगों के बीच कनवर्ट करने के सामान्य तरीके हैं या नहीं। दूसरे शब्दों में, एक विज्ञापन-पॉलीमोर्फिक फ़ंक्शन दिया गया है, इसके पैरामीट्रिक समकक्ष को कैसे कार्यान्वित किया जाए? और दूसरी तरफ के बारे में क्या?एड-होक पॉलीमोर्फिक फ़ंक्शंस और पैरामीट्रिक पॉलिमॉर्फिक वाले

उदाहरण के रूप में sort लें। यह sortBy के मामले में sort :: Ord a => [a] -> [a] लिखने के लिए आसान है:

sort :: Ord a => [a] -> [a] 
sort = sortBy compare 

लेकिन दूसरी तरह के आसपास मुश्किल लगता है, अब तक सबसे अच्छा मैं कर सकता है एक सा "वस्तु उन्मुख" जाने के लिए है:

import qualified Data.List as L 

data OrdVal a = OV (a -> a -> Ordering) a 

instance Eq (OrdVal a) where 
    (OV cmp a) == (OV _ b) = a `cmp` b == EQ 

instance Ord (OrdVal a) where 
    (OV cmp a) `compare` (OV _ b) = a `cmp` b 

sortBy :: (a -> a -> Ordering) -> [a] -> [a] 
sortBy cmp = map unOV . L.sort . map (OV cmp) 
    where 
    unOV (OV _ v) = v 

लेकिन यह उचित समाधान की तुलना में एक हैक की तरह लगता है।

तो मैं जानना चाहते हैं:

  1. इस विशिष्ट उदाहरण के लिए बेहतर तरीके देखते हैं?
  2. विज्ञापन-पॉलीमोरिमिक फ़ंक्शंस और पैरामीट्रिक वाले लोगों के बीच कनवर्ट करने के लिए सामान्य तकनीक क्या हैं?
+1

यदि हम शब्दकोशों को पारित कर सकते हैं (उदा। एग्डा implicits में), यह मामूली होगा। हालांकि, मेरा मानना ​​है कि कुछ वर्ग/पुस्तकालय इस तथ्य का फायदा उठाते हैं कि हम कुछ आविष्कार सुनिश्चित करने के लिए शब्दकोशों को पास नहीं कर सकते हैं। उदाहरण के लिए, कल्पना करें कि क्या हम हर समय एक अलग ऑर्डरिंग का उपयोग करके'डेटा.नेट.इन्सर्ट 'को कॉल कर सकते हैं ... – chi

+6

यह भी ध्यान रखें कि आपका "हैक" अभ्यास में काम करता है, लेकिन केवल तभी जब आप दो अलग-अलग' cmp' फ़ंक्शंस ' OrdVal एक मूल्य। यदि आप करते हैं, तो आपका 'ऑर्ड' उदाहरण 'ऑर्ड' कानूनों को पूरा नहीं करता है। – chi

उत्तर

7

मैं जरूरी नहीं कह रहा हूँ आप इस करना चाहिए, लेकिन आप कर सकते हैं उपयोग reflection यह पैकेज के लिए सूची के प्रत्येक तत्व के साथ बिना तुलना समारोह के आसपास पारित करने के लिए:

{-# LANGUAGE UndecidableInstances #-} 
import Data.Reflection 

newtype O a = O a 

instance Given (a -> a -> Ordering) => Eq (O a) where 
    x == y = compare x y == EQ 

instance Given (a -> a -> Ordering) => Ord (O a) where 
    compare (O x) (O y) = given x y 

यह देखते हुए (हे) के ऊपर बुनियादी सुविधाओं, आप तो sort के मामले में sortBy लिख सकते हैं इस प्रकार है:

import Data.Coerce 
import Data.List (sort) 

sortBy :: (a -> a -> Ordering) -> [a] -> [a] 
sortBy cmp = give cmp $ from . sort . to 
    where 
    to :: [a] -> [O a] 
    to = coerce 

    from :: [O a] -> [a] 
    from = coerce 

(ध्यान दें कि Data.Coerce का उपयोग करके, हम O आवरण के सभी संभावित क्रम लागत से बचने)

+2

'दिया गया' थोड़ा बुरा है, और आपको वास्तव में इसकी आवश्यकता नहीं है। नए प्रकार के लिए एक प्रेत जोड़ें और उसके बाद 'दिए गए' के ​​बजाय 'Reifies' का उपयोग करें, 'देने' के बजाय 'पुनः प्राप्त करें' और 'दिए गए' के ​​बजाय' प्रतिबिंबित करें 'का उपयोग करें। – dfeuer

4

कैक्टस के जवाब reflection में कुछ हद तक छायादार Given वर्ग पर निर्भर करता है। हालांकि, इसके बिना प्रतिबिंब का उपयोग करना संभव है।

{-# LANGUAGE ScopedTypeVariables, MultiParamTypeClasses, UndecidableInstances #-} 

module SortReflection where 

import Data.Reflection 
import Data.List (sort) 
import Data.Proxy 
import Data.Coerce 

newtype O s a = O {getO :: a} 

instance Reifies s (a -> a -> Ordering) => Eq (O s a) where 
    a == b = compare a b == EQ 

instance Reifies s (a -> a -> Ordering) => Ord (O s a) where 
    compare = coerce (reflect (Proxy :: Proxy s)) 

sortBy :: forall a . (a -> a -> Ordering) -> [a] -> [a] 
sortBy cmp = reify cmp $ 
    \(_ :: Proxy s) -> coerce (sort :: [O s a] -> [O s a]) 

जांच कोर का उत्पादन इंगित करता है कि इस sortBy चारों ओर एक पतली आवरण को संकलित करता है। Proxy के बजाय Tagged पर आधारित Reifies कक्षा का उपयोग करके यह पतला दिखता है, लेकिन एड Kmett उत्पादित एपीआई पसंद नहीं करता है।

+0

'sortBy' कार्यान्वयन में' coerce' का उपयोग क्यों नहीं करें? – Cirdec

+0

@Cirdec, अगर आप इसे कहीं और इस्तेमाल कर रहे हैं, तो इसका कोई विशेष कारण नहीं है। मुझे एहसास नहीं हुआ जब मैंने शुरू किया कि यह कहीं और फायदेमंद होगा। किसी भी मामले में, मैं आम तौर पर '# का उपयोग करना पसंद करता हूं।'और' # 'जहां' कोरर्स 'का उपयोग करने के बजाय सीधे लागू होता है, ऐसा करने के लिए आवश्यक प्रकार के हस्ताक्षरों की संख्या को कम करने के लिए होता है और कोड को स्पष्ट कर सकता है। यहां तक ​​कि जब 'Data.Profunctor.Unsafe' अनुपलब्ध है,' -> 'उदाहरण के उन बिट्स को आसानी से कॉपी किया जा सकता है। – dfeuer

+0

एकमात्र प्रकार का हस्ताक्षर जो आपको दो 'कॉरर्स' के साथ चाहिए, वह है :: [ओ एस ए] -> [ओ एस ए] '। यदि आप 'sortby cmp = cmp $ \ (_ :: प्रॉक्सी एस) -> कॉरपोरेशन को पुनः परिभाषित करते हैं। (सॉर्ट :: [ओ एस ए] -> [ओ एस ए])। वाणिज्य 'आपको संभावित मध्यवर्ती सूची आवंटन के बारे में चिंता करने की ज़रूरत नहीं है। – Cirdec

संबंधित मुद्दे