2015-10-24 8 views

答えて

14

typeは、タイプ同義語をと宣言します。タイプシノニムは、既存のタイプの新しい名前です。たとえば、これはin the standard libraryを定義する方法Stringです:

type String = [Char] 

StringChar秒のリストについては、別の名前です。コンパイル時に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は、システム内の他のタイプから離れている新しいタイプを宣言することを認識することが重要です。Stringdataタイプとしてのリスト(タイプ同義語ではなく)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を考えてみましょう。 IntMonoidのように多くの方法で作ることができ、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 
1

dataを使用すると、新しいデータ型を作成し、そのためにコンストラクタを宣言します。あなたがの値を渡すことができtype場合

type MyChar = Char 

type

data NewData = NewDataConstructor 

は、あなただけの別名を定義しますMyCharタイプはCharを期待して機能しますが、その逆もありますが、data MyChar = MyChar Charではこれを行うことはできません。

2

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の場合、datanewtypeの両方が、不透明な抽象化を作成します。 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は異なる動作をします。