2015-04-07 5 views
15

मेरे पास कक्षा Cyc c r है जिसमें c m r फ़ॉर्म के डेटा के लिए फ़ंक्शन हैं, जहां m एक प्रेत प्रकार है। उदाहरण के लिए,एक डिक्टरी को एक बाधा में बदलना

class Cyc c r where 
    cyc :: (Foo m, Foo m') => c m r -> c m' r 

मैं m एक वर्ग पैरामीटर नहीं कर के लिए अच्छे कारणों की क्या ज़रूरत है। इस उदाहरण के प्रयोजनों के लिए, प्राथमिक कारण यह है कि यह कार्यों पर बाधाओं की संख्या को कम करता है। मेरे वास्तविक उदाहरण में, इस इंटरफ़ेस के लिए एक अधिक आकर्षक आवश्यकता यह है कि मैं बदलते और छिपे हुए प्रेत प्रकारों के साथ काम करता हूं, इसलिए यह इंटरफ़ेस मुझे किसी भी प्रेत प्रकार के लिए Cyc बाधा प्राप्त करने देता है।

उस विकल्प का एक नकारात्मक पक्ष यह है कि मैं Num (c m r)Cyc की सुपरक्लस बाधा नहीं बना सकता। मेरा इरादा यह है कि c m r(Cyc c r, Foo m) जब भी Num होना चाहिए। वर्तमान समाधान बहुत कष्टप्रद है: मैं कक्षा Cyc

witNum :: (Foo m) => c m r -> Dict (Num (c m r)) 

जो तरह-की ही बात सिद्ध करने के लिए विधि गयी। अब जब मैं एक समारोह है कि एक सामान्य Cyc लेता है और एक Num (c m r) बाधा की जरूरत है, मैं लिख सकते हैं:

foo :: (Cyc c r, Foo m) => c m r -> c m r 
foo c = case witNum c of 
    Dict -> c*2 
पाठ्यक्रम के

मैं foo करने के लिए एक Num (c m r) बाधा जोड़ सकता है, लेकिन मैं संख्या को कम करने की कोशिश कर रहा हूँ बाधाओं, याद है? (Cyc c r, Foo m) को Num (c m r) बाधा को इंगित करना है (और मुझे अन्य प्रयोजनों के लिए Cyc c r और Foo m की आवश्यकता है), इसलिए मैं Num बाधा को भी लिखना नहीं चाहता हूं।

इस प्रश्न को लिखने की प्रक्रिया में, मुझे इसे पूरा करने के लिए एक बेहतर (?) तरीका मिला, लेकिन इसकी अपनी कमियां हैं।

मॉड्यूल फू:

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, ScopedTypeVariables #-} 
module Foo where 

import Data.Constraint 

class Foo m 

class Cyc c r where 
    cyc :: (Foo m, Foo m') => c m r -> c m' r 
    witNum :: (Foo m) => c m r -> Dict (Num (c m r)) 

instance (Foo m, Cyc c r) => Num (c m r) where 
    a * b = case witNum a of 
      Dict -> a * b 
    fromInteger a = case witNum (undefined :: c m r) of 
        Dict -> fromInteger a 

-- no Num constraint and no Dict, best of both worlds 
foo :: (Foo m, Cyc c r) => c m r -> c m r 
foo = (*2) 

मॉड्यूल बार: इस दृष्टिकोण मुझे सब कुछ मैं के लिए देख रहा हूँ हो जाता है

{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, OverlappingInstances #-} 
module Bar where 

import Foo 
import Data.Constraint 

data Bar m r = Bar r deriving (Show) 

instance (Num r) => Cyc Bar r where 
    witNum _ = Dict 

instance (Num r, Foo m) => Num (Bar m r) where 
    (Bar a) * (Bar b) = Bar $ a*b 
    fromInteger = Bar . fromInteger 

instance Foo() 

bar :: Bar() Int 
bar = foo 3 

है, यह कमजोर लगती है। मेरा मुख्य चिंताएं हैं:

  1. मैं मॉड्यूल Foo में Num के लिए सामान्य उदाहरण सिर से सावधान कर रहा हूँ।
  2. किसी भी अतिव्यापी उदाहरणों Foo में आयात कर रहे हैं, मैं अचानक IncoherentInstances की जरूरत है या foo पर Num बाधा स्थगित करने उदाहरण चयन क्रम के लिए।

वहाँ एक वैकल्पिक तरीका हर समारोह है कि जरूरत Num (c m r) कि इन कमियां में से किसी से बचा जाता है में Dict का उपयोग कर से बचने के लिए है?

+3

यदि आप [बाधाओं] का उपयोग कर रहे हैं (https://hackage.haskell.org/package/constraints) 'witNum' को परिभाषित करने का मूर्ख तरीका' witNum :: Foo m: - Num (cmr) 'होगा जो आप उदाहरण के साथ deconstruct 'foo = case witNum :: Foo m: - उप डिक्ट की संख्या (सी एम आर) -> (* 2)' (जो कोई सुंदर नहीं है)। – Cirdec

+0

कि 'न्यूम (सी एम आर)' इंस्टेंस हेड * आपको डराएगा। – dfeuer

+0

लेकिन मैं इसके बारे में * क्या करूँ? :-) – crockeea

उत्तर

1

6 महीने के विचार के बाद, अंततः मेरे ऊपर मेरी लटकती टिप्पणी का उत्तर है: newtype रैपर जोड़ें!

मैं दो में Cyc वर्ग विभाजित:

class Foo m 

class Cyc c where 
    cyc :: (Foo m, Foo m') => c m r -> c m' r 

class EntailCyc c where 
    entailCyc :: Tagged (c m r) ((Foo m, Num r) :- (Num (c m r))) 

तो मैं इसके बाद के संस्करण के रूप में मेरे Cyc उदाहरण परिभाषित:

data Bar m r = ... 

instance Cyc Bar where ... 

instance (Num r, Foo m) => Num (Bar m r) where ... 

instance EntailCyc Bar where 
    witNum _ = Dict 

तो मैं एक newtype आवरण को परिभाषित करने और इसके लिए एक सामान्य Cyc उदाहरण दे:

newtype W c m r = W (c m r) 

instance Cyc (W c m r) where cyc (W a) = W $ cyc a 

instance (EntailCyc c, Foo m, Num r) => Num (W c m r) where 
    (W a) + (W b) = a + b \\ witness entailCyc a 

अंत में, मैं चैन जीई सभी कार्यों कि एक सामान्य c m r प्रकार का उपयोग किया एक W c m r प्रकार का उपयोग करने के लिए:

foo :: (Cyc c, EntailCyc c, Foo m, Num r) => W c m r -> W c m r 
foo = (*2) 

यहां मुद्दा यह है कि fooकई की कमी (जैसे, Eq (W c m r), Show (W c m r), आदि) कि प्रत्येक व्यक्तिगत रूप से अपने स्वयं के की आवश्यकता होगी की आवश्यकता हो सकती है बाधाओं। हालांकि, Eq के लिए W c m r, Show के लिए सामान्य उदाहरणों, आदि सभी बिल्कुल बाधाओं (EntailCyc c, Foo m, Eq/Show/... a) है, तो foo पर बाधाओं से ऊपर केवल की कमी मैं लिखने की जरूरत है!

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