LYH 8.1 入出力

8.1 入出力

8.1 不純なものと純粋なものを分離する

Haskellでは関数が副作用を持つことが許されない。木のようなデータ構造でさえ、要素を追加しようと思ったら、新しい要素をが追加された木を、新たに変数に束縛することで対応できた。しかし、入出力に関しては、どうやっても状態の書き換えが必須となる。ディスプレイの状態が書き換わらずにどうやってユーザが扱うことが出来るだろうか。また、一行の入力を求める関数があったとき、ユーザの入力は毎回異なるから、関数が生成する文字列は毎回異なる。どう対処すればよいか。

副作用が含まれる処理が、副作用を含まない処理に影響を与えなければ良い。副作用が含まれる処理の中で完結していれば、副作用を含む処理と含まない処理を分離して扱うことができる。

以下の一文を考えよう。

name <- getLine

前提知識がない状態だと、「一行を読み取って、変数に読み取った文字列を代入している」と読めそうである。

もしそうであれば、getLineの型は、getLine :: String だろうか。実際に:tで確かめてみると以下のように表示される。

getLine :: IO String

ここで、IO String は、Stringを生成するI/Oアクションと読むことが出来る。

生成されるStringは毎回同じではないため、getLineに副作用が存在している。しかし、I/Oアクションによって生成されたStringをnameに束縛していると考えると、Stringをnameに束縛する作業は副作用がない処理と変わりはない。I/Oアクションによって生成されたStringを置いておく箱から、Stringを取り出してくる処理として<-を用いることで、副作用のある処理と副作用のない処理を分離して考えることが出来る。

8.1 確認問題

  1. I/Oアクションとは何か。
  2. I/Oアクションがどうやって入出力を可能にするのか。
  3. 純粋関数型言語によって得られる利点を3つあげよ。
続きを読む

LYH 7.11 型を司るもの、種類

7.11 型を司るもの、種類

  1. Intの種類を調べたいとき、GHCiではどのように入力すればよいか。
  2. 1.に対して、値の型を調べるコマンドは何か。
  3. 具体型とは何か。具体型はGHCiではどのような記号で表記されるか。
  4. Maybeの種類を答えよ。
  5. Maybe Intの種類を答えよ。
  6. isUpper関数の型と種類を答えよ。
  7. isUpper 'A'の型と種類を答えよ。
  8. Eitherの種類を答えよ。
  9. 型コンストラクタは関数と同様に(9)されているので、部分適用ができる。Either Stringの種類を答えよ。
  10. Functorになれる型の種類を答えよ。
続きを読む

LYH 7.10 Functor 型クラス

7.10 Functor 型クラス

1.序文

  1. Functorはどのような型クラスか。
  2. Functor型クラスに属している型の例を1つあげよ。
  3. Functor型クラスの実装を示せ。
  4. 3.中のfmapを読み、どのような関数か説明せよ。
  5. リストに対するFunctorインスタンス宣言を示せ。

2.MaybeはFunctorだよ、たぶん

  1. MaybeをFunctorのインスタンスとして宣言するコードを書け。

3. TreeもFunctorの森に

  1. fmapをTree a型専用の関数と思って型シグネチャを書け。
  2. TreeをFunctorのインスタンスとして宣言するコードを書け。

4. EitherはFunctorであるか否か

  1. Functor型クラスは型引数をいくつ要求するか。
  2. Either a bは型引数を2つ要求する。これはファンクターに出来るだろうか。どのようにすればよいか。
  3. 標準ライブラリのControl.Monad.Instances内で、Either aはどのように定義されているか。
  4. fmapがEither aに特化していたとして、型シグネチャを書け。
  5. fmapがRightを優先する(?)ことをなんというか。
続きを読む

LYH 7.9 YesとNoの型クラス

7.9 YesとNoの型クラス

  1. 様々な型の真理値判定を可能とするためのYesNo型を宣言せよ。
  2. 1.に関して、0は偽、それ以外は真と解釈するように数に関してのインスタンスを定義せよ。
  3. 空リストは偽、そうでなければ真とするようにリストに関してのインスタンスを定義せよ。
  4. 3.のインスタンス宣言について、[a]の部分を同じ意味で別の表記に変更することが出来る。どのように変えればよいか。
  5. Boolインスタンスを定義せよ。
  6. 5.で用いたidとは何か。
  7. Maybe aインスタンスを定義せよ。
  8. YesNo値をとり、好きな型の値の2つのうち1つを返す関数yesNoIfを定義せよ。
続きを読む

LYH 7.8 型クラス 中級講座

7.8 型クラス 中級講座

1. 序文

  1. 型クラスはインターフェースのようなものである。型クラスはどのような振る舞いを定義するか。例を3つあげよ。
  2. 型クラスの振る舞いは何を定義することで得られるか。
  3. Eqは、(3.1)が出来る値の型クラスである。Eqはどのような関数(メソッド)を定義しているか。(3.2)
  4. 標準ライブラリにおけるEqの定義を書け。
  5. class Eq a whereと宣言して、クラスの中で(==) :: a -> a -> Boolと型宣言する。関数の型は何か。

2. 交通信号データ型

  1. 値としてRed, Yellow, Greenをもつ交通信号の状態を定義する型TrafficLightを定義し、Eqのインスタンスを自動導出を用いずに作れ。
  2. クラスを宣言したときには、==を定義するのに/=を使い、/=の定義には==を用いた。インスタンス宣言では、どちらか一方を上書きすれば良い。これは型クラスのなんと呼ばれる概念か。
  3. Showの最小完全定義を満たすように、型TrafficLightををShowのインスタンスにするコードを書け。このとき、例えば値Redなら"Red light"と表示されるようにせよ。

3. サブクラス化

  1. class (Eq a) => Num a whereと書かれたとき、NumEqにはどのような関係があるか。
  2. 型クラス宣言に何をつけるとサブクラスを作ることが出来るか。

4. 多相型を型クラスのインスタンス

  1. 型コンストラクタMaybeを型クラスEqのインスタンスとする例を1つ書け。
  2. 型クラスYourTypeClassのインスタンスの詳細を知るにはGHCiで何と入力すればよいか。
続きを読む

LYH 7.7 再帰的なデータ構造

代数(的)データ型の3種類を言え。( 1 )
リストは ( 2 ) または ( 3 ) のいずれかの値を取るデータ構造である。

代数データ型を使って独自のリスト型 List a を実装せよ。( 4 )
( 4 ) をレコード構文を用いて書け。( 5 )

Cons とは ( 6 ) を言い換えたものである。Haskell標準の ( 6 ) は、値とリストをとってリストを返す ( 7 ) である。( 6 ) には、( 8 ) と ( 9 ) の2つのフィールドがあるといえる。

リストの改善

記号文字だけを使って関数に名前をつけると ( 10 ) になる。値コンストラクタも ( 11 ) であるので同様。
ただし、値コンストラクタの名前は ( 12 ) で始まる必要がある。

結合性宣言を infixr 5 とした上で、Cons a (List a) と同等の値コンストラクタ a :-: (List a) を定義せよ。( 13 )
結合性宣言は必須ではないので、これを除けば Cons a (List a) と同等という意味。結合性宣言を省略した演算子はすべて infixl 9 となる。

標準のリストにおける ++ と同等の、独自のリスト型List aにおける ^++ の定義を書け。ただし、結合性宣言の数字は 5 である。( 14 )

パターンマッチとは、( 15 ) をマッチさせることに他ならない。なので、( 14 ) のコードにおいて ( 16 ) というパターンマッチを使うことができる。

パターンマッチは ( 15 ) であれば何に対してでも使えるので、(今まで :-: などの中置コンストラクタについてパターンマッチさせてきたが)通常の前置コンストラクタもパターンマッチできる他、( 17 ) や ( 18 ) といったものもパターンマッチできる。これらは数値型や文字型の ( 15 ) だからからである。

木を植えよう

二分木のデータ型Tree aを実装せよ。ただしShow型のインスタンスであるものとする。( 19 )

二分木を作るための2つの関数を実装せよ。( 20 )

ある要素が二分木に属しているか判定する関数を作れ。( 21 )

リストから要素を1つずつ辿って値を生成する操作は畳み込みを使うのが便利である。畳み込みを用いて、リストnumsから生成された木numsTreeを作れ。( 22 )

続きを読む

LYH 7.6 型シノニム

新しい型を作る = ( 1 ) キーワード
型同義名(型シノニム)を作る = ( 2 ) キーワード

例えば、Stringは以下ように型シノニムとして定義されている。

type String = [Char]

電話帳を定義してみよう。

type PhoneNumber = String
type Name = String
type PhoneBook = [(Name, PhoneNumber)]

pbook :: PhoneBook
pbook = [
  ("betty", "555-2938"),
  ("bonnie", "452-2928"),
  ...
]

inPhoneBook :: (  3  )
inPhoneBook name pnumber pbook = (name, pnumber) `elem` pbook
型シノニムの多相化
type AssocList (  4  ) = [(k, v)]

とすることで、型引数を取ることができる。AssocListは ( 5 ) である。

以下の2つは同義

type (  6  )
type IntMap v = Map.Map Int v
そこを左に行って、すぐ右へ

Either a b は( 7 )を2つとるデータ型である。
Left, Right はそれぞれ( 8 )である。

ghci> Right 20
(  9  )

ghci> Left "w00t"
(  10  )

ghci> :t Right 'a'
(  11  )

ghci> :t Left True
(  12  )

Left True の型を評価すると ( 12 ) となっていることがわかる。Left ( 8 ) で作った値なので、1つ目の型引数は Bool に決まっているが、2つ目の型引数は ( 13 ) のまま残っている。これは、Nothing という値に ( 14 ) という型が付くのと同じ理屈である。

Nothing は何故失敗したかの情報を持つことはできない。これを持てるようにするために、普通は ( 15 ) 型の返り値を使う。ここで、( 16 ) は失敗が起こった場合に何であるかを伝えてくれる型、( 17 ) は、成功した計算の型である。したがって、エラーは ( 18 ) を、結果は ( 19 ) を使って表す。

ロッカーの割当のコードを書け。( 20 )
ロッカーを表すMapから暗証番号を検索する関数を作りたい。
関数が失敗するパターンが、( 21 ) と、( 22 ) の2通りあるので、( 23 ) 型で表すようにする。
実際に、暗証番号を検索する関数を作れ。( 24 )

続きを読む