2017-09-12 78 views
0

我想使用應用性功能,並試圖如下:無法構造無限類型

*ReaderExercise Control.Applicative> (+4) <*> (+3) 

然後得到以下錯誤信息:

<interactive>:51:11: error: 
    * Occurs check: cannot construct the infinite type: a ~ a -> b 
     Expected type: (a -> b) -> a 
     Actual type: a -> a 
    * In the second argument of `(<*>)', namely `(+ 3)' 
     In the expression: (+ 4) <*> (+ 3) 
     In an equation for `it': it = (+ 4) <*> (+ 3) 
    * Relevant bindings include 
     it :: (a -> b) -> b (bound at <interactive>:51:1) 

什麼我想到的是回報函數一個參數。
這是什麼意思一個無限的類型?

+0

但是''<*>有類型'(<*> ):: Applicative f => f(a - > b) - > fa - > fb' ...由於在左邊你寫了'(+4)',Haskell的理由是'Num n => a〜b〜 N'。但是這意味着'(+3)'應該有'n'類型...... –

+0

即使它起作用,它也意味着像'\ x - >(+)x 4((+)x 3)'這裏的第一個'(+)'有太多爭論。 – chi

+0

就目前而言,由於您沒有勾勒出您的想法,因此要解釋您的思維錯誤在哪裏有點困難。以下是關於如何幫助我們幫助你的一些提示:你爲什麼期望這個函數返回一個參數?由於類型'(<*>)::應用型F => F(A - > B) - >發 - > fb',你能指望什麼每種類型的變量獲得專門到'(+4)<*>(+3) '(什麼是'f'?'a'?'b'?)?在這個表達式中,你認爲什麼具體類型對'4'有效? '(+4)'? '(+3)'? –

回答

2

當Haskell確定某個類型變量(由程序員顯式給出或由Haskell隱式引入)必須滿足一個條件時,它將需要遞歸地發生錯誤「發生檢查:無法構造[無限]類型」按照自身的方式定義,這將導致無限的「深度」類型(即,在其自身定義中「發生」的類型變量)。

它通常從任一對有關在程序混淆兩個不同的「結構的水平」程序員的一部分的錯字或概念上的錯誤造成的。

作爲一個簡單的例子,整數(類型[Int])的列表是一個有效的Haskell類型,所以是整數([[Int]])或整數的列表的列表的列表的列表的列表的列表的列表([[[[[Int]]]]] ),但只允許有限數量的列表級別。你不可能擁有列表清單等列表,一路下來 - 這將是一個無限的類型。如果Haskell認爲你想要它構造這樣的類型,它會給你一個「發生檢查」的錯誤。

如下定義:

yuck (x:xs) = x == xs 

給出了這樣的錯誤正是這個原因。 Haskell從左側知道yuck取一些未知元素類型的列表a其中變量x是類型a的頭和變量xs是類型[a]的尾部。從RHS,操作(==)力量xxs具有相同類型 - 換句話說,它意味着約束a ~ [a]其中波浪線表示「式的平等」。沒有有限型(無類型與數量有限列表級別)有這個特性,只有無效無限類型[[[[...forever...]]]]可以讓您免除外列表級別,仍然有剩下的相同類型的,所以你的錯誤。

這裏的問題是程序員混淆了兩層結構:列表xs和元素x

在你的具體的例子,對錯誤的原因是相似的,但難以解釋。操作者:

(<*>) :: (Applicative f) => f (a -> b) -> f a -> f b 

採取與不同的基本類型2個應用性操作:左手側有一個類型通過施加到底層類型a -> b的適用函子f給出右手側有一個類型通過施加到底層類型b相同適用函子f給出。

你還沒告訴哈斯克爾其適用函子f你想用的,所以哈斯克爾試圖推斷出它。因爲LHS具有類型:

(+4) :: (Num n) => n -> n 

哈斯克爾嘗試與f (a -> b)匹配類型n -> n。使用(->)類型運算符的前綴形式來編寫這些類型可能會更清楚:Haskell試圖匹配(->) n nf ((->) a b),其中f是一個應用函子。

幸運的是,對於任何類型的(->) t都有一個應用仿函數實例t。所以,Haskell的理由是你想要的應用仿函數是f = (->) n,並且它成功匹配(->) n n = f nf ((->) a b)。這意味着n等於((->) a b)。然後,Haskell嘗試匹配RHS上的類型,匹配(->) n n = f n(->) n a = f a。這工作,它意味着n等於a

現在我們有一個問題。 n同時等於a -> b(來自LHS)和a(來自RHS)。這意味着創造無限功能型的,這東西看起來像:

(((... forever ...)->b)->b)->b)->b 

這是你可以排除在外...->b,並留下與同類型的唯一途徑。這是一個不可能的無限類型,所以你會得到錯誤。

底層問題在於您犯了概念性錯誤。鑑於您正在使用ReaderExample,我認爲您打算使用(->) n應用仿函數實例,所以您和Haskell在這一點上達成了一致。在這種情況下:

(+4) :: (Num n) -> n -> n 

是讀者的行動,從閱讀器讀取數,並增加了四到它。同樣,(+3)是一個閱讀器操作,它從閱讀器中讀取一個數字,並向其中添加三個數字。

然而,(<*>)是發生在從所述讀取器讀取以產生函數(非數字!)的LHS讀取器動作的操作者,然後將其應用於使用RHS從讀取的結果讀者產生一個數字。例如,如果你定義:

multiplyByReader :: (Num n) -> n -> n -> n 
multiplyByReader readerNum input = readerNum * input 

則:

multiplyByReader <*> (+4) 

或簡單的版本:

(*) <*> (+4) 

纔有意義。意圖的含義是:構建一個閱讀器動作,(1)使用LHS從閱讀器中讀取一個數字,以創建一個函數,該函數與閱讀器相乘;然後(2)將此函數應用於將RHS應用於讀者的數字。

這將相當於\r -> r * (r + 4),你可以看到:

> ((*) <*> (+4)) 5 -- same a 5 * (5 + 4) 
45 
> 

當你寫(+3) <*> (+4),你是混合了兩種不同結構層次:LHS讀者產生了一些而應該得到功能可以應用於一個數字。

我最好的猜測是你想要創建一個閱讀器動作,將(+4)應用於讀者以獲取一個數字,然後將(+3)應用於該結果。在這種情況下,(+3)不是讀者操作;它只是你要應用到讀者動作(+4),這相當於fmap平在讀取器操作的結果的函數:

(+3) <$> (+4) 

當然,你能等效它直接寫爲:

(+3) . (+4) 

兩者都是增加7閱讀次數複合讀寫操作:

> ((+3) <$> (+4)) 5 
12 
> ((+3) . (+4)) 5 
12 
> 
相關問題