答えて
type
は、タイプ同義語をと宣言します。タイプシノニムは、既存のタイプの新しい名前です。たとえば、これはin the standard libraryを定義する方法String
です:
type String = [Char]
String
がChar
秒のリストについては、別の名前です。コンパイル時にGHCはプログラム内のString
のすべての用途を[Char]
に置き換えます。
明らかであるように、String
は、文字通りのリストChar
です。単なるエイリアスです。あなたはString
値にすべての標準リスト関数を使用することができます。
-- length :: [a] -> Int
ghci> length "haskell"
7
-- reverse :: [a] -> [a]
ghci> reverse "functional"
"lanoitcnuf"
data
は型シノニムとは異なり、他のタイプとは異なり、新しいデータ型を、宣言しています。データ型には、タイプの可能なケースを定義するコンストラクタがあります。例えば、これは、in the standard libraryを定義する方法Bool
ある:
data Bool = False | True
Bool
値がTrue
又はFalse
のいずれかであり得ます。データ型はのパターンマッチングをサポートしているため、データ型の値に対してランタイムケース分析を実行できます。
yesno :: Bool -> String
yesno True = "yes"
yesno False = "no"
data
タイプは、(Bool
と同様に)複数のコンストラクタを持つことができる他の種類によってパラメータ化することができ、それらの内部の他のタイプを含むことができ、及び再帰それ自体を参照することができます。これを実証する例外のモデルがあります。 Error a
には、タイプa
のエラーメッセージが含まれており、原因となったエラーが含まれている可能性があります。
data Error a = Error { value :: a, cause :: Maybe Error }
type ErrorWithMessage = Error String
myError1, myError2 :: ErrorWithMessage
myError1 = Error "woops" Nothing
myError2 = Error "myError1 was thrown" (Just myError1)
それはdata
は、システム内の他のタイプから離れている新しいタイプを宣言することを認識することが重要です。String
がdata
タイプとしてのリスト(タイプ同義語ではなく)Char
であると宣言されていた場合、それに対してリスト関数を使用することはできません。 newtype
:
data String = MkString [Char]
myString = MkString ['h', 'e', 'l', 'l', 'o']
myReversedString = reverse myString -- type error
型宣言の1つの以上様々なものがあります。これは、data
宣言のように機能します.1つのフィールドを持つ単一のコンストラクタに限定されている点を除いて、他の型とは別の新しいデータ型を導入し、パターンマッチングが可能です。つまり、newtype
は、既存のタイプをラップするタイプのdata
です。
重要な違いは、newtype
のコストです:コンパイラはnewtype
はそれがラップタイプと同じ方法で表現されていることをお約束します。 newtype
の梱包や開梱にはランタイムコストはかかりません。これにより、値(構造ではなく)の値を区別するためにnewtype
が役に立ちます。
newtype
は、タイプクラスとよく対話します。たとえば、要素(mappend
)と特殊な「空」要素(mempty
)を結合する方法の種類のクラスMonoid
を考えてみましょう。 Int
はMonoid
のように多くの方法で作ることができ、0の加算と1の乗算を含めることができます。Monoid
のインスタンスに使用するものをどのように選択することができますか?Int
?設定を表現しない方が良いですし、newtype
を使用すると、ランタイムコストなしでどちらかの使用を有効にすることができます。 the standard libraryを言い換え:ちょうどlet
よう
-- introduce a type Sum with a constructor Sum which wraps an Int, and an extractor getSum which gives you back the Int
newtype Sum = Sum { getSum :: Int }
instance Monoid Sum where
(Sum x) `mappend` (Sum y) = Sum (x + y)
mempty = Sum 0
newtype Product = Product { getProduct :: Int }
instance Monoid Product where
(Product x) `mappend` (Product y) = Product (x * y)
mempty = Product 1
data
を使用すると、新しいデータ型を作成し、そのためにコンストラクタを宣言します。あなたがの値を渡すことができtype
場合
type MyChar = Char
:type
で
data NewData = NewDataConstructor
は、あなただけの別名を定義しますMyChar
タイプはChar
を期待して機能しますが、その逆もありますが、data MyChar = MyChar Char
ではこれを行うことはできません。
type
作品:それはあなたが何かに再利用可能な名前を付けることができますが、あなたは定義をインライン化していたかのように、その何かが常に動作します。だから、
type ℝ = Double
f :: ℝ -> ℝ -> ℝ
f x y = let x2 = x^2
in x2 + y
は、のように
f' :: Double -> Double -> Double
f' x y = x^2 + y
とまったく同じように動作:あなたはどこでもあなたのコード内のf'
およびその逆でf
を置き換えることができます。何も変わることはありません。
OTOHの場合、data
とnewtype
の両方が、不透明な抽象化を作成します。 OOのクラスコンストラクタに似ています:がという単純な数値で実装されていても、必ずしものように振る舞いません。のような数です。ここで例えば、
newtype Logscaledℝ = LogScaledℝ { getLogscaled :: Double }
instance Num LogScaledℝ where
LogScaledℝ a + LogScaledℝ b = LogScaledℝ $ a*b
LogScaledℝ a - LogScaledℝ b = LogScaledℝ $ a/b
LogScaledℝ a * LogScaledℝ b = LogScaledℝ $ a**b
、Logscaledℝ
はまだちょうどDouble
数のデータ単位ですが、それは明らかDouble
は異なる動作をします。