Haskell Language
型クラス
サーチ…
前書き
Haskellの型板は、その型の定義とは別の型に関連付けられた振る舞いを定義する手段です。例えば、Javaでは、型の定義の一部、つまりインターフェース、抽象クラスまたは具象クラスの中で振る舞いを定義しますが、Haskellはこれらの2つのことを別々に保ちます。
Haskellのbaseパッケージには既に定義されているいくつかの型式があります。これらの関係については、後述の「備考」セクションで説明します。
備考
Typeclassopediaの記事の次の図は、Haskellのさまざまなタイプ間の関係を示しています。
たぶんFunctorクラス
Haskellでは、データ型は関数のような引数を持つことができます。例えば、 Maybe型を取る。
Maybe 、失敗の考え方やその可能性を表現するのに非常に便利な型です。言い換えれば、計算が失敗する可能性がある場合は、 Maybe型を使用します。 Maybe 、他のタイプのラッパーのように動作し、追加の機能を与えます。
その実際の宣言はかなりシンプルです。
Maybe a = Just a | Nothing
これは、 Maybeが成功を表すJustと失敗を表すNothing 2つの形式で提供されることを示しています。 Maybeのタイプを決定する1つの引数を取るJustで、 Nothing取りません。たとえば、 Just "foo"という値は、 Maybeの追加機能でラップされた文字列型のMaybe String型を持ちます。値Nothing入力していないMaybe a場所を任意のタイプにすることができます。 a
追加機能を与えるために型をラップするというこのアイデアは非常に有用なものであり、単なるMaybe以上にも適用可能です。他の例には、それぞれ異なる機能を提供するEither 、 IO 、およびListタイプがあります。しかし、これらのラッパー・タイプすべてに共通のアクションと能力があります。これらの中で最も顕著なものは、カプセル化された値を変更する機能です。
この種のタイプは、値を持つことができるボックスと考えるのが一般的です。異なるボックスには異なる値が設定され、異なるものがありますが、その中のコンテンツにアクセスすることができないと便利なものはありません。
このアイデアをカプセル化するために、HaskellにはFunctorという標準的なtypeclassが付属しています。それは以下のように定義される。
class Functor f where
fmap :: (a -> b) -> f a -> f b
見て分かるように、クラスは2つの引数の単一の関数fmap持っています。最初の引数は、一種の関数であり、別の、 a b 。二番目の引数は、型の値を含むファンクタ(ラッパ型)でありa 。 b型の値を含むファンクタ(ラッパー型)を返します。
簡単に言えば、 fmapは関数を取り、ファンクタの内部の値に適用されます。これは、型がFunctorクラスのメンバであるために必要な唯一の関数ですが、非常に便利です。より具体的なアプリケーションを持つファンクタで動作する関数は、 ApplicativeとMonadタイプメスにあります。
型継承:Ord型クラス
Haskellはクラス拡張の概念をサポートしています。たとえば、クラスOrdはEqすべての操作を継承しますが、値の間にOrderingを返すcompare関数もあります。 Ordは、共通の順序比較演算子、およびminメソッドとmaxメソッドも含まれます。
=>表記は、関数シグネチャと同じ意味を持ち、 Ordを実装するために、 Eqを実装するためにa型が必要です。
data Ordering = EQ | LT | GT
class Eq a => Ord a where
compare :: Ord a => a -> a -> Ordering
(<) :: Ord a => a -> a -> Bool
(<=) :: Ord a => a -> a -> Bool
(>) :: Ord a => a -> a -> Bool
(>=) :: Ord a => a -> a -> Bool
min :: Ord a => a -> a -> a
max :: Ord a => a -> a -> a
compare続くすべてのメソッドは、以下の方法でcompareできます。
x < y = compare x y == LT
x <= y = x < y || x == y -- Note the use of (==) inherited from Eq
x > y = not (x <= y)
x >= y = not (x < y)
min x y = case compare x y of
EQ -> x
LT -> x
GT -> y
max x y = case compare x y of
EQ -> x
LT -> y
GT -> x
それ自身で拡張する型クラスOrdは、少なくともcompareメソッドまたは(<=)メソッド自体を実装する必要があります。これは、指向の継承ラティスを構築します。
式
Preludeの関数やIO以外のすべての基本データ型( Int 、 String 、 Eq a => [a] )は、 Eq 。型がEqインスタンス化する場合、 値または構造の等価性について2つの値を比較する方法を知っていることを意味します。
> 3 == 2
False
> 3 == 3
True
必要なメソッド
-
(==) :: Eq a => a -> a -> Booleanまたは(/=) :: Eq a => a -> a -> Boolean(どちらか一方のみが実装されている場合、定義されたもの)
定義する
-
(==) :: Eq a => a -> a -> Boolean -
(/=) :: Eq a => a -> a -> Boolean
ダイレクトスーパークラス
無し
注目すべきサブクラス
Ord
インスタンスタイプOrd含み、例えば、 Int 、 String 、および[a]タイプ用がありますa Ord aインスタンス)。タイプがOrdインスタンス化する場合、そのタイプの値の「自然な」順序付けを知っていることを意味します。タイプの「自然な」順序付けには多くの可能性のある選択肢があり、 Ordを優先させることに注意してください。
Ordは標準(<=) 、 (<) 、 (>) 、 (>=)演算子を提供しますが、興味深いことに、それらをすべてカスタム代数データ型
data Ordering = LT | EQ | GT
compare :: Ord a => a -> a -> Ordering
必要なメソッド
-
compare :: Ord a => a -> a -> Orderingまたは(<=) :: Ord a => a -> a -> Boolean(標準のデフォルトcompareメソッドはその実装で(<=))
定義する
-
compare :: Ord a => a -> a -> Ordering -
(<=) :: Ord a => a -> a -> Boolean -
(<) :: Ord a => a -> a -> Boolean -
(>=) :: Ord a => a -> a -> Boolean -
(>) :: Ord a => a -> a -> Boolean -
min :: Ord a => a -> a -> a -
max :: Ord a => a -> a -> a
ダイレクトスーパークラス
モノイド
Monoidのインスタンス化Monoidには、 Monoid戻り値などを含むリスト、数値、関数が含まれています。インスタンス化するMonoidタイプが連想バイナリ操作(サポートしなければならないmappendまたは(<>)その値を組み合わせ)、および特別な「ゼロ」値(有するmemptyそれに値を組み合わせることは、その値を変更しないように)。
mempty <> x == x
x <> mempty == x
x <> (y <> z) == (x <> y) <> z
直感的に、 Monoid型は値を一緒に付加することをサポートするという点で "リストのような"ものです。あるいは、 Monoid型は、順序については考慮するが、グループ化については考慮しない値の列として考えることができます。たとえば、バイナリツリーはMonoidですが、 Monoid演算を使用すると、その分岐構造を見ることはできません。値のトラバーサルしか見ることができません( FoldableおよびTraversable参照)。
必要なメソッド
-
mempty :: Monoid m => m -
mappend :: Monoid m => m -> m -> m
ダイレクトスーパークラス
無し
Num
数値型の最も一般的なクラス、より正確にはリングのためのものです 。つまり、通常の意味で加減算したり乗算することができますが、必ずしも分割する必要はありません。
このクラスには整数型( Int 、 Integer 、 Word32など)と小数型( Double 、 Rational 、複素数など)の両方が含まれます。有限型の場合、セマンティクスは一般にモジュラ算術 、つまりオーバーフローとアンダーフローを伴う†と理解されます。
数値クラスのルールは、 モナドまたはモンゴイドの法則、または同等性の比較のための法則よりもずっと厳密に従わないことに注意してください。特に、浮動小数点数は概しておおまかな意味でのみ法則に従います。
メソッド
fromInteger :: Num a => Integer -> a。整数を一般的な数値型に変換します(必要に応じて範囲を囲みます)。 Haskellの数値リテラルは、周囲の一般的な変換を伴う単相性Integerリテラルとして理解できるので、IntコンテキストとComplex Double設定の両方でリテラル5を使用できます。(+) :: Num a => a -> a -> a。標準的な追加は、一般的に連想的および可換的なものとして理解されます。a + (b + c) ≡ (a + b) + c a + b ≡ b + a(-) :: Num a => a -> a -> a。加算の逆である減算:(a - b) + b ≡ (a + b) - b ≡ a(*) :: Num a => a -> a -> a。乗算、加算を超えて分布する連想演算:a * (b * c) ≡ (a * b) * c a * (b + c) ≡ a * b + a * c最も一般的なインスタンスの場合、乗算も可換ですが、これは必須条件ではありません。
negate :: Num a => a -> a。単項否定演算子の完全な名前。-1は、negate 1構文砂糖です。-a ≡ negate a ≡ 0 - aabs :: Num a => a -> a。絶対値関数は、常に同じ大きさの非負の結果を与えますabs (-a) ≡ abs a abs (abs a) ≡ abs aabs a ≡ 0た場合にのみ起こるはずa ≡ 0実数型では、負でないことを意味します。常に
abs a >= 0です。複合体などの型は明確な順序付けを持っていませんが、absの結果は常に真の部分集合‡にあるべきです(つまり、否定を伴わない単一の数値リテラルとしても書ける数を与える)。signum :: Num a => a -> a。 sign関数は、名前に応じて、引数の符号に応じて-1または1だけを-1ます。実際には、それは非ゼロの実数に対してのみ当てはまります。一般的にsignumは正規化関数としてよく理解される:abs (signum a) ≡ 1 -- unless a≡0 signum a * abs a ≡ a -- This is required to be true for all Num instancesHaskell 2010レポートの6.4.4節では、有効な
Numインスタンスを保持するために、この最後の等価性が明示的に必要であることに注意してください。
いくつかのライブラリ、特に線形とhmatrixは、 Numクラスが何を対象としているかについて、はるかに漠然とした理解を持っています。 算術演算子を過負荷にする方法と同じように扱います。これは+と-では非常に簡単ですが、他の方法では既に*などで面倒です。たとえば、 *は行列の乗算または要素ごとの乗算を意味する必要がありますか?
このような非数値インスタンスを定義することは、おそらく悪い考えです。 VectorSpaceなどの専用クラスを検討してください。
† 特に、符号なしの型の "ネガティブ"は大きな陽性にラップされます(-4 :: Word32) == 4294967292例(-4 :: Word32) == 4294967292 。
• これは広く実現されていません 。ベクタータイプには実際のサブセットがありません。そのようなタイプの論争的なNumインスタンスは、一般的にabsとsignum要素的に定義します。これは、数学的に言えば実際には意味をなさないものです。
