6
class Listy a b where
   fromList :: [b] -> a
   toList :: a -> [b]
   lifted :: ([b] -> [b]) -> (a -> a) 
   lifted f = fromList . f . toList

data MyString = MyString { getString :: String } deriving Show

instance Listy MyString Char where
  toList = getString
  fromList = MyString

今、私は例えば書く必要がありますlifted (reverse::(String -> String)) (MyString "Foobar")。型署名の必要性を回避するためのトリックはありますか?

4

2 に答える 2

11

本質的に問題は、 の型を設定しても、aの型が何であるかがコンパイラに通知されないことbです。クラスのインスタンスは 1 つしかないため ( isとis )、誰でもいつでも新しいインスタンスを追加できると思うかもしれません。したがって、インスタンスが1 つしかないという事実は、コンパイラが必要な型を決定するのに役立ちません。aMyStringbChar

これに対する解決策は、Functional Dependencies または Type Families を使用することです。後者は新しいソリューションであり、最終的には前者を「置き換える」ことを意図していますが、現時点では両方とも完全にサポートされています。FDがなくなるかどうかはまだわかりません。とにかく、FDの場合:

class Listy a b | a -> b where ...

基本的に、これは「それぞれに対して1つのクラスインスタンスしか存在できない」ことを示していますa。つまり、 がわかれば、aいつでも を決定できますb。(しかし、その逆ではありません。) クラスの残りの部分は、以前と同じように見えます。

代替手段は TF です。

class Listy a where
  type Element a :: *
  ...

instance Listy MyString where
  type Element MyString = Char
  ...

2 番目のタイプが と呼ばれる代わりに、 と呼ばれるようbになりElement aました。この単語Elementは、listy 型を受け取り、対応する要素型を返すクラス メソッドのように機能します。その後、次のことができます

instance Listy ByteString where
  type Element ByteString = Word8
  ...

instance Listy [x] where
  type Element [x] = x
  ...

instance Ord x => Listy (Set x) where
  type Element (Set x) = x
  ...

等々。(Listy必ずしも上記のすべての型に意味があるわけではありません。これらは、クラスを定義する方法の単なる例です。)

于 2012-10-17T08:09:15.553 に答える
5

-XFunctionalDependencies を試すことができます

class Listy a b | a -> b where
   fromList :: [b] -> a
   toList :: a -> [b]
   lifted :: ([b] -> [b]) -> (a -> a) 
   lifted f = fromList . f . toList
于 2012-10-17T07:32:55.997 に答える