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 - a
abs :: Num a => a -> a
。絶対値関数は、常に同じ大きさの非負の結果を与えますabs (-a) ≡ abs a abs (abs a) ≡ abs a
abs 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 instances
Haskell 2010レポートの6.4.4節では、有効な
Num
インスタンスを保持するために、この最後の等価性が明示的に必要であることに注意してください。
いくつかのライブラリ、特に線形とhmatrixは、 Num
クラスが何を対象としているかについて、はるかに漠然とした理解を持っています。 算術演算子を過負荷にする方法と同じように扱います。これは+
と-
では非常に簡単ですが、他の方法では既に*
などで面倒です。たとえば、 *
は行列の乗算または要素ごとの乗算を意味する必要がありますか?
このような非数値インスタンスを定義することは、おそらく悪い考えです。 VectorSpace
などの専用クラスを検討してください。
† 特に、符号なしの型の "ネガティブ"は大きな陽性にラップされます(-4 :: Word32) == 4294967292
例(-4 :: Word32) == 4294967292
。
• これは広く実現されていません 。ベクタータイプには実際のサブセットがありません。そのようなタイプの論争的なNum
インスタンスは、一般的にabs
とsignum
要素的に定義します。これは、数学的に言えば実際には意味をなさないものです。