2017-04-21 5 views
3

私はGabriel Gonzaleの優れたblog post on Free Monadsを使って作業しています。データ型のShowインスタンスからのエラー

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

そして、私のショーインスタンスは次のとおりです:私は、次のタイプのために表示するインスタンスを作成し、GHCiの中でそれをいじってみたかった私の理解を支援するために、(ブログ記事から)タイプがある

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実装の外観はどうですか?私はいずれかのインスタンスを見てみましたが、何が起こっていたのか理解できませんでした。

+0

は、 'M'は 'IO'または' Maybe'のような* *型のコンストラクタです。完全な型( 'IO()'や 'Maybe String'など)ではありません。しかし 'm(Thread m r)'は完全な型です。 – immibis

+0

さらに、データ・コンストラクタ 'Atomic'は2つではなく1つの引数をとります。 – immibis

答えて

4

あなたのShowインスタンスを書くそのとを採用できあなたが受け取るエラーは、種類エラーです。種類は型の「型」です。型が値を分類するのと同じように、型は型を分類します。ハスケルが型から値を推論するのと同じように、型から型を推定します。特定のタイプ(例えば、1 :: Int)があり、あるタイプが特定の種類(例えば、Int :: *)であることを示すために、同じシンボル::を使用します。

エラーメッセージに示された2種類があり:*は、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の定義では、の引数がAtomicコンストラクタの定義に渡されます。このコンストラクタのフィールドの型は、m (Thread m r)です。

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

ので、そのm :: * -> *、不一致を意味します:Threadのようなものを見てください。あなたが複数のフィールドに一致するパターンを提供してきました。ここ

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

が、Atomicは一つだけのフィールドがあります。

次のエラーはつまり、あなたのShowインスタンスの実装です。あなたは、次の実装を変更する必要があります

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がそれを許可する拡張子を示唆して開始しますので、これは、「標準」はHaskellではありません。

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)’ 

はのがで、ghci … -XFlexibleContextsとコマンドライン上(-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)’ 

私たちは、-allこの手段-XUndecidableInstancesを追加することができますが、GHCは証明できないタイプレベルの計算が停止します書いているということです。場合によってはこれは望ましくありませんが、この場合はインスタンスの解決が受け入れ可能なShowインスタンスを見つけるか、失敗することがわかっています。今、コンパイラはそれを受け入れ、そして我々はShowインスタンスを試すことができ、m ~ []r ~ Intのような単純な何かを言う:

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

しかし、あなたは「doesnの型コンストラクタにmを設定すると、これは動作しませんのでご注意しますtは任意の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 IntまたはMaybe String

Showには、種類が*のタイプが必要です。しかしThreadには* -> *が必要です。したがって、GHCはあきらめます。 Show mThread m rの前に来るので、GHCは種類が*だと考えています。Action (m (Thread m r))では動作しません。タイプコンストラクタ(* -> *)が必要です。

この問題は、一部のパッケージにはShow1などのクラスが存在する理由です。

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

それとも、undecidable instancesの分野を掘り下げとm (Thread m r)を示すことができるかどう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クラスに関連性があるかどうか疑問に思っていました。これは絶対に受け入れられる答えに値するが、ジョン・パーディーは、私が受け入れられたとしてマークすることができなかった問題のそのような大きな故障を含んでいる。この回答には多くの価値があることを知りたかっただけであり、それを提供してくれてありがとう。 – user4301448

関連する問題