2017-04-21 71 views
3

我一直在通過Gabriel Gonzale的優秀blog post on Free Monads。爲了幫助我理解我想爲以下類型創建一個顯示實例,並在GHCI玩它,(從博客文章)的類型是:來自數據類型的顯示實例的錯誤

data Thread m r = Atomic (m (Thread m r)) | Return r 

而且我型我秀的實例是:

instance (Show m, Show r) => Show (Thread m r) where 
    show (Atomic m x) = "Atomic " ++ show m ++ " " ++ show x 
    show (Return x) = "Return " ++ show x 

不幸的是GHCI給我試圖加載文件時,這個錯誤:

• Expected kind ‘* -> *’, but ‘m’ has kind ‘*’ 
• In the first argument of ‘Thread’, namely ‘m’ 
    In the first argument of ‘Show’, namely ‘Thread m r’ 
    In the instance declaration for ‘Show (Thread m r)’ 

所以,最重要的是:這是什麼錯誤的意思是,爲什麼我得到它?我認爲回答這將有助於我非常理解博客文章(儘管略微迂迴)。 另外,一個工作的Show實現看起來像什麼?我試着看着Either的實例,但不明白髮生了什麼。

+0

'M'是*類型構造*像'IO'或'Maybe'。它不是一個完整的類型(比如'IO()'或'Maybe String')。但'm(Thread m r)'是一個完整的類型。 – immibis

+0

此外,數據構造函數'Atomic'只需要一個參數,而不是兩個。 – immibis

回答

4

第一所有,您收到的錯誤是錯誤。一種是一種類型的「類型」。就像類型對值進行分類一樣,種類也對類型進行分類。正如Haskell從值推斷類型,它也從類型推斷類型。我們使用相同的符號::來表示值具有某種類型(例如,1 :: Int)並且表示類型具有某種類型(例如,Int :: *)。

有在錯誤消息中提到兩種:*是那種由值居住類型,如IntBool,而* -> *是那種類型的構造Maybe[],和IO 。您可以將類型構造函數看作類型級函數:Maybe將類型(類型爲*)作爲參數,並返回一個類型(也是類型*),例如Maybe Int :: *。種類以功能相同的方式進行咖喱;例如,Either有那種* -> * -> *,因爲它需要的那種*兩個參數產生一類的一種*

Either :: * -> * -> * 
Either Int :: * -> * 
Either Int Bool :: * 

所以誤差來源於類型類的約束你的Show實例:

instance (Show m, Show r) => Show (Thread m r) where 
      ------ 

Show是可顯示的種類*的類別。您可以通過鍵入GHCI :kind Show(或:k Show)看到這一點:

> :kind Show 
Show :: * -> Constraint 

所以Show需要一個類型的一種*並返回一個類型類的約束。沒有涉及約束的太多細節,這意味着Show m意味着m :: *。但是,Thread的定義將m的參數傳遞給Atomic構造函數的定義,該構造函數的類型爲m (Thread m r)。看看那種Thread

> :kind Thread 
Thread :: (* -> *) -> * -> * 

這意味着,m :: * -> *,因此不匹配。

下一個錯誤是在你的Show實例的執行,即:

show (Atomic m x) = "Atomic " ++ show m ++ " " ++ show x 
       -      ---------------- 

在這裏您提供匹配多個字段的模式,但Atomic只有一個領域。你應該改變的執行情況如下:

show (Atomic m) = "Atomic " ++ show m 

如果刪除Show m約束,你會看到一個更有用的錯誤消息:

Could not deduce (Show (m (Thread m r))) 
    arising from a use of ‘show’ 
from the context (Show r) 
    bound by the instance declaration at … 
In the second argument of ‘(++)’, namely ‘show m’ 
In the expression: "Atomic " ++ show m 
In an equation for ‘show’: show (Atomic m) = "Atomic " ++ show m 

這是說你想打電話show在類型m (Thread m r)的值上,但在上下文中沒有該限制。所以,你可以添加:

instance (Show (m (Thread m r)), Show r) => Show (Thread m r) where 
      --------------------- 

這不是「標準」哈斯克爾,所以GHC開始提示擴展,允許它:

Non type-variable argument in the constraint: Show (m a) 
(Use FlexibleContexts to permit this) 
In the context: (Show (m a), Show r) 
While checking an instance declaration 
In the instance declaration for ‘Show (Thread m r)’ 

讓我們嘗試加入-XFlexibleContexts(在命令行上ghci … -XFlexibleContexts,在與:set -XFlexibleContexts的會話,或與{-# LANGUAGE FlexibleContexts #-}的源文件),因爲它實際上是一個非常良性的擴展。現在,我們得到一個不同的錯誤:

Variable ‘a’ occurs more often than in the instance head 
    in the constraint: Show (m a) 
(Use UndecidableInstances to permit this) 
In the instance declaration for ‘Show (Thread m r)’ 

我們可以添加-XUndecidableInstances - 所有這意味着,你正在寫一個類型級計算是GHC不能證明將暫停。有時候這是不可取的,但在這種情況下,這很好,因爲我們知道實例解析會找到可接受的Show實例或失敗。現在,編譯器接受它,我們可以嘗試我們的Show例如,假設有像m ~ []r ~ Int簡單的東西:

> Atomic [Atomic [Return 1, Return 2]] :: Thread [] Int 
Atomic [Atomic [Return 1,Return 2]] 

但是請注意,當你設置m到沒有按」一類的構造函數,這將無法正常工作噸有任何Show情況下,如IO

> Atomic (return (Atomic (return (Return 1) >> return (Return 2)))) :: Thread IO Int 

No instance for (Show (IO (Thread IO Int))) 
    arising from a use of ‘print’ 
In a stmt of an interactive GHCi command: print it 

此外,您也可能會注意到你缺少一些括號中的Show實例的結果:

> Atomic (Right (Atomic (Left "asdf"))) :: Thread (Either String) Int 
Atomic Right Atomic Left "asdf" 

這是一個簡單的解決方案,我會留給你。

這將使您的實例的Free從文章數據類型,如Toy上班,順便:

> Atomic (Free (Output "foo" (Pure (Return "bar")))) 
Atomic (Free (Output "foo" (Pure Return ("bar")))) 
4

根據Thread m r = Action (m (Thread m r) | …,m是接受一種類型並返回另一種類型的東西。我們稱m爲類型構造函數或類型* -> *。類似的東西又如Maybe

data Maybe a = Just a | Nothing 

Maybe它自己是不是一個類型。你必須爲它提供另一種類型,例如Maybe IntMaybe String

現在Show預計類型*。但Thread需要* -> *。因此,GHC放棄。由於Show m出現在Thread m r之前,因此GHC認爲它的種類是*,它不適用於Action (m (Thread m r)),因爲這需要一個類型構造函數(* -> *)。

順便說一句,這個問題是爲什麼在某些軟件包中存在像Show1這樣的類。你可以採取的是和然後寫你Show例如:

instance (Show1 m, Show r) => Show (Thread m r) where 
    show (Atomic x) = "Atomic " ++ show1 x 
    show (Return x) = "Return " ++ show x 

或者你也可以鑽研undecidable instances的境界,說Thread m r可以證明,如果m (Thread m r)可以證明:

{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE UndecidableInstances #-} 

instance (Show r, Show (m (Thread m r))) => Show (Thread m r) where 
    show (Atomic x) = "Atomic " ++ show x 
    show (Return x) = "Return " ++ show x 
+0

這是一個很好的答案,我甚至在看Show的兩個實例,並想知道Show1類是否有相關性。這絕對是值得接受的答案,但Jon Purdy的這個問題包含了很多細節,我不能將他標記爲已接受。只是想讓你知道這個答案也有很多價值,並且感謝你提供這個答案。 – user4301448

相關問題