133

Learnyouahaskell.comから Haskell を学んでいます。型コンストラクターとデータ コンストラクターを理解できません。たとえば、次の違いがよくわかりません。

data Car = Car { company :: String  
               , model :: String  
               , year :: Int  
               } deriving (Show) 

この:

data Car a b c = Car { company :: a  
                     , model :: b  
                     , year :: c   
                     } deriving (Show)  

Car1 つ目は、単純に 1 つのコンストラクター ( ) を使用して、タイプ のデータを構築していることを理解していCarます。2つ目はよくわかりません。

また、次のように定義されたデータ型はどのようになりますか。

data Color = Blue | Green | Red

このすべてに当てはまりますか?

私が理解していることから、3 番目の例 ( ) は、 、またはColorの 3 つの状態をとることができる型です。しかし、それは最初の 2 つの例を理解する方法と矛盾します。つまり、型は 1 つの状態にしかならないということですか? さまざまなパラメーターを使用して構築できますか? もしそうなら、2番目の例はどのように当てはまりますか?BlueGreenRedCarCar

基本的に、上記の 3 つのコード例/構成を統一する説明を探しています。

4

6 に答える 6

249

data宣言では、型コンストラクターは等号の左側にあるものです。データ コンストラクターは、等号の右側にあるものです。型が必要な場合は型コンストラクターを使用し、値が必要な場合はデータ コンストラクターを使用します。

データ コンストラクター

簡単にするために、色を表す型の例から始めましょう。

data Colour = Red | Green | Blue

ここには、3 つのデータ コンストラクターがあります。Colourは型であり、 typeGreenの値を含むコンストラクターですColour。同様に、RedBlueはどちらも type の値を構築するコンストラクタですColour。でも、スパイスを効かせることは想像できます!

data Colour = RGB Int Int Int

まだ type しかColourありRGBませんが、値ではありません。3 つの Int を取り、値を返す関数です! RGBタイプを持っています

RGB :: Int -> Int -> Int -> Colour

RGBいくつかの値を引数として取り、それらを使用して新しい値を構築する関数であるデータ コンストラクターです。オブジェクト指向プログラミングを行ったことがある場合は、これを認識する必要があります。OOP では、コンストラクターもいくつかの値を引数として取り、新しい値を返します!

この場合、RGB3 つの値に適用すると、色の値が得られます。

Prelude> RGB 12 92 27
#0c5c1b

データ コンストラクターを適用して、型の値を作成しました。Colourデータ コンストラクターには、変数のように値が含まれるか、他の値を引数として取り、新しいを作成します。以前にプログラミングを行ったことがある場合、この概念はそれほど奇妙ではないはずです。

休憩

sを格納するために二分木を構築したい場合はString、次のようなことを想像できます。

data SBTree = Leaf String
            | Branch String SBTree SBTree

ここに表示されているのは、SBTree2 つのデータ コンストラクターを含む型です。つまり、型の値を構築する2 つの関数 (すなわちLeafと) があります。二分木の仕組みに慣れていない場合は、しばらくお待ちください。二分木がどのように機能するかを実際に知る必要はありません。これが何らかの方法で s を格納することだけです。BranchSBTreeString

また、両方のデータ コンストラクターが引数を取ることもわかりますString。これは、ツリーに格納する文字列です。

しかし!も格納できるようにしたい場合はBool、新しいバイナリ ツリーを作成する必要があります。次のようになります。

data BBTree = Leaf Bool
            | Branch Bool BBTree BBTree

型コンストラクタ

SBTreeとはどちらBBTreeも型コンストラクターです。しかし、明らかな問題があります。それらがどれほど似ているか分かりますか?これは、どこかにパラメーターが本当に必要であることを示しています。

したがって、これを行うことができます:

data BTree a = Leaf a
             | Branch a (BTree a) (BTree a)

ここで、型コンストラクターへのパラメーターとして型変数を導入します。 aこの宣言でBTreeは、関数になっています。引数としてを取り、新しいを返します。

ここでは、プログラムで値に割り当てることができる型である具象型(例にはInt[Char]およびが含まれます) と、型を代入できるようにする必要がある型コンストラクター関数の違いを考慮することが重要です。値に割り当てられます。値は「何かのリスト」である必要があるため、「リスト」型にすることはできません。同じ精神で、値は「何かを格納するバイナリ ツリー」である必要があるため、「バイナリ ツリー」型にすることはできません。Maybe Bool

たとえばBool、引数としてを渡すと、sを格納するバイナリ ツリーであるBTreetype が返されます。type 変数の出現箇所をすべて type に置き換えると、それがどのように真であるかを自分で確認できます。BTree BoolBoolaBool

必要に応じて、次の種類BTreeの関数として表示できます

BTree :: * -> *

種類はやや型に似ています。 は具象型を示しているため、具象型から具象型へ*と言います。BTree

まとめ

ここに戻って、類似点に注意してください。

  • データ コンストラクターは、0 個以上の値を取り、新しい値を返す "関数"です

  • コンストラクターは、0 個以上の型を取り、新しい型を返す「関数」です

値にわずかなバリエーションが必要な場合、パラメーターを持つデータ コンストラクターはクールです。これらのバリエーションをパラメーターに入れ、値を作成する人に、どの引数を入れるかを決定させます。同じ意味で、パラメーターを持つ型コンストラクターはクールです。タイプにわずかなバリエーションが必要な場合!これらのバリエーションをパラメーターとして入れ、型を作成する人に、どの引数を入れるかを決定させます。

ケーススタディ

ここでのホーム ストレッチとして、型を考えることができMaybe aます。その定義は

data Maybe a = Nothing
             | Just a

ここでMaybeは、具象型を返す型コンストラクターを示します。Just値を返すデータコンストラクタです。Nothing値を含むデータ コンストラクターです。の型Justを見ると、

Just :: a -> Maybe a

つまり、Justtype の値を受け取り、 typeaの値を返しますMaybe a。の種類Maybeを見ると、

Maybe :: * -> *

つまり、Maybe具象型を受け取り、具象型を返します。

もう一度!具象型と型コンストラクター関数の違い。s のリストを作成できませんMaybe- 実行しようとすると

[] :: [Maybe]

エラーが発生します。Maybe Intただし、 、またはのリストを作成することはできますMaybe a。これMaybeは、 が型コンストラクター関数であるためですが、リストには具象型の値が含まれている必要があります。Maybe IntおよびMaybe a具象型 (または、必要に応じて、具象型を返す型コンストラクター関数の呼び出し) です。

于 2013-08-13T09:49:52.497 に答える
47

Haskell には代数データ型があり、他のほとんどの言語にはありません。これはおそらくあなたを混乱させているものです。

他の言語では、通常、さまざまな種類のデータを保持する一連の名前付きフィールドを持つ「レコード」、「構造体」などを作成できます。Redまた、(小さい)固定された可能な値のセット(たとえば、 、Greenおよび)を持つ「列挙型」を作成することもできますBlue

Haskell では、これらの両方を同時に組み合わせることができます。奇妙ですが、本当です!

なぜ「代数的」と呼ばれるのですか?さて、オタクは「合計型」と「製品型」について話します。例えば:

data Eg1 = One Int | Two String

Eg1値は基本的整数または文字列です。したがって、すべての可能なEg1値のセットは、可能なすべての整数値とすべての可能な文字列値のセットの「合計」です。そのため、オタクはEg1「和タイプ」と呼んでいます。一方で:

data Eg2 = Pair Int String

すべてEg2の値は、整数と文字列の両方で構成されます。したがって、すべての可能なEg2値のセットは、すべての整数のセットとすべての文字列のセットのデカルト積です。2つのセットが「乗算」されるため、これは「製品タイプ」です。

Haskell の代数型は積型の和型です。コンストラクターに複数のフィールドを指定して製品タイプを作成し、複数のコンストラクターで (製品の) 合計を作成します。

これが役立つ理由の例として、データを XML または JSON のいずれかとして出力し、構成レコードを取得するものがあるとします。ただし、明らかに、XML と JSON の構成設定はまったく異なります。したがって、次のようなことができます。

data Config = XML_Config {...} | JSON_Config {...}

(明らかに、適切なフィールドがいくつかあります。) 通常のプログラミング言語では、このようなことはできません。そのため、ほとんどの人はそれに慣れていません。

于 2013-08-13T17:34:45.160 に答える
28

最も単純なケースから始めます。

data Color = Blue | Green | Red

Colorこれは、引数を取らない「型コンストラクタ」を定義し、 3 つの「データ コンストラクタ」、 BlueGreenおよび を持ちRedます。どのデータ コンストラクターも引数を取りません。これは、 、 、 の 3 つのタイプがあることを意味しColorます。BlueGreenRed

ある種の値を作成する必要がある場合は、データ コンストラクターを使用します。お気に入り:

myFavoriteColor :: Color
myFavoriteColor = Green

myFavoriteColorデータコンストラクターを使用して値を作成しますGreen-データコンストラクターによって生成される値のmyFavoriteColorタイプColorであるため、タイプになります。

ある種のを作成する必要がある場合は、型コンストラクターを使用します。これは通常、署名を書く場合に当てはまります。

isFavoriteColor :: Color -> Bool

この場合、Color(引数を取らない) 型コンストラクターを呼び出しています。

まだ私と一緒に?

ここで、赤/緑/青の値を作成するだけでなく、「強度」も指定したいとします。同様に、0 から 256 までの値です。各データ コンストラクターに引数を追加することでそれを行うことができるため、最終的には次のようになります。

data Color = Blue Int | Green Int | Red Int

ここで、3 つのデータ コンストラクタのそれぞれが type の引数を取りますInt。型コンストラクタ ( Color) はまだ引数を取りません。だから、私の好きな色は濃い緑で、私は書くことができました

    myFavoriteColor :: Color
    myFavoriteColor = Green 50

Green繰り返しますが、データ コンストラクターを呼び出し、 type の値を取得しますColor

人々が色の強さをどのように表現するかを指示したくない場合を想像してみてください。先ほど行ったように数値が必要な場合もあります。他の人は、「明るい」または「それほど明るくない」を示すブール値だけで問題ない場合があります。これに対する解決策はInt、データ コンストラクターでハードコードするのではなく、型変数を使用することです。

data Color a = Blue a | Green a | Red a

ここで、型コンストラクターは 1 つの引数 (呼び出した別の型a!) を受け取り、すべてのデータ コンストラクターはその型の 1 つの引数 (値!) を受け取りますa。だから、あなたが持つことができます

myFavoriteColor :: Color Bool
myFavoriteColor = Green False

また

myFavoriteColor :: Color Int
myFavoriteColor = Green 50

Colorデータ コンストラクターによって返される「有効な」型を取得するために、引数 (別の型) を指定して型コンストラクターを呼び出す方法に注意してください。これは、コーヒーを 1 杯か 2 杯飲みながら読みたい種類の概念に触れています。

ここで、データ コンストラクターと型コンストラクターとは何か、データ コンストラクターが他の値を引数として取り、型コンストラクターが他の型を引数として取る方法を理解しました。HTH。

于 2013-08-13T09:34:34.600 に答える
6

他の人が指摘したように、ポリモーフィズムはここではそれほど役に立ちません。おそらくすでにおなじみの別の例を見てみましょう。

Maybe a = Just a | Nothing

この型には 2 つのデータ コンストラクターがあります。Nothingやや退屈で、有用なデータは含まれていません。一方、型が何であれ -Justの値が含まれています。この型を使用する関数を書きましょう。たとえば、リストの先頭があればそれを取得します (これはエラーをスローするよりも便利であることに同意していただければ幸いです)。aaInt

maybeHead :: [Int] -> Maybe Int
maybeHead [] = Nothing
maybeHead (x:_) = Just x

> maybeHead [1,2,3]    -- Just 1
> maybeHead []         -- None

したがって、この場合aは ですが、Int他のタイプでも同様に機能します。実際、すべてのタイプのリストに対して関数を機能させることができます (実装を変更しなくても)。

maybeHead :: [t] -> Maybe t
maybeHead [] = Nothing
maybeHead (x:_) = Just x

一方、特定のタイプの のみを受け入れる関数を書くことができますMaybe

doubleMaybe :: Maybe Int -> Maybe Int
doubleMaybe Just x = Just (2*x)
doubleMaybe Nothing= Nothing

簡単に言えば、ポリモーフィズムを使用すると、独自の型に他のさまざまな型の値を操作する柔軟性を与えることができます。

あなたの例では、ある時点でString会社を特定するには不十分であると判断するかもしれませんが、独自のタイプCompany(国、住所、バックアカウントなどの追加データを保持する) が必要です。の最初の実装は、最初の値の代わりにCar使用するように変更する必要があります。2 番目の実装は問題ありません。そのまま使用すると、以前と同じように機能します (もちろん、会社のデータにアクセスする関数は変更する必要があります)。CompanyStringCar Company String Int

于 2013-08-13T09:43:52.630 に答える