2014-11-02 55 views
1

在ghci中,所述:t命令不會看到什麼問題在這樣的表達式3 4(sin . (+)) 1 2 3 4sin . (+) 1 2 3 4(-) - (-)並愉快地提供他們的類型信息:浮動(一 - > a)中,NUM(一 - >一 - >一)等在Haskell

3 4 :: (Num (a -> t), Num a) => t 

sin . (+) 1 2 3 4 
    :: (Floating c, Num (a1 -> a2 -> a -> c), Num a1, Num a2) => a -> c 

(sin . (+)) 1 2 3 4 
    :: (Floating ((a -> a1 -> t) -> a -> a1 -> t), Num (a -> a1 -> t), 
     Num a, Num a1) => 
    t 

(-) - (-) :: (Num (a -> a -> a), Num a) => a -> a -> a 

我相信這背後有一個原因。但我幾乎無法想象(3 4)有什麼意義(我不是指實用的東西,而是至少可以編譯的東西),並且如果有人給我一個線索,我會很感激。

+1

事實上,'3 4'不編譯!如果沒有,你會看到一個類型錯誤,而不是一個類型。實際上並沒有關係,因爲'3 4'編譯的原因是類型類是開放的,所以有人可能會出現並定義一個實例'Num(a - > a)'在這個點上'3 4 '有價值。 – user2407038 2014-11-02 04:43:10

+1

我不知道這是否是重複本身,而是看到[這個問題和它的答案(http://stackoverflow.com/q/26515102/1186208)。謝謝你(含蓄地)同意我說'(3 4)'是一種可憎的東西 - 但是,如果你寫這個實例,它就會遵循並且毫無怨言地運行。 (相當於3.) – 2014-11-02 04:46:07

回答

4

很多事情「typecheck」與瘋狂或不可能推斷約束。我們有map :: (a -> b) -> [a] -> [b]的原因之一,而不是僅僅嚴格更一般的fmap :: Functor f => (a -> b) -> f a -> f b是,如果你錯誤地使用後者,你很可能最終會出現錯誤No instance (Functor [something obviously not a functor])而不是更友好的Expecting [a], got ...

Num/Floating的情況尤其令人不安,因爲所有數字文字都是使用類型類重載的。編譯器會高興地推斷出任何整型文字的任何類型,並且如果可能的話,就把它作爲一個約束條件。通常這會導致實例解析錯誤。

此外,它可能寫一個不完全無意義的instance Num b => Num (a -> b)That's a different question, though.正如我在那裏指出的那樣,它引起了我的質疑,並且故意不在base中。 (或者,就此而言,我查看了替代數字類型類軟件包。)

+1

我認爲能夠定義'Num(a - > b)'本身不是錯誤的,但這是GHC可能更有幫助的一些情況之一,它解釋如果類型推斷結束需要一個不存在的函數實例,真正的問題是*可能*將函數應用於錯誤的參數。 – 2014-11-02 05:38:09

+1

我已經看過一個Num實例,用於演示之前使用的函數,以便在數字上獲得自然的單位,例如「3千米」,其設置爲自動轉換爲基本單位。這很聰明,但我從來沒有用它在真實的代碼 – bheklilr 2014-11-02 16:26:05

+0

感謝您的原因和鏈接。 – zabolekar 2014-11-02 18:52:28

1

您幾乎可以製作任何東西Num。如果FlexibleInstances開啓時,你可能有一個主要-不確定,但是定義的,足以對演示實例:

{-# LANGUAGE FlexibleInstances #-} 
instance Num (Integer -> Integer) where 
    fromInteger = (+) 
    (+) = (.) 
    (-) = undefined 
    (*) = undefined 
    negate = undefined 
    abs = undefined 
    signum = undefined 

然後有一些類型的註釋,你可以得到一個合理的結果:

ghci> 2 (3 :: Integer) :: Integer 
5 
+0

謝謝你的例子。不知道這是真棒還是可怕的。 – zabolekar 2014-11-02 18:54:43

1

Haskell提供了很多其他語言不具備的靈活性。這樣做的代價是有很多情況下,其他語言會給你一個語法或類型錯誤,但Haskell不會。讓我們把你的例子:

3 4 :: (Num (a -> t), Num a) => t 

在Haskell,如果您有任何兩個語法有效的表達,使他們彼此相鄰,其間還爲您提供了一個有效的表達空白。因此,例如,如果fx兩種表達,這也是一個表達式:

f x 

這種類型的表達的被稱爲功能應用,或簡稱只是應用

除此語法規則外,還有一個輸入規則。我會寫上前面的表達式類型註釋:

((f :: a -> b) (x :: a)) :: b 

也就是說,對於應用程序得到很好的類型的,那麼對於類型abf :: a -> bx :: af x :: b一些選擇。

它使用什麼類型推斷你的程序的語法結構和任何顯式類型的註釋,您和您使用已經提供給弄清楚,可以分配給你的表達最普遍的類型庫。

所以現在,回到你的例子:

3 4 :: (Num (a -> t), Num a) => t 

這種表達是語法上有效的應用,因爲它具有f x形式我上面所解釋的。此外,整數常量33都有一段a(爲一個字面的每次出現獨立地選自)類型Num a => a。把所有這些東西放在一起,好吧,我們有一個句法結構良好的表達式,如果單獨採用,也會進行類型檢查。

你可能認爲這是瘋狂的,在某些情況下,一個整數文字可能是一個功能......但好,沒有這麼多。一個例子是:我曾經在那裏我使用的Haskell到原型數據轉換和查詢,其中在DSL表達式靜置功能從輸入(查詢條件)到值的域專用語言的項目。舉例來說,一個框架可能是一組對年/月值及營業員的,和值可能是由銷售人員在該月的售電量,或單位出售數量:

sales :: (YearMonth, Salesperson) -> Money 
units :: (YearMonth, Salesperson) -> Integer 

有的straightfoward方式(其中icktoofay的回答證明)的擴展Num類,讓我們寫這樣的表述:

avgSales :: (YearMonth, Salesperson) -> Money 
avgSales = sales/fromIntegral units 

,並延長Num類這樣也可以讓我們像對待3整數文字作爲文字DSL。對應於3功能爲常數函數返回3無論(YearMonth, Salesperson)組合是什麼:

salesTimesThree :: (YearMonth, Salesperson) -> Money 
salesTimesThree = sales * 3 

在實踐中你不能延伸到Num功能類型,然而,您可以定義爲這個新的類型(以及增加對查詢做IO來從文件或數據庫中的數據的能力):

newtype Query tag point value = 
    Query { runQuery :: Set (Tagged tag point) -> IO (Map (Tagged tag point) value) } 

instance Functor (Query tag point) where .... 
instance Applicative (Query tag point) where .... 
instance Num value => Num (Query tag point value) where .... 

但仍然是利落有作任何類型的成數的能力在那裏你可以明智地提供Num操作。隱含的缺點是你所說的:錯誤信息將不那麼有用。

相關問題