7

以下のコードを検討してください。

{-# LANGUAGE MultiParamTypeClasses,FlexibleInstances,FunctionalDependencies,UndecidableInstances,FlexibleContexts  #-}
class Foo a c | a -> c
instance Foo Int Float 
f :: (Foo Int a) => Int -> a 
f = undefined

ghciで推定されたタイプのfを見ると

> :t f
> f :: Int -> Float

次のコードを追加すると

g :: Int -> Float 
g = undefined 

h :: (Foo Int a) => Int -> a 
h = g

エラーが発生します

Could not deduce (a ~ Float)

ここで何が起こったのか理解できませんか?制限により、のタイプは、推定されたタイプのに示されているようにFoo Int a制限されているはずです。hInt -> Floatf

インスタンスを解決する前に型の統合が行われているためですか?

[アップデート]

カフェのメーリングリストでダン・ドエルが説明

答えは、fundepの実装と型族の違いはローカル制約情報であると私は信じています。Fundepsはローカル伝播を行いません。

したがって、最初の定義では、ローカルで' Int -> a'を提供しました。これは、GHCに受け入れられます。次に、関数の外部で' (Foo Int a) => Int -> a'が実際にであることがわかりInt -> Floatます。

2番目の定義では、''を与えようとしていますが、GHCは、それを決定するために使用しない制約' 'をInt -> Float''に提供する必要があることをローカルでしか認識していません。Int -> aFoo Int aa ~ Float

これはfundepsに固有のものではありません。ローカル制約ルールを持つバージョンのfundepsを作成することができます(新しいタイプのファミリーのものに変換することで簡単にできます)。しかし、その違いは、重複するインスタンスが型族ではなく、fundepsでサポートされている理由でもあります。しかし、私は今はそれには入りません。

それが何を意味するのかまだわかりません。それで、まだより理解しやすい答えを探しています。

4

1 に答える 1

4

この問題を回避するには、代わりに型族を使用できます。

{-# LANGUAGE TypeFamilies, FlexibleContexts #-}

class Fooo a where
  type Out a :: *

instance Fooo Int where
  type Out Int = Float
  
ff :: (Foo Int) => Int -> (Out Int)
ff = undefined

gg :: Int -> Float
gg= undefined

hh :: (Foo Int) => Int -> (Out Int)
hh = gg

型族はいいです。可能な場合は型族を使用してください!


あなたのエラーは、ghcがクラス定義とインスタンスf :: Int -> Floatから推測できるためだと思いますが、から推測することはできません。f :: Foo Int a => Int -> ag :: Foo Int a => Int -> ag:: Int -> Float

制約を関数の秘密の辞書パラメーターと考えると便利です。fには固有の、しかし制約されたポリモーフィズムがあり、gにはありません。

ghciは、定義しようとした場合と本質的にまったく同じエラーメッセージを表示していることに注意してください。

j :: Int -> Float
j = undefined

k :: Eq a => Int -> a
k = j

kこれが機能しないことは明らかです。これは、2番目の引数で逆に多形である必要があることがわかっているためです。ghcはタイプInt -> aをと一致させようとして失敗し、インスタンスが存在する場合でも、コンテキストからInt -> Float推測できないと文句を言います。あなたの例では、のインスタンスがあってもコンテキストから推測できないと言っています。の可能なタイプは1つだけであると推測できますが、Fooのクラスとインスタンス作成することで、関係を定義し、それが関数従属性であると主張しました。これは関数を定義することと同じではありません(これが型族が問題を解決する理由です-それは関数を定義します)。a ~ FloatEq aEq Floata ~ FloatFoo Int aFoo Int Floata

ghcはあなたが書くときにも文句を言う

aconst :: (Foo Int a) => a
aconst = 0.0

あるいは

anotherconst :: (Foo Int a) => a
anotherconst = 0.0::Float

それはあなたがそれを与えた特定のタイプ(または)とそれが望んでいた制約された多形性 と一致することができないので常に。aFractional aFloat

あなたが欲しい

forall a.Foo Int a

と同じタイプFloatですが、そうではありません。を満たすタイプは1つしかないforall a.Foo Int aのでFloat、ghciはf::forall a.(Foo Int a)=>a->Float(Fooの辞書を使用して)それを取得して推測できますが、ghciが取得してそれに気付くことf::Int->Floatが期待されますが、Floatの辞書はなく、タイプであり、型クラス。ghciは一方の方法でそれを行うことができますが、もう一方の方法ではできません。Floatforall a.Foo Int a

ローカル情報に関するDanのポイントは、ghcはの定義と、書き換え可能なFoo推論のために作成したインスタンスの両方を使用する必要があること、およびコンパイルのこの時点では、ghcはグローバル情報を使用していないことです。マッチ。私のポイントは、パターンに一致するがパターンに一致しないという意味で、一致するが一致しないということです。Floatforall a.(Foo Int a)Floatforall a.(Foo Int a)forall a.(Foo Int a)Float"this"(x:xs)(x:xs)"this"

于 2012-09-13T17:37:58.227 に答える