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

参考ページ

公式ドキュメント

学習リソース

フレームワークとライブラリ

  • Yesod - Webフレームワーク
  • Snap - 軽量Webフレームワーク
  • Pandoc - ドキュメント変換ツール