2014-02-19 43 views
3

我剛剛開始瞭解你對Haskell的好感,我在類型類中遇到了一些麻煩。我想創建一個可以使用任何數字類型的函數,並強制它成爲一個double類型。輸入num的雙倍簽名?

我首先想到的是定義

numToDouble :: Num -> Double 

但我不認爲這工作,因爲Num不是,這是一個類型類(這在我看來是一組類型)。所以看着read,顯示(Read a) => String -> a。我正在讀的是「讀取一個字符串,並返回一個由用戶指定的類型a」。所以我寫了下面

numToDouble :: (Num n) => n -> Double 
numToDouble i = ((i) :: Double) 

這在我看來就像「拿(輸入n的事情必須在Num類型類,並將其轉換爲一個雙」,這似乎是合理的監守我可以做20::Double

這將產生以下輸出

Could not deduce (n ~ Double)                                                            
from the context (Num n)                                                             
    bound by the type signature for numToDouble :: Num n => n -> Double 

我不知道我在讀的基礎上能發現什麼,好像這事做多態?

編輯:

要清楚,我的問題是:爲什麼這不工作?

+0

你可以寫這個函數,但它需要啓用'RankNTypes':'toDouble ::(forall n。num n => n) - > Double; toDouble i = i'。這可能不像你想要的那樣,你可以寫'toDouble 1'而不是'toDouble(1 :: Int)',因爲函數的參數必須是完全多態的 - 類型必須是「Num中的任何類型」 '' – user2407038

回答

9

你可以說「20 ::雙師型」的原因是,在Haskell字面整數的類型是「數一=>一」,這意味着它可以是任何你喜歡的數字類型。

你是正確的,一個類型類是一組類型。確切地說,它是在類型類的「where」子句中實現函數的一組類型。您的numToDouble的類型簽名正確表達您想要執行的操作。

所有你知道類型的值「n」在你的功能是,它實現了民接口。這由+, - ,*,negate,abs,signum和fromInteger組成。最後一個是唯一一個進行類型轉換的人,但它對於你想要的沒有任何用處。

請記住,Complex也是Num的一個實例。 numToDouble應該怎麼做?正確的事情並不明顯,這是你有問題的原因之一。

然而,降低類型層次結構中的Real類型類別,它具有您可能想要使用的所有更直接的數字類型的實例,例如浮點數,雙精度和各種整數類型。這包括一個函數「toRational」,它將任何實數值轉換爲一個比率,您可以使用「fromRational」將其轉換爲Double,這是「分數」類型類型的函數。

所以嘗試:

toDouble :: (Real n) => n -> Double 
toDouble = fromRational . toRational 

但是,當然,這其實是過於具體。 GHCI說:

Prelude> :type fromRational . toRational 
fromRational . toRational :: (Fractional c, Real a) => a -> c 

所以它轉換爲任何實際類型的任何分數類型(後者包括任何可以做的劃分,包括事情是不是真正的實例,如園區)當數字類型亂搞我一直髮現自己使用它作爲一種通用的數字強制。

編輯:爲leftaroundabout說,

realToFrac = fromRational . toRational 
3

你不能在Haskell中「轉換」任何東西。在特定類型之間,可能有轉換的可能性 - 具有專用功能。

在你的具體例子中,肯定不應該的工作。 Num是類的所有類型,可以是處理作爲數值類型的,並且在他們數值(至少整數的,所以這裏的一個這樣的轉換函數fromInteger)。

但這些類型可以分開,其中有任何其他的東西,其中往往不是實際的,因此不能近似Double。最明顯的例子是Complex

只有的特定類其中的實數是驚奇的,稱爲Real。確實有點奇怪的是,它的方法是一個轉換toRational,因爲它的理由不能完全覆蓋真實...但它們在它們之間密集,所以它是好的。無論如何,您可以使用該功能來實現你需要的轉換:

realToDouble :: Real n => n -> Double 
realToDouble i = fromRational $ toRational i 

順便說一句,該組合fromRational . toRational已經是一個標準功能:realToFrac,更普遍一點。


呼叫類型類「集合類型」是種好了,就像你經常可以脫身,而無需調用任何類型的集合在數學一組 - 但它不是真正正確的。最有問題的是,在一個特定的類中,你不能說某種類型是而不是:類型類是開放的,所以在項目的任何地方你都可以爲給定類聲明某種類型的實例。

+2

有些語言使用'Real'作爲'Float'或'Double'的同義詞。有趣的是,Haskell的「Rational」比這些「Real」類型有更多的元素。不要告訴康託。 :) – AndrewC

+2

使用'Real'作爲'Double'的同義詞是一種令人憎惡的行爲。實數有很多浮點數沒有的好的屬性。所以讓我們用正確的話來說明正確的事情。 – augustss

+0

@augustss:我同意把'Double'稱爲'真正'並不好,但是OTOH與我們的最佳匹配......除了[AERN]之類的高級內容外(http://hackage.haskell.org/package/AERN-Real)實現;當然;但在大多數應用中使用幾乎不可行。當然,'Double'比'Rational'更適合實數。 – leftaroundabout

3

有作爲在Haskell數字蒙上沒有這樣的事情。當你寫i :: Double時,這意味着什麼不是「鑄造iDouble」;這只是一個斷言,i的類型是Double。在你的情況,但是,你的函數的簽名也斷言i的類型是Num n => n,即任何類型的n(由主叫方選擇的)實現Num;例如,n可能是Integer。這兩個斷言不能同時存在,因此你會得到一個錯誤。

令人困惑的是,你可以1 :: Double。但是,這是因爲在Haskell,像1數字字面的含義爲fromInteger one,其中one :: Integer是它的值是一個Integer相同。

但是,這隻適用於數字文字。如果你從幾乎任何其他語言來到Haskell,這是令人驚訝的事情之一。在大多數語言中,您可以自由地使用混合數字類型的表達式,並依靠隱式強制來「做我的意思」。在Haskell中,另一方面,您必須始終使用像fromIntegralfromRational這樣的函數。儘管大多數靜態類型語言都有一種從一種數字類型轉換爲另一種數字類型的語法,但在Haskell中,您只需使用一個函數。

3

只是要100%清楚,問題是

(i) :: Double 

這並不轉換iDouble,它要求i已經是一個Double。這不是你的意思。

您的功能的類型簽名是正確的。 (或者至少,這意味着你認爲它的意思。)但是你的函數的實現是錯誤的。

如果要轉換一種數據類型到另一種,你必須真正調用函數某種

不幸的是,Num本身只允許您將Integer轉換爲任何Num實例。你試圖轉換一些不一定是Integer的東西,所以這沒有幫助。正如其他人所說,你可能想要fromRational或類似的...