2012-09-13 68 views
2

對於給定類型'a',下面的函數f採用類型'c'的參數。對於不同類型的'a','c'以不同的方式受到限制。具體來說,當'a'是任何Integral類型時,'c'應該被允許爲任何'Real'類型。當'a'是浮動時,'c'只能是浮動。使用函數依賴關聯的參數限制

的嘗試是:

{-# LANGUAGE 
MultiParamTypeClasses, 
FlexibleInstances, 
FunctionalDependencies, 
UndecidableInstances #-} 

class AllowedParamType a c | a -> c 

class Foo a where 
    f :: (AllowedParamType a c) => c -> a 

fIntegral :: (Integral a, Real c) => c -> a 
fIntegral = error "implementation elided" 

instance (Integral i, AllowedParamType i d, Real d) => Foo i where 
    f = fIntegral 

出於某種原因,GHC 7.4.1抱怨說,它 「無法推斷(真正的C)從使用fIntegral的產生」。在我看來,功能依賴應該允許這種推論。在這個例子中,a與i是統一的,所以通過函數依賴,d應該與c一致,在實例中它被聲明爲'Real'。我在這裏錯過了什麼?

拋開功能依賴關係,這種方法是否足以表達上述限制,還是有更好的方法?我們只對「一個」一些不同的價值觀工作,所以會有這樣的情況,如:

instance (Integral i, Real c) => AllowedParamType i c 
instance AllowedParamType Float Float 

感謝

+0

當您說'class AllowedParamType a c | a - > c',你說給定任何類型'a'作爲第一個參數,最多隻有一個類型'c'可以用作第二個參數。但是當你說第一個類型是一個'Integral'類型時,*任何*'Real'類型都可以用作第二個參數。理想情況下,GHC會給你一個錯誤信息指出這一點。 – dave4420

+0

這不是真的。它只是意味着一個唯一的確定b允許以上類型的實例。 – Satvik

+0

Satvik是對的,fundep *的獨特性不應該是導致代碼不能編譯的原因。然而,dave4420也有一個正確的想法:從長遠來看,fundep不會起作用,因爲我不想將Int的參數類型限制爲整個程序的一種Real。 – crockeea

回答

1

好吧,這個人一直在嘮叨我。考慮到各種情況下的, 讓我們去整個生豬和擺脫不是一個實例存在其他的 源和目標類型之間的關係:

{-# LANGUAGE OverlappingInstances, FlexibleInstances,TypeSynonymInstances,MultiParamTypeClasses #-} 

class Foo a b where f :: a -> b 

現在我們可以匹配對類型它們之間的f但是我們喜歡,例如:

instance Foo Int Int where f = (+1) 
instance Foo Int Integer where f = toInteger.((7::Int) -) 
instance Foo Integer Int where f = fromInteger.(^ (2::Integer)) 
instance Foo Integer Integer where f = (*100) 
instance Foo Char Char where f = id 
instance Foo Char String where f = (:[]) -- requires TypeSynonymInstances 
instance (Foo a b,Functor f) => Foo (f a) (f b) where f = fmap f -- requires FlexibleInstances 
instance Foo Float Int where f = round 
instance Foo Integer Char where f n = head $ show n 

這也意味着大量的顯式類型註解,以避免No instance for...Ambiguous type錯誤消息。 例如,你不能這樣做main = print (f 6),但你可以做main = print (f (6::Int)::Int)

你可以列出所有你想要的標準類型, 這可能導致一個可怕的很多重複的實例,我們的你可以照亮藍色Touchpaper的和做的事:

instance Integral i => Foo Double i where f = round -- requires FlexibleInstances 
instance Real r => Foo Integer r where f = fromInteger -- requires FlexibleInstances 

請注意:這並不意味着「嘿,如果你有一個整體式i, 你用這個方便的可以有一個實例Foo Double i免費功能全面「 它的意思是:」 時候你有任何類型i,這絕對是一個實例 Foo Double i。順便說一下,我爲此使用了round,所以除非您的類型iIntegral, 我們會掉線。「例如,這對於Foo Integer Char實例來說是一個大問題。

這很容易破壞你的其他情況,所以如果你現在輸入f (5::Integer) :: Integer

Overlapping instances for Foo Integer Integer 
    arising from a use of `f' 
Matching instances: 
    instance Foo Integer Integer 
    instance Real r => Foo Integer r 

你可以改變你的pragma包括OverlappingInstances:

{-# LANGUAGE OverlappingInstances, FlexibleInstances,TypeSynonymInstances,MultiParamTypeClasses #-} 

所以現在f (5::Integer) :: Integer回到500,所以顯然它使用更具體的Foo Integer Integer實例。

我認爲這種方法可能適用於您,手動定義多個實例,仔細考慮何時完全通配 使實例脫離標準類型類。 (或者,並非所有的標準類型都是如此,衆所周知,notMany choose 2 = notIntractablyMany,因此您可以將它們全部列出。)

3

一個可能更好的辦法,就是用約束種類和類型系列(GHC擴展,需要GHC 7.4,我認爲)。這允許您將約束指定爲類實例的一部分。

{-# LANGUAGE ConstraintKinds, TypeFamilies, FlexibleInstances, UndecidableInstances #-} 

import GHC.Exts (Constraint) 

class Foo a where 
    type ParamConstraint a b :: Constraint 
    f :: ParamConstraint a b => b -> a 

instance Integral i => Foo i where 
    type ParamConstraint i b = Real b 
    f = fIntegral 

編輯:在進一步的實驗,也有一些細微之處的意思是,這並不按預期工作,具體而言,type ParamConstraint i b = Real b太一般了。我現在不知道解決方案(或者是否存在)。

+0

我想知道如果約束類型可能會做的伎倆。這些微妙之處是什麼? – crockeea

+1

那麼,只要你不添加任何其他實例,這個解決方案就可以很好地工作...... –

0

下面是一個解決更普遍問題的建議,不是你的具體情況(我需要更多的細節,但我承諾稍後檢查)。我正在寫它,以防其他人正在尋找對您類似問題的解決方案,在我發現SO之前,我當然是在過去。當它幫助你嘗試一種全新的方法時,它特別好。

我曾經有過的工作習慣:

  1. 介紹一個多參數類型的類(類型掛出所有的地方,所以......)
  2. 介紹函數依賴(應該收拾一下吧。但後來我最終需要...)
  3. 添加FlexibleInstances(警鐘已敲響。還有一個原因,編譯器在默認情況下有此關...)
  4. 添加UndecidableInstances(GHC告訴你,你在你的自己的,因爲它不相信它取決於你設定的挑戰)
  5. 一切都炸了。以某種方式重構。

然後我發現type families(函數式編程的類型(hooray) - 多參數類型類(有點類似於邏輯編程類型))的樂趣。我的工作流程改爲:

  1. 引入包含關聯類型的類型類,即更換

    class MyProblematicClass a b | a -> b where 
        thing :: a -> b 
        thang :: b -> a -> b 
    

    class MyJustWorksClass a where 
        type Thing a :: * -- Thing a is a type (*), not a type constructor (* -> *) 
        thing :: a -> Thing a 
        thang :: Thing a -> a -> Thing a 
    
  2. 緊張地添加FlexibleInstances。沒有任何問題。

  3. 有時通過使用像(MyJustWorksClass j,j~a)=>代替(MyJustWorksClass a)=>(Show t,t ~ Thing a,...)=>代替(Show (Thing a),...) =>的限制,以幫助GHC出解決的事情。 (~實質上是指'與'是相同的類型)
  4. 請緊急添加FlexibleContexts。沒有任何問題。
  5. 一切正常。

原因「沒有什麼不順心的一切」是GHC 用我喜歡的類型功能Thang而不是使用只是一堆斷言的是有沒有一個功能它試圖推斷計算類型Thing a它應該能夠解決它。

給它一個去吧!閱讀Fun with Type Functions之前閱讀manual

+0

我爲我的解決方案嘗試新類型類型的原因是因爲我知道一個關聯類型不會允許我表達約束是一個類而不是一個具體的類型(因爲關聯類型必須是具體的)。相反,我使用了一個fundep,但是這會遇到與關聯類型同義詞相同的問題。 – crockeea

+0

@Eric:問題在於沒有函數依賴關係?你在評論中的回答不是。您描述的源類型和目標類型之間的關係似乎是多對多的。 – AndrewC