2017-10-23 14 views
1

もっと一般的なコードを書く方法を学ぶために、私は という簡単なArrayクラスを書こうとしました。考え方は、単純な配列操作 だけをArrayクラスが提供する関数で記述し、異なるデータ型をインスタンスとして記述することです。 だから私は配列の異なる表現を書いてみたいです。なぜGHCはFoldableにベクターを「アップキャスト」しないのですか?

最初の試みとして、配列にはいくつかの行と列と折り畳み可能なコンテナがあります。

module Array where 

import qualified Data.Vector as V 
import   Data.Foldable 

class Array arr where 
    aRows :: arr a -> Int 
    aCols :: arr a -> Int 
    aData :: Foldable t => arr a -> t a 

data VectorArray a = VectorArray 
    { vRows :: !Int 
    , vCols :: !Int 
    , vData :: !(V.Vector a)} 

instance Array VectorArray where 
    aRows = vRows 
    aCols = vCols 
    aData = vData 

最後の行はコンパイルされません:

• Couldn't match type ‘t’ with ‘V.Vector’ 
    ‘t’ is a rigid type variable bound by 
    the type signature for: 
     aData :: forall (t :: * -> *) a. Foldable t => VectorArray a -> t a 
    at src/Array.hs:19:5 
    Expected type: VectorArray a -> t a 
    Actual type: VectorArray a -> V.Vector a 
• In the expression: vData 

エラーメッセージの私の解釈:あなたは(プログラマが)私にV.Vector Aを与えるが、私(GHCコンパイラが) がt aたいtは、Foldableクラスのインスタンスである必要があります。

今、V.VectorはFoldableのインスタンスです。だから私は、インスタンスとして Foldableを引数として渡さなければならないすべての関数で、タイプV.Vectorの値を使用できます。 私の質問は:GHCがなぜアップキャストされないのですかV.VectorFoldable?例がありますか? そのようなアップキャストは問題の原因となるでしょうか?

PS:上記のエラーを回避するには、aData関数をArrayクラス から削除し、VectorArrayをFoldableのインスタンスにします。

答えて

8

aDataの種類はどのt、発信者選択し、そのために、それはタイプt aの値を返すことを言います。しかし、実装はVectorしか返しません。 "Upcasting"と呼ぶとVectorFoldable構造体に変換する必要がありますが、これを暗黙のうちに行うことはできません。実際には不可能です:ベクトルが空で、ユーザーがNonEmptyリストを望む場合

3

は、型抜きのインスタンスではありません。 IntNumではありません。 VectorArray FooArrayではありません。 タイプは型クラスのインスタンスである:IntVectorArrayNumあるVectorFoldableで、Arrayです。 Foldableは値に適用できないため、x :: Vector Fooからx :: Foldableまでアップキャストできません。 Haskellにはサブタイプがあります(例えば、mempty :: forall m. Monoid m => m)。Group m(そのようなクラスが存在する場合)はMonoid m,mempty :: forall g. Group g => gも意味しますが、それは少し秘密であり、関係はありません。

あなたは実存タイプたい:構文exists f. Foldable f => f a

class Array arr where 
    aRows :: arr a -> Int 
    aCols :: arr a -> Int 
    aData :: arr a -> (exists f. Foldable f => f a) 

は(やや標準)擬似Haskellのです。つまり、それが何であれ、それはFoldableであり、それにはaが含まれていますが、その包含するタイプが正確にわからないことを意味します。あなたはこれを直接行うことはできませんが、近くに取得することができます:実存のタイプの

data SomeFoldable a = forall f. Foldable f => SomeFoldable (f a) 
-- Can't be a newtype: the Foldable f dictionary needs to be put somewhere 
aData :: Array arr => arr a -> SomeFoldable a 

Haskellの構文はかなりひねくれている、それはそれらを定義するために反対の数量詞、forallを使用していますよう。正当化は、上記のコンストラクタ

SomeFoldable :: forall f. Foldable f => f a -> SomeFoldable a 

特に何であるかf「忘れる」を作成することです。

これらは非常に簡単、ちょうどFoldableArrayのスーパークラスを作るほど素敵ではありません使用:

instance Array VectorArray where 
    aRows = vRows 
    aCols = vCols 
    aData = SomeFoldable . vData 

sum :: (Num a, Array arr) => arr a -> a 
sum arr = case aData arr of 
    SomeFoldable fa -> foldl' (+) 0 fa 
-- Need to use patterns to destructure; let won't work (same for GADTs). 
-- Foldable instance in scope on right side of case 

-- Example: 
instance Array [] where 
    aRows _ = 1 
    aCols = length 
    aData = SomeFoldable 
sum [1,2,3] == 6 
+2

実存的なタイプの議論は良いですが、答えは非常にスタートが、それはOPのように聞こえますコード*はハスケルだけがアップキャストをサポートしていれば動作します。ここでの問題は、Haskellがアップキャストをサポートしていないことではなく、OPがアップキャストしようとしているタイプが、実際に彼らが望むタイプのスーパータイプではないので、ここでアップキャストすることは有効ではないということですからアップキャスト。 (そして、Haskellがサブタイプ化しているのは、より多態型のインスタンスである型だけで、それは "アップキャスト"をサポートしています)。 – Ben

関連する問題