3

Haskell の次の単純なコードに問題があります。

import Prelude hiding (cycle).

class ICycle a where
    cycle :: a -> a

instance ICycle [a] where
    cycle [] = []
    cycle (x:xs) = xs ++ [x]

instance ICycle Bool where
    cycle True = False
    cycle False = True

instance Num a => ICycle a where
    cycle n = n+1

main = do
    print $ cycle $ [1,2,3]
    print $ cycle $ True
    print $ cycle $ 42

ここで、最初の 2 つのインスタンス宣言は期待どおりに機能しますが、3 番目のインスタンス宣言は、フラグの組み合わせに応じてさまざまな種類のエラーを引き起こします。

Num aよりも短くないことがわかっているICycle aため、コンパイラは型チェックを終了できません。例では、右辺をより大きな用語にするか、最初に目的のクラスを他のクラスのサブクラスとして宣言することで、これを回避できることを確認しました。ここでは逆に、既存のクラスを新しいクラスのサブクラスとして宣言したいと考えています。

このような型クラスの使用に異議があるのだろうか。または、自然な解決策がある場合。

4

2 に答える 2

3

newtypeこの特定の例では、インスタンスをラップするためにa を使用するのが最善だと思います。

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Prelude hiding (cycle)

class ICycle a where
    cycle :: a -> a

newtype Succ a = Succ { runSucc :: a }
  deriving (Num, Eq, Ord, Bounded, Enum, Show, Read)

newtype Pred a = Pred { runPred :: a }
  deriving (Num, Eq, Ord, Bounded, Enum, Show, Read)

instance Enum a => ICycle (Succ a) where
    cycle = Succ . succ . runSucc

instance Enum a => ICycle (Pred a) where
    cycle = Pred . pred . runPred

main = do
    print $ cycle $ (42 :: Succ Int)
    print $ cycle $ (42 :: Pred Int)

succ、pred、2 倍、2 分の 1 など、数値を循環させる方法は複数あります。インスタンスに a を使用する利点newtype(質問で指摘したように、RHSを「大きく」する)は、それらすべてを使用できることです。

標準ライブラリは、ProductSumfor で同じトリックを行いMonoidます。

別の見方をすると、 の新しいスーパークラスを定義して、Numのすべてのインスタンスにデフォルトの実装を追加する ことができた場合Num、それらのすべての実装からその選択を取り除くことになります。おそらく意味をなさない方法で。

于 2015-04-29T01:29:26.040 に答える
2

Haskell レポート 2010 の第 4 章Declarations and Bindingsによると、インスタンスとして定義されるものは型コンストラクターである必要があります。したがって、

instance Num a => ICycle a where
    ...

aは型コンストラクターではなく型変数であるため、無効です。

したがって、残念ながら、それを行う有効な方法はタイプごとです。言わなければならない:

instance ICycle Int where
    ...

instance ICycle Double where
    ...

等々。

于 2015-04-29T02:36:05.173 に答える