Haskell
#30
TIOBE#29
PYPL#28
GitHub#75
プログラミング言語
Haskell
概要
Haskellは純粋関数型プログラミング言語で、強力な型システム、遅延評価、数学的な美しさを特徴とする学術的および実用的な言語です。
詳細
Haskellは1990年に学術委員会によって標準化された純粋関数型プログラミング言語で、数学者Haskell Curryにちなんで名付けられました。すべての計算が数学的関数として表現され、副作用が型システムによって明示的に管理される純粋関数型パラダイムを採用しています。遅延評価(必要に応じて計算)、強力な静的型システム、型推論、高階関数、パターンマッチング、代数的データ型、モナドなどの先進的な機能を提供します。学術研究、プログラミング言語理論、コンパイラ設計、暗号学、金融システムなどの分野で利用されており、関数型プログラミングの概念を他の言語に広める重要な役割を果たしています。
書き方の例
Hello World
-- 基本的な出力
main :: IO ()
main = putStrLn "Hello, World!"
-- 複数行の出力
main2 :: IO ()
main2 = do
putStrLn "こんにちは、Haskell!"
putStrLn "純粋関数型プログラミング言語です"
putStrLn "遅延評価と強力な型システムが特徴です"
-- 変数を使った出力
greetingProgram :: IO ()
greetingProgram = do
let name = "太郎"
age = 25
putStrLn $ "私の名前は" ++ name ++ "で、" ++ show age ++ "歳です。"
-- 文字列補間スタイル
formatGreeting :: String -> Int -> String
formatGreeting name age = "名前: " ++ name ++ ", 年齢: " ++ show age
main3 :: IO ()
main3 = putStrLn $ formatGreeting "山田" 30
データ型と変数
-- 基本的なデータ型
number :: Int
number = 42
bigNumber :: Integer -- 任意精度整数
bigNumber = 12345678901234567890
floatValue :: Float
floatValue = 3.14
doubleValue :: Double
doubleValue = 3.141592653589793
character :: Char
character = 'A'
text :: String
text = "文字列" -- String は [Char] のエイリアス
boolean :: Bool
boolean = True
-- リスト
numberList :: [Int]
numberList = [1, 2, 3, 4, 5]
stringList :: [String]
stringList = ["apple", "banana", "cherry"]
-- 無限リスト(遅延評価のため)
infiniteNumbers :: [Int]
infiniteNumbers = [1..]
-- リスト範囲
rangeList :: [Int]
rangeList = [1..10]
evenNumbers :: [Int]
evenNumbers = [2, 4..20]
-- タプル
coordinate :: (Int, Int)
coordinate = (10, 20)
person :: (String, Int, Bool)
person = ("田中太郎", 30, True)
-- Maybe型(null安全)
maybeName :: Maybe String
maybeName = Just "山田"
noName :: Maybe String
noName = Nothing
-- Either型(エラーハンドリング)
result :: Either String Int
result = Right 42
errorResult :: Either String Int
errorResult = Left "エラーが発生しました"
-- カスタムデータ型
data Color = Red | Green | Blue | RGB Int Int Int
deriving (Show, Eq)
data Person = Person
{ personName :: String
, personAge :: Int
, personEmail :: String
} deriving (Show)
-- 使用例
redColor :: Color
redColor = Red
customColor :: Color
customColor = RGB 255 128 0
john :: Person
john = Person "John Doe" 25 "[email protected]"
-- 型クラス(インターフェース)
class Describable a where
describe :: a -> String
instance Describable Color where
describe Red = "赤色"
describe Green = "緑色"
describe Blue = "青色"
describe (RGB r g b) = "RGB(" ++ show r ++ "," ++ show g ++ "," ++ show b ++ ")"
関数定義とパターンマッチング
-- 基本的な関数定義
add :: Int -> Int -> Int
add x y = x + y
-- カリー化された関数
addCurried :: Int -> (Int -> Int)
addCurried x = \y -> x + y
-- パターンマッチング
factorial :: Int -> Int
factorial 0 = 1
factorial n = n * factorial (n - 1)
-- ガード(条件分岐)
classifyNumber :: Int -> String
classifyNumber n
| n < 0 = "負の数"
| n == 0 = "ゼロ"
| n <= 10 = "1から10"
| otherwise = "10より大きい"
-- リストのパターンマッチング
listLength :: [a] -> Int
listLength [] = 0
listLength (_:xs) = 1 + listLength xs
listHead :: [a] -> Maybe a
listHead [] = Nothing
listHead (x:_) = Just x
-- 複雑なパターンマッチング
processData :: Maybe (Either String Int) -> String
processData Nothing = "データなし"
processData (Just (Left err)) = "エラー: " ++ err
processData (Just (Right val)) = "値: " ++ show val
-- 高階関数
applyTwice :: (a -> a) -> a -> a
applyTwice f x = f (f x)
-- 関数合成
compose :: (b -> c) -> (a -> b) -> a -> c
compose f g x = f (g x)
-- 演算子定義
(.:) :: (b -> c) -> (a -> b) -> a -> c
(.:) = compose
-- 無名関数(ラムダ)
square :: Int -> Int
square = \x -> x * x
-- where句
calculateCircle :: Double -> (Double, Double)
calculateCircle r = (circumference, area)
where
circumference = 2 * pi * r
area = pi * r * r
-- let式
calculateRectangle :: Double -> Double -> Double
calculateRectangle width height =
let perimeter = 2 * (width + height)
area = width * height
in perimeter + area
-- 部分適用
addFive :: Int -> Int
addFive = add 5
multiplyByTen :: Int -> Int
multiplyByTen = (*) 10
-- 使用例
exampleUsage :: IO ()
exampleUsage = do
print $ add 10 20
print $ factorial 5
print $ classifyNumber 7
print $ listLength [1, 2, 3, 4]
print $ applyTwice (+1) 5
print $ addFive 15
リスト処理と高階関数
-- map関数(リストの各要素に関数を適用)
squareList :: [Int] -> [Int]
squareList = map (^2)
-- filter関数(条件に合う要素を抽出)
evenOnly :: [Int] -> [Int]
evenOnly = filter even
-- fold関数(リストを単一の値に畳み込み)
sumList :: [Int] -> Int
sumList = foldr (+) 0
productList :: [Int] -> Int
productList = foldr (*) 1
-- foldl(左畳み込み)
reverseList :: [a] -> [a]
reverseList = foldl (flip (:)) []
-- リスト内包表記
squares :: [Int]
squares = [x^2 | x <- [1..10]]
evenSquares :: [Int]
evenSquares = [x^2 | x <- [1..10], even x]
-- 条件付きリスト内包表記
pythagoreanTriples :: [(Int, Int, Int)]
pythagoreanTriples = [(a, b, c) | c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2]
-- zipWith(2つのリストを組み合わせ)
addLists :: [Int] -> [Int] -> [Int]
addLists = zipWith (+)
-- take(先頭からn個取得)
firstFive :: [a] -> [a]
firstFive = take 5
-- drop(先頭からn個削除)
skipFive :: [a] -> [a]
skipFive = drop 5
-- concat(リストのリストを平坦化)
flattenList :: [[a]] -> [a]
flattenList = concat
-- concatMap(mapしてからconcat)
duplicateElements :: [a] -> [a]
duplicateElements = concatMap (\x -> [x, x])
-- カスタム高階関数
mapMaybe :: (a -> Maybe b) -> [a] -> [b]
mapMaybe _ [] = []
mapMaybe f (x:xs) = case f x of
Nothing -> mapMaybe f xs
Just y -> y : mapMaybe f xs
-- 無限リストの処理(遅延評価)
fibonacci :: [Int]
fibonacci = 0 : 1 : zipWith (+) fibonacci (tail fibonacci)
primes :: [Int]
primes = sieve [2..]
where
sieve (p:xs) = p : sieve [x | x <- xs, x `mod` p /= 0]
-- 使用例
listExamples :: IO ()
listExamples = do
print $ squareList [1, 2, 3, 4, 5]
print $ evenOnly [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print $ sumList [1, 2, 3, 4, 5]
print $ take 10 squares
print $ take 10 fibonacci
print $ take 10 primes
print $ addLists [1, 2, 3] [4, 5, 6]
モナドとdo記法
-- Maybe モナド
safeDivide :: Double -> Double -> Maybe Double
safeDivide _ 0 = Nothing
safeDivide x y = Just (x / y)
safeSquareRoot :: Double -> Maybe Double
safeSquareRoot x
| x < 0 = Nothing
| otherwise = Just (sqrt x)
-- Maybe モナドの連鎖
safeCalculation :: Double -> Double -> Maybe Double
safeCalculation x y = do
result1 <- safeDivide x y
result2 <- safeSquareRoot result1
return (result2 * 2)
-- または演算子を使って
safeCalculation' :: Double -> Double -> Maybe Double
safeCalculation' x y =
safeDivide x y >>= safeSquareRoot >>= \result -> return (result * 2)
-- List モナド
combinations :: [Int] -> [Int] -> [(Int, Int)]
combinations xs ys = do
x <- xs
y <- ys
guard (x + y > 5) -- 条件をフィルタ
return (x, y)
-- IO モナド
getUserInput :: IO (String, Int)
getUserInput = do
putStrLn "名前を入力してください:"
name <- getLine
putStrLn "年齢を入力してください:"
ageStr <- getLine
let age = read ageStr :: Int
return (name, age)
interactiveProgram :: IO ()
interactiveProgram = do
(name, age) <- getUserInput
putStrLn $ "こんにちは、" ++ name ++ "さん!"
putStrLn $ "あなたは" ++ show age ++ "歳ですね。"
when (age >= 20) $ putStrLn "成人ですね!"
-- カスタムモナド(State モナド例)
newtype State s a = State { runState :: s -> (a, s) }
instance Functor (State s) where
fmap f (State g) = State $ \s -> let (a, s') = g s in (f a, s')
instance Applicative (State s) where
pure a = State $ \s -> (a, s)
(State f) <*> (State g) = State $ \s ->
let (f', s') = f s
(a, s'') = g s'
in (f' a, s'')
instance Monad (State s) where
return = pure
(State g) >>= f = State $ \s ->
let (a, s') = g s
(State h) = f a
in h s'
get :: State s s
get = State $ \s -> (s, s)
put :: s -> State s ()
put s = State $ \_ -> ((), s)
-- State モナドの使用例
counter :: State Int Int
counter = do
count <- get
put (count + 1)
return count
runCounter :: (Int, Int)
runCounter = runState (counter >> counter >> counter) 0
ファンクタとアプリカティブ
-- Functor型クラス
-- fmap :: (a -> b) -> f a -> f b
-- Maybe Functor の使用例
functorExamples :: IO ()
functorExamples = do
print $ fmap (*2) (Just 5) -- Just 10
print $ fmap (*2) Nothing -- Nothing
print $ fmap show [1, 2, 3] -- ["1", "2", "3"]
-- カスタムFunctor
data Box a = Box a deriving (Show)
instance Functor Box where
fmap f (Box x) = Box (f x)
-- Applicative Functor
-- pure :: a -> f a
-- (<*>) :: f (a -> b) -> f a -> f b
applicativeExamples :: IO ()
applicativeExamples = do
print $ pure (+) <*> Just 3 <*> Just 5 -- Just 8
print $ pure (+) <*> [1, 2] <*> [10, 20] -- [11, 21, 12, 22]
-- 複数の Maybe を組み合わせ
combineThree :: Maybe Int -> Maybe Int -> Maybe Int -> Maybe Int
combineThree mx my mz = (+) <$> mx <*> ((*) <$> my <*> mz)
-- Alternative型クラス
-- empty :: f a
-- (<|>) :: f a -> f a -> f a
alternativeExamples :: IO ()
alternativeExamples = do
print $ Nothing <|> Just 5 <|> Just 3 -- Just 5
print $ [] <|> [1, 2] <|> [3, 4] -- [1, 2]
-- Traversable(リスト処理とモナドの組み合わせ)
-- traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
traversableExamples :: IO ()
traversableExamples = do
print $ traverse safeDivide [10, 8, 6] <*> pure 2 -- Just [5.0, 4.0, 3.0]
print $ traverse (\x -> if x > 0 then Just x else Nothing) [1, 2, -3] -- Nothing
型レベルプログラミング
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeFamilies #-}
-- 型レベル自然数
data Nat = Zero | Succ Nat
-- 型レベルで長さが保証されるベクトル
data Vec (n :: Nat) a where
VNil :: Vec 'Zero a
VCons :: a -> Vec n a -> Vec 'Succ n a
-- 型安全な head 関数
vHead :: Vec ('Succ n) a -> a
vHead (VCons x _) = x
-- 型安全な tail 関数
vTail :: Vec ('Succ n) a -> Vec n a
vTail (VCons _ xs) = xs
-- ベクトルの連結
vAppend :: Vec n a -> Vec m a -> Vec (Add n m) a
vAppend VNil ys = ys
vAppend (VCons x xs) ys = VCons x (vAppend xs ys)
-- 型レベル加算
type family Add (n :: Nat) (m :: Nat) :: Nat where
Add 'Zero m = m
Add ('Succ n) m = 'Succ (Add n m)
-- Phantom Types
newtype Tagged tag a = Tagged a deriving (Show)
-- タグを使った型安全性
data UserID
data ProductID
createUser :: String -> Tagged UserID Int
createUser name = Tagged 123 -- 実際にはDBに保存
createProduct :: String -> Tagged ProductID Int
createProduct name = Tagged 456 -- 実際にはDBに保存
-- 型レベルで異なるIDの混在を防ぐ
-- getUserById :: Tagged UserID Int -> User
-- getProductById :: Tagged ProductID Int -> Product
-- Generalized Algebraic Data Types (GADTs)
data Expr a where
LitInt :: Int -> Expr Int
LitBool :: Bool -> Expr Bool
Add :: Expr Int -> Expr Int -> Expr Int
If :: Expr Bool -> Expr a -> Expr a -> Expr a
-- 型安全な評価器
eval :: Expr a -> a
eval (LitInt n) = n
eval (LitBool b) = b
eval (Add e1 e2) = eval e1 + eval e2
eval (If cond then_ else_) = if eval cond then eval then_ else eval else_
-- 使用例
exampleExpr :: Expr Int
exampleExpr = If (LitBool True) (Add (LitInt 1) (LitInt 2)) (LitInt 0)
特徴的な機能
遅延評価
-- 無限データ構造
ones :: [Int]
ones = 1 : ones
naturals :: [Int]
naturals = [0..]
-- 遅延評価のため、必要な分だけ計算される
takeFirst10 :: [Int]
takeFirst10 = take 10 naturals
-- フィボナッチ数列(無限)
fibs :: [Integer]
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
-- 素数の無限リスト(エラトステネスの篩)
sieve :: [Int] -> [Int]
sieve (p:xs) = p : sieve [x | x <- xs, x `mod` p /= 0]
primes :: [Int]
primes = sieve [2..]
-- 遅延I/O
lazyReadFile :: FilePath -> IO String
lazyReadFile = readFile -- ファイル全体を一度にメモリに読み込まない
-- 条件付き計算(必要な時だけ実行)
expensiveCalculation :: Int -> Int
expensiveCalculation n = if n > 1000 then n * n else 0
conditionalComputation :: Int -> Int
conditionalComputation n =
let expensive = expensiveCalculation n
in if n > 500 then expensive else n
-- take/drop の組み合わせでストリーミング処理
processChunks :: [a] -> [[a]]
processChunks [] = []
processChunks xs = take 10 xs : processChunks (drop 10 xs)
-- 無限リストの並列処理
parallelSum :: [Int] -> Int
parallelSum xs = foldr (+) 0 xs -- 遅延評価により効率的
型クラスと多相性
-- 基本的な型クラス
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
x /= y = not (x == y)
class Eq a => Ord a where
compare :: a -> a -> Ordering
(<), (<=), (>), (>=) :: a -> a -> Bool
max, min :: a -> a -> a
-- カスタム型クラス
class Serializable a where
serialize :: a -> String
deserialize :: String -> Maybe a
instance Serializable Int where
serialize = show
deserialize s = case reads s of
[(x, "")] -> Just x
_ -> Nothing
instance Serializable Bool where
serialize True = "T"
serialize False = "F"
deserialize "T" = Just True
deserialize "F" = Just False
deserialize _ = Nothing
-- 多相関数
identity :: a -> a
identity x = x
first :: (a, b) -> a
first (x, _) = x
swap :: (a, b) -> (b, a)
swap (x, y) = (y, x)
-- 制約付き多相性
showAndCompare :: (Show a, Ord a) => a -> a -> String
showAndCompare x y = show x ++ " " ++ comparison ++ " " ++ show y
where
comparison = case compare x y of
LT -> "<"
EQ -> "=="
GT -> ">"
-- Higher-Kinded Types
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
class Applicative m => Monad m where
(>>=) :: m a -> (a -> m b) -> m b
-- モナド変換子
newtype ReaderT r m a = ReaderT { runReaderT :: r -> m a }
instance Monad m => Functor (ReaderT r m) where
fmap f (ReaderT g) = ReaderT $ \r -> fmap f (g r)
instance Monad m => Applicative (ReaderT r m) where
pure a = ReaderT $ \_ -> pure a
(ReaderT f) <*> (ReaderT g) = ReaderT $ \r -> f r <*> g r
instance Monad m => Monad (ReaderT r m) where
(ReaderT g) >>= f = ReaderT $ \r -> do
a <- g r
runReaderT (f a) r
-- 型族(Type Families)
type family Element c :: * where
Element [a] = a
Element (Maybe a) = a
Element String = Char
class Collection c where
empty :: c
insert :: Element c -> c -> c
member :: Element c -> c -> Bool
instance Collection [a] where
empty = []
insert = (:)
member = elem
パーサーコンビネータ
-- 簡単なパーサーライブラリ
newtype Parser a = Parser { parse :: String -> Maybe (a, String) }
instance Functor Parser where
fmap f (Parser p) = Parser $ \input -> do
(result, rest) <- p input
return (f result, rest)
instance Applicative Parser where
pure a = Parser $ \input -> Just (a, input)
(Parser pf) <*> (Parser pa) = Parser $ \input -> do
(f, rest1) <- pf input
(a, rest2) <- pa rest1
return (f a, rest2)
instance Monad Parser where
(Parser p) >>= f = Parser $ \input -> do
(result, rest) <- p input
parse (f result) rest
-- 基本パーサー
char :: Char -> Parser Char
char c = Parser $ \input ->
case input of
(x:xs) | x == c -> Just (c, xs)
_ -> Nothing
string :: String -> Parser String
string "" = pure ""
string (c:cs) = (:) <$> char c <*> string cs
digit :: Parser Int
digit = Parser $ \input ->
case input of
(x:xs) | x >= '0' && x <= '9' -> Just (read [x], xs)
_ -> Nothing
-- コンビネータ
(<|>) :: Parser a -> Parser a -> Parser a
(Parser p1) <|> (Parser p2) = Parser $ \input ->
case p1 input of
Nothing -> p2 input
result -> result
many :: Parser a -> Parser [a]
many p = some p <|> pure []
some :: Parser a -> Parser [a]
some p = (:) <$> p <*> many p
-- 数値パーサー
number :: Parser Int
number = read <$> some digit
-- 式パーサー例
data Expr = Num Int | Add Expr Expr | Mul Expr Expr deriving (Show)
parseExpr :: Parser Expr
parseExpr = parseAdd
parseAdd :: Parser Expr
parseAdd = chainLeft parseMul (Add <$ char '+')
parseMul :: Parser Expr
parseMul = chainLeft parseFactor (Mul <$ char '*')
parseFactor :: Parser Expr
parseFactor = Num <$> number
<|> (char '(' *> parseExpr <* char ')')
chainLeft :: Parser a -> Parser (a -> a -> a) -> Parser a
chainLeft p op = p >>= rest
where
rest a = (do f <- op
b <- p
rest (f a b)) <|> pure a
-- 使用例
parseExample :: String -> Maybe Expr
parseExample input = fst <$> parse parseExpr input
バージョン
バージョン | ステータス | 主要な特徴 | リリース年 |
---|---|---|---|
Haskell 2010 | Current | 現在の標準、モジュールシステム改善 | 2010 |
Haskell 98 | Legacy | 初の安定版標準 | 1998 |
GHC 9.4 | Latest Compiler | パフォーマンス改善、型エラー改善 | 2022 |
GHC 9.2 | Stable Compiler | Record Dot Syntax、型族改善 | 2021 |
参考ページ
公式ドキュメント
- Haskell.org - 公式サイト
- GHC User's Guide - GHCユーザーガイド
- Hackage - パッケージリポジトリ
学習リソース
- Learn You a Haskell - 初心者向けガイド
- Real World Haskell - 実践的なHaskell
- Haskell Programming from First Principles - 包括的な学習書