16

私は現在、Learn you a Haskell第8章にいますが、型クラスのセクションに到達しました。上記のセクションで、作成者は、さまざまな型をクラスのインスタンスにする方法の例を示します(たとえば、カスタム型など)。これを見て、私は(楽しみと練習のために)型のインスタンスを実装してみることにしました。もちろん、このすべてを無視します。FunctorMaybeTreeData.SetData.Set.map

実際のインスタンス自体は非常に単純で、次のように記述しました。

instance Functor Set.Set where
  fmap f empty = Set.empty
  fmap f s = Set.fromList $ map f (Set.elems s)  

しかし、私はたまたまこの関数を使用しているので、コンパイラエラーで説明されているように、で使用fromListされる型を要求するクラス制約が発生します。SetOrd

Error occurred
ERROR line 4 - Cannot justify constraints in instance member binding
*** Expression    : fmap
*** Type          : Functor Set => (a -> b) -> Set a -> Set b
*** Given context : Functor Set
*** Constraints   : Ord b

参照:実例

インスタンスに制約を設定するか、型シグネチャをに追加しようとしましたfmapが、どちらも成功しませんでした(どちらもコンパイラエラーでした)。

このような状況で、どのように制約を満たし、満たすことができますか?可能な方法はありますか?

前もって感謝します!:)

4

3 に答える 3

15

残念ながら、標準クラスでこれを行う簡単な方法はありません。これが、デフォルトでインスタンスが付属していないFunctor理由です。インスタンスを作成することはできません。SetFunctor

これは問題の1つであり、いくつかの解決策が提案されています(たとえば、Functorクラスを別の方法で定義する)が、これを最適に処理する方法についてコンセンサスがあるかどうかはわかりません。

1つのアプローチは、制約の種類Functorを使用してクラスを書き直し、新しいクラスのインスタンスが持つ可能性のある追加の制約を具体化することだと思います。これにより、クラスの型を含める必要があることを指定できます。FunctorSetOrd

別のアプローチでは、マルチパラメータクラスのみを使用します。Monadクラスでこれを行うことについての記事しか見つかりませんでしたが、顔のSet一部を作成することは、それをMonadの一部にすることと同じ問題に直面しFunctorます。それは制限付きモナドと呼ばれます。

ここでマルチパラメータクラスを使用する基本的な要点は、次のようになります。

class Functor' f a b where
  fmap' :: (a -> b) -> f a -> f b

instance (Ord a, Ord b) => Functor' Data.Set.Set a b where
  fmap' = Data.Set.map

基本的に、ここで行っているのは、クラスの一部でもある型を作成することだけです。Setこれにより、そのクラスのインスタンスを作成するときに、これらのタイプが何であるかを制限できます。

このバージョンには、とのFunctor2つの拡張子が必要です。(クラスを定義できるようにするには最初の拡張機能が必要であり、インスタンスを定義できるようにするには2番目の拡張機能が必要です。)MultiParamTypeClassesFlexibleInstancesSet

Haskell:Functorではない(またはTraversableではない)Foldableの例?これについては良い議論があります。

于 2012-10-16T19:38:53.607 に答える
2

不可能だよ。Functorこのクラスの目的は、があればFunctor f => f a、を好きなものに置き換えることができるということaです。クラスは、これまたはそれだけを返すように制限することはできません。その要素が特定の制約を満たす必要があるためSet(実際、これは実装の詳細ではなく、実際にはセットの本質的なプロパティです)、の要件を満たしていませんFunctor

別の回答で述べたように、そのようなクラスを開発する方法はFunctorそのように制約されますが、クラスのユーザーに保証が少なくなるため、実際には別のクラスです(これを使用することはできません)必要なタイプパラメータ)、より広い範囲のタイプに適用できるようになることと引き換えに。つまり、結局のところ、型のプロパティを定義するという古典的なトレードオフです。それを満たす必要のある型が多いほど、満たす必要のある型は少なくなります。

(これが表示されるもう1つの興味深い例はMonadPlusクラスです。特に、すべてのインスタンスに対してインスタンスMonadPlus TC を作成できますMonoid (TC a)が、常に逆方向に移動できるとは限りません。したがって、Monoid (Maybe a)インスタンスはインスタンスとは異なります。MonadPlus Maybe前者は次のことができるためです。制限しますaが、後者はできません。)

于 2012-10-25T15:18:51.587 に答える
0

これは、CoYonedaファンクターを使用して行うことができます。

{-# LANGUAGE GADTs #-}

data CYSet a where
    CYSet :: (Ord a) => Set.Set a -> (a -> b) -> CYSet b

liftCYSet :: (Ord a) => Set.Set a -> CYSet a
liftCYSet s = CYSet s id

lowerCYSet :: (Ord a) => CYSet a -> Set.Set a
lowerCYSet (CYSet s f) = Set.fromList $ map f $ Set.elems s

instance Functor CYSet where
  fmap f (CYSet s g) = CYSet s (f . g)

main = putStrLn . show
  $ lowerCYSet
  $ fmap (\x -> x `mod` 3)
  $ fmap abs
  $ fmap (\x -> x - 5)
  $ liftCYSet $ Set.fromList [1..10]
-- prints "fromList [0,1,2]"
于 2017-11-15T19:03:33.710 に答える