やってみよう
「Haskell入門ハンズオン!」事前資料の一覧
- 概要
- 処理系の導入と対話環境
- やってみよう
ファイルに保存した定義を読みこむ
ファイルに保存した定義を、対話環境に読みこんでみる。まずは、好きなエディタ(例ではVim)で、ファイルを作成する。
% vim fruits.hs
myFavoriteFruit = "apple"
これを、対話環境に読みこむ。
% stack ghci
Prelude> :load fruits.hs
*Main>
値を表示してみよう。
*Main> myFavoriteFruit
"apple"
対話環境はそのままにして、別のターミナルでファイルを編集する。
% vim fruits.hs
myFavoriteFruit = "banana"
対話環境で値を表示する。ファイルを再読み込みするためには、コマンド:reloadを使う。
*Main> :reload
*Main> myFavoriteFruit
"banana"
関数を定義する
関数doubleを定義してみる。
% vim functions.hs
double x = x * 2
関数を使う
関数doubleを使ってみる。
% stack ghci
Prelude> :load functions.hs
*Main> double 8
16
関数リテラル
関数リテラルを使うと、関数に名前をつけずに直接、作ることができる。関数定義は、関数リテラルによって作った関数で、変数を束縛していると考えることができる。
*Main> (\x -> x * 2) 8
16
演算子と関数
演算子と関数とには、見た目のちがいだけしかない。演算子には、記号列を使う。また、中置記法となる。演算子と関数とは、``や()でたがいに変換できる。
*Main> 3 + 5
8
*Main> (+) 3 5
8
*Main> div 128 5
25
*Main> 128 `div` 5
25
型を宣言する
変数luckyを定義する。Integer型として宣言することにしよう。
% vim functions.hs
lucky :: Integer
lucky = 7
関数doubleにも型宣言をつけてみよう。
double :: Integer -> Integer
double x = x * 2
対話環境に読みこもう。
*Main> :reload
*Main> lucky
7
*Main> :type lucky
lucky :: Integer
*Main> double lucky
14
数値型の例
数値をあらわす型には、数値の種類に応じて、さまざまなものがある。たとえば、整数値をあらわす型Integerや、浮動小数点数をあらわす型Doubleなどがある。
型注釈
Haskellには、さまざまな型の値に解釈されることのある、多相的な式を書くことができる。このような式では、型注釈をつけないと、ひとつの型の値として評価できないことがある。たとえば、数値リテラルなども、そのような式のひとつである。対話環境で試してみよう。
*Main> 123 :: Integer
123
*Main> 123 :: Double
123.0
Bool値
Bool値とは、偽(そうでない)と真(そうである)とをあらわす、ふたつの値のこと。
*Main> False
False
*Main> True
True
Maybe値
Maybe値とは、値がないかもしれない値。「失敗するかもしれない」関数の返り値として使われる。値があるときは「Just 値」のように表記/表示され、値がないときには「Nothing」のように表記/表示される。
*Main> Just 8
Just 8
*Main> Nothing
Nothing
タプル
タプルについても、試してみよう。たとえば、つぎのようなタプルを定義する。
% vim functions.hs
taro :: (String, Integer)
taro = ("Taro Yamada", 35)
対話環境で、みてみよう。
% stack ghci
Prelude> :load functions.hs
*Main> taro
("Taro Yamada",35)
*Main> :type taro
(String,Integer)
パターンマッチ
パターンマッチは、Haskellでは本質的な機能のひとつ。分岐と、複合的な値からの値のとりだしとが、パターンマッチによって実現される。たとえば、失敗するかもしれない計算の結果を受けとって、値をかえす関数を、みてみよう。
% vim functions.hs
helloTo :: Maybe String -> String
helloTo (Just n) = "Hello, " ++ n ++ "!"
helloTo Nothing = "Hello, customer!"
タプルの、それぞれの要素の取り出しも、パターンマッチでできる。
human :: (String, Integer) -> String
human (n, a) = n ++ "(" ++ show a ++ ")"
関数showは数値を文字列に変換している。リテラルもパターンとして使える。
safeRecip :: Double -> Maybe Double
safeRecip 0 = Nothing
safeRecip x = Just (1 / x)
それぞれの関数を試してみよう。
% stack ghci
Prelude> :load functions.hs
*Main> helloTo (Just "Yoshikuni")
"Hello, Yoshikuni!"
*Main> helloTo Nothing
"Hello, customer!"
*Main> human ("Taro Yamada", 32)
"Taro Yamada(32)"
*Main> safeRecip 8
Just 0.125
*Main> safeRecip 0
Nothing
ワイルドカード
値に束縛された変数を使わないときには、ワイルドカードが使える。ワイルドカードはアンダーバーで示される。
% vim functions.hs
isNothing Nothing = True
isNothing (Just _) = False
試してみよう。
*Main> :reload
*Main> isNothing Nothing
True
*Main> isNothing (Just 8)
False
@パターン
@パターンを使うと、変数束縛したうえで、さらに細かいパターンにマッチさせることができる。
% vim functions.hs
atPattern :: Maybe Integer -> String
atPattern jx@(Just x) = show jx ++ ": " ++ show x
atPattern Nothing = ""
試してみよう。
*Main> :reload
*Main> atPattern (Just 8)
"Just 8: 8"
*Main> atPattern Nothing
""
ガード構文
Bool値に対するパターンマッチは頻出なので、特別な構文がある。安全に平方根をもとめる関数を定義してみる。
% vim functions.hs
safeSqrt :: Double -> Maybe Double
safeSqrt x
| x < 0 = Nothing
| otherwise = Just (sqrt x)
試してみよう。
% stack ghci
Prelude> :load functions.hs
*Main> safeSqrt 9
Just 3.0
*Main> safeSqrt 2
Just 1.142135623730951
*Main> safeSqrt (- 5)
Nothing
if式
おなじことをif式で書いてみる。
% vim functions.hs
safeSqrt' :: Double -> Double
safeSqrt' x = if x < 0 then Nothing else Just (sqrt x)
うえとおなじようにして、試してみよう。
case式
case式を使うと、関数定義ではなく式のなかでパターンマッチができる。
% vim functions.hs
yesNo :: Char -> Maybe Bool
yesNo c = case c of
'n' -> Just False
'y' -> Just True
_ -> Nothing
試してみよう。
*Main> :reload
*Main> yesNo 'y'
Just True
*Main> yesNo 'c'
Nothing
多相
多相性のある関数を定義してみよう。ふたつめの引数を無視する関数は、つぎのようになる。
% vim functions.hs
ignoreSecond :: a -> b -> a
ignoreSecond x y = x
第1引数、第2引数ともに、どのような型であっても、この関数は実行できる。また、第1引数と返り値の型は、おなじでなければならないので、おなじ型変数aで表現されている。試してみよう。
% stack ghci
Prelude> :load functions.hs
*Main> ignoreSecond 8 "hello"
8
*Main> ignoreSecond False 123.45
False
*Main> :type ignoreSecond
ignoreSecond :: a -> b -> a
型シノニム
予約語typeによる構文を使って、型の別名を定義することができる。
% vim functions.hs
type Human = (String, Integer)
showHuman :: Human -> String
showHuman (n, a) = n ++ "(" ++ show a ++ ")"
試してみよう。
% stack ghci
Prelude> :load functions.hs
*Main> showHuman ("Taro", 39)
"Taro(39)"
型引数
型シノニムの定義には、型引数を使うことができる。
% vim functions.hs
type Check a = (Bool, a)
tasks :: [Chack String]
tasks = [(False, "Shopping"), (True, "Walking")]
対話環境で見てみる。
% stack ghci
Prelude> :load functions.hs
*Main> tasks
[(False,"Shopping"),(True,"Walking")]
*Main> :type tasks
tasks :: [Check String]
モジュールの導入
ソースファイルのなかでモジュールを導入するには、予約語importを使う。
% vim useMaybe.hs
import Data.Maybe (fromMaybe)
maybe0 :: Maybe Integer -> Integer
maybe0 = fromMaybe 0
試してみよう。
% stack ghci
Prelude> :load useMaybe.hs
*Main> maybe0 (Just 8)
8
*Main> maybe0 Nothing
0
コメント
--から行末までと、{-と-}とで、かこまれた部分とは、コメントとなる。コメントはコードに影響をあためない。
% vim functions.hs
some :: Integer
some = 8 -- some is 8
hoge :: Integer -> Integer
hoge x = x * 3
{-
hoge 3 = 9
hoge 10 = 30
...
-}
試してみよう。
% stack ghci
Prelude> :load functions.hs
*Main> some
8
*Main> hoge 5
15