Skip to content

Haskell 치트시트

Haskell은 강력한 정적 타이핑을 가진 순수 함수형 프로그래밍 언어입니다.

01

기본

Hello World

main이 진입점, putStrLn은 줄바꿈과 함께 출력합니다

haskell
-- GHCi interactive environment
-- launch: ghci  (quit with :q)

-- simple arithmetic
> 2 + 3
5

-- function application (no parentheses needed)
> succ 5
6

-- define a function
> double x = x * 2
> double 10
20

-- lists
> [1,2,3,4]
[1,2,3,4]

-- strings are lists of chars
> "hello"
"hello"

주석

{- -}는 여러 줄 주석용

haskell
-- single line comment

{- multi-line comment
   spanning multiple lines -}

{- nested {- comments -} are allowed -}

-- comments are ignored by the compiler

GHCi 대화형

:t는 타입 표시, :q는 종료

haskell
-- hello.hs
main :: IO ()
main = putStrLn "Hello, World!"

-- run interpreted
--   runhaskell hello.hs
-- or compile to an executable
--   ghc hello.hs && ./hello

let 바인딩

let은 do 블록에서 지역 변수를 정의합니다

haskell
-- in GHCi, 'let' defines a binding (no 'in' needed)
> let x = 5
> let double n = n * 2
> x + double x
15

-- in a source file, top-level bindings need no 'let'
y = 10

-- 'let' inside an expression needs 'in'
z = let a = 2 in a * a   -- 4

들여쓰기 규칙

Haskell은 중괄호 대신 들여쓰기를 사용합니다

haskell
-- :t shows the type of an expression
> :t 'a'
'a' :: Char

> :t True
True :: Bool

> :t "hi"
"hi" :: [Char]

> :t (+)
(+) :: Num a => a -> a -> a

-- :info shows typeclass info
> :info Num

Loading Files

:load loads a module into GHCi; :reload reloads it after edits. :browse lists everything a module exports. :main runs the program with command-line arguments. These make the REPL workflow smooth.

haskell
-- GHCi commands
> :load MyModule.hs    -- or :l
> :reload              -- or :r, reload after edits
> :type main           -- check a type
> :browse Data.List    -- list a module's exports
> :main arg1 arg2      -- run main with args

-- :set +t auto-prints types of results
02

타입

기본 타입

::는 타입을 명시적으로 주석 처리합니다

haskell
-- Int: bounded integer (platform-dependent)
x :: Int
x = 9

-- Integer: arbitrary-precision (unbounded)
big :: Integer
big = 2 ^ 100

-- Float / Double (prefer Double)
piVal :: Double
piVal = 3.14159

-- Bool
flag :: Bool
flag = True

-- Char (single Unicode character)
c :: Char
c = 'a'

함수 타입

->는 오른쪽 결합, 마지막이 반환 타입입니다

haskell
-- explicit type signature above a binding
age :: Int
age = 30

-- annotate an expression inline with ::
result = (5 :: Int) + 3

-- function signature
add :: Int -> Int -> Int
add x y = x + y

-- annotate a list
nums :: [Double]
nums = [1.0, 2.0, 3.0]

타입 변수

소문자는 타입 변수입니다

haskell
-- 'a' is a type variable (polymorphic)
id' :: a -> a
id' x = x

-- works for any type
> id' 5
5
> id' "hello"
"hello"

-- length works on any list
length :: [a] -> Int

Maybe 타입

Maybe는 실패할 수 있는 계산을 나타냅니다

haskell
-- '->' is right-associative
add :: Int -> Int -> Int
-- equivalent to: Int -> (Int -> Int)

-- takes a function as an argument
apply :: (a -> b) -> a -> b
apply f x = f x

-- returns a function
const' :: a -> b -> a
const' x y = x

Either 타입

Either는 오류 처리에 사용됩니다

haskell
-- Maybe: optional values
data Maybe a = Nothing | Just a

safeDiv :: Double -> Double -> Maybe Double
safeDiv _ 0 = Nothing
safeDiv x y = Just (x / y)

-- Either: a value or an error
data Either a b = Left a | Right b

-- convention: Left = error, Right = success
parse :: String -> Either String Int
parse s = Right (read s)

Tuples & List Types

Tuples (a, b) hold a fixed number of values of possibly different types. Lists [a] hold any number of values of the same type. A tuple's type encodes its size; a list's type does not.

haskell
-- tuple: (a, b)
pair :: (Int, String)
pair = (1, "one")

-- list: [a]
nums :: [Int]
nums = [1, 2, 3]

-- list of tuples
pairs :: [(Int, String)]
pairs = [(1, "a"), (2, "b")]

-- empty list keeps the polymorphic type
empty :: [a]
empty = []
03

타입 클래스

Eq 타입 클래스

Eq는 동등성 비교를 정의합니다

haskell
-- name, parameters, '=', body
double :: Int -> Int
double x = x * 2

-- application: just a space, no parentheses
> double 5
10

-- multiple parameters (curried)
add :: Int -> Int -> Int
add x y = x + y

> add 3 4
7

Show와 Read

Show/Read 타입 클래스

haskell
-- application is left-associative
> max 3 5
5
> max (max 3 5) 7
7

-- '$' has the lowest precedence, right-associative
> sum (map (*2) [1..5])
30
> sum $ map (*2) [1..5]   -- same, fewer parens

-- chain reads left to right
> putStrLn $ show $ sum [1..10]
55

Num과 Ord

Num은 숫자 연산, Ord는 순서 비교용

haskell
-- wrap an operator in () to use as a function
> (+) 2 3
5
> (*) 4 5
20

-- sections: partial application of operators
> (+10) 5
15
> (3/) 12
0.25
> (/3) 12
4.0

-- backticks make a function infix
> 10 `div` 3
3
> 10 `mod` 3
1

Functor 타입 클래스

Functor는 매핑 가능합니다

haskell
-- (.) composes functions right to left
(.) :: (b -> c) -> (a -> b) -> a -> c
(f . g) x = f (g x)

> negate . sum $ [1,2,3]
-6

-- chaining
fn = ceiling . negate . tan
> fn (pi/4)
-1

-- point-free style
odd' = not . even

타입 제약

=>의 왼쪽이 타입 제약입니다

haskell
-- functions are first-class values
applyTwice :: (a -> a) -> a -> a
applyTwice f x = f (f x)

> applyTwice (+3) 10
16
> applyTwice (\x -> x*x) 2
16

-- pass functions to higher-order functions
> map (*2) [1..5]
[2,4,6,8,10]
> filter even [1..6]
[2,4,6]

Infix & Prefix

Any binary function can be written infix between backticks (3 `elem` xs), and any operator can be written prefix in parentheses (mod 10 3). Choose whichever reads more naturally in context.

haskell
-- infix operators can be written prefix with ()
> mod 10 3
1
> div 10 3
3

-- functions can be written infix with backticks
> 10 `mod` 3
1
> 10 `div` 3
3

-- elem: membership test, often infix
> 3 `elem` [1,2,3]
True
04

함수

함수 정의

함수 호출에 괄호가 필요 없습니다

haskell
-- lists are homogeneous linked lists
nums :: [Int]
nums = [1, 2, 3, 4, 5]

-- strings are [Char]
chars = ['h','e','l','l','o']
str   = "hello"   -- same as chars

-- empty list
empty = []

-- cons (prepend) is O(1)
> 0 : [1,2,3]
[0,1,2,3]

다중 매개변수 함수

모든 함수는 단일 매개변수(커링)입니다

haskell
-- ranges
> [1..5]
[1,2,3,4,5]

-- with a step (first two elements)
> [2,4..10]
[2,4,6,8,10]

-- decreasing needs the step
> [5,4..1]
[5,4,3,2,1]

-- infinite lists (lazy)
> take 5 [1..]
[1,2,3,4,5]

> take 3 (cycle [1,2])
[1,2,1]

연산자를 함수로

()는 연산자를 함수로 감쌉니다

haskell
-- basic access (partial: crash on empty)
> head [1,2,3]
1
> tail [1,2,3]
[2,3]
> last [1,2,3]
3
> init [1,2,3]
[1,2]

-- safe helpers
> length [1,2,3]
3
> null []
True
> reverse [1,2,3]
[3,2,1]

함수 합성

. 연산자가 함수를 합성합니다

haskell
-- '++' concatenates two lists
> [1,2] ++ [3,4]
[1,2,3,4]

-- ':' prepends a single element (O(1))
> 1 : [2,3]
[1,2,3]

-- concat flattens one level
> concat [[1,2],[3,4],[5]]
[1,2,3,4,5]

-- intercalate joins with a separator
> intercalate ", " ["a","b","c"]
"a, b, c"

where 절

where는 함수 하단에 도우미를 정의합니다

haskell
-- like mathematical set-builder notation
> [x*2 | x <- [1..5]]
[2,4,6,8,10]

-- with a predicate (filter)
> [x | x <- [1..10], even x]
[2,4,6,8,10]

-- multiple generators = Cartesian product
> [(x,y) | x <- [1,2], y <- ['a','b']]
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]

-- with a let binding
> [x*y | x <- [1..3], let y = x+1]
[2,6,12]

Common List Functions

elem checks membership (often infix). sum and product reduce lists of numbers. take/drop extract or remove a prefix. splitAt splits at an index into a pair. All return new lists since lists are immutable.

haskell
-- membership (often written infix)
> elem 3 [1,2,3]
True
> 3 `elem` [1,2,3]
True

-- reductions
> sum [1..10]
55
> product [1..5]
120

-- take / drop a prefix
> take 3 [1..10]
[1,2,3]
> drop 3 [1..10]
[4,5,6,7,8,9,10]

-- split at an index
> splitAt 3 [1..6]
([1,2,3],[4,5,6])
05

패턴 매칭

기본 패턴 매칭

순서대로 매치, _는 모든 것에 매치

haskell
-- tuples can hold different types
pair :: (Int, String)
pair = (1, "hello")

triple :: (Int, String, Double)
triple = (1, "x", 3.14)

-- the size is part of the type
-- (1,2) and (1,2,3) are different types

리스트 패턴 매칭

:가 head와 tail을 분리합니다

haskell
-- fst/snd only work on pairs
> fst (1, "a")
1
> snd (1, "a")
"a"

-- swap exchanges the components
> swap (1, 2)
(2,1)

-- these do NOT work on triples
> fst (1, 2, 3)   -- type error!

튜플 패턴 매칭

튜플의 각 위치를 매치합니다

haskell
-- destructure a tuple by position
describe :: (Int, String) -> String
describe (n, s) = show n ++ ": " ++ s

-- ignore components with _
firstOf :: (a, b, c) -> a
firstOf (x, _, _) = x

-- in a list comprehension
> [n | (n, _) <- [(1,'a'),(2,'b')]]
[1,2]

as 패턴

@는 매치된 전체 값을 유지합니다

haskell
-- zip pairs elements from two lists
> zip [1,2,3] ['a','b','c']
[(1,'a'),(2,'b'),(3,'c')]

-- stops at the shorter list
> zip [1,2,3] [4,5]
[(1,4),(2,5)]

-- unzip splits a list of pairs
> unzip [(1,'a'),(2,'b')]
([1,2],"ab")

case 표현식

함수 본문 내에서 패턴 매칭을 위한 case

haskell
-- list: same type, variable length
ints :: [Int]
ints = [1, 2, 3]

-- tuple: mixed types, fixed length
mixed :: (Int, String, Bool)
mixed = (1, "x", True)

-- list of tuples (tabular data)
people :: [(String, Int)]
people = [("Alice",30),("Bob",25)]

-- cannot mix types in a list
> [1, "two"]   -- type error
06

가드

기본 가드

otherwise는 True의 별칭입니다

haskell
-- match on literals, top to bottom
lucky :: Int -> String
lucky 7 = "LUCKY SEVEN!"
lucky _ = "Sorry, you're out of luck"

> lucky 7
"LUCKY SEVEN!"
> lucky 13
"Sorry, you're out of luck"

다중 매개변수 가드

가드는 순서대로 검사합니다

haskell
-- empty list
isEmpty [] = True
isEmpty _  = False

-- head and tail (cons pattern)
head' :: [a] -> a
head' (x:_) = x

-- first two elements
firstTwo :: [a] -> [a]
firstTwo (x:y:_) = [x,y]
firstTwo xs      = xs

가드와 where

where는 모든 가드에서 공유될 수 있습니다

haskell
-- match by position
addPair :: (Int, Int) -> Int
addPair (x, y) = x + y

-- nested patterns
getAge :: (String, (Int, Int)) -> Int
getAge (_, (age, _)) = age

-- ignore with _
nameOf :: (String, Int) -> String
nameOf (name, _) = name

As Patterns

As-patterns (name@pattern) bind a name to the whole matched value while also destructuring it. Useful when you need both the original and its parts, avoiding costly reconstruction.

haskell
-- '@' keeps the whole value AND its parts
firstLetter :: String -> String
firstLetter ""        = "Empty string!"
firstLetter all@(x:_) =
  "The first letter of " ++ all ++ " is " ++ [x]

> firstLetter "Haskell"
"The first letter of Haskell is H"

Case Expressions

case performs pattern matching within an expression. It's useful when a function's body needs to branch on a value computed after some setup. Patterns are tried in order; add a catch-all if needed.

haskell
-- pattern match inside any expression
describe :: [a] -> String
describe xs =
  case xs of
    []  -> "empty"
    [_] -> "singleton"
    _   -> "longer"

-- case on any value, e.g. an Ordering
classify n = case compare n 0 of
  LT -> "negative"
  EQ -> "zero"
  GT -> "positive"

Constructor Patterns

Patterns can match data constructors like Red, Nothing, and Just. This is how algebraic data types are deconstructed. The compiler warns about non-exhaustive patterns, so cover all constructors.

haskell
-- match on data constructors
data Color = Red | Green | Blue

toString :: Color -> String
toString Red   = "red"
toString Green = "green"
toString Blue  = "blue"

-- match on Maybe
fromMaybe :: a -> Maybe a -> a
fromMaybe d Nothing  = d
fromMaybe _ (Just x) = x
07

Where / Let

where 절

where는 함수 정의의 하단에 있습니다

haskell
-- '|' tests boolean conditions in order
bmiTell :: Double -> String
bmiTell bmi
  | bmi <= 18.5 = "underweight"
  | bmi <= 25.0 = "normal"
  | bmi <= 30.0 = "overweight"
  | otherwise   = "obese"

> bmiTell 22.0
"normal"

let-in 표현식

let-in은 표현식이며, 어디서나 사용 가능합니다

haskell
-- otherwise is just True
otherwise :: Bool
otherwise = True

-- a signum implementation
signum' :: Int -> Int
signum' n
  | n < 0     = -1
  | n == 0    = 0
  | otherwise = 1

do 블록에서 let

do 블록의 let은 in이 필요 없습니다

haskell
-- multiple parameters
max' :: Int -> Int -> Int
max' a b
  | a > b     = a
  | otherwise = b

-- shared helpers via where
bmiTell :: Double -> Double -> String
bmiTell w h
  | bmi <= 18.5 = "underweight"
  | bmi <= 25.0 = "normal"
  | otherwise   = "overweight"
  where bmi = w / h^2

리스트 컴프리헨션에서 let

let은 리스트 컴프리헨션에서 사용할 수 있습니다

haskell
-- pattern matching: fixed values/structure
factorial 0 = 1
factorial n = n * factorial (n-1)

-- guards: ranges and conditions
clamp :: Int -> Int -> Int -> Int
clamp lo hi x
  | x < lo     = lo
  | x > hi     = hi
  | otherwise  = x

if-then-else

In Haskell, 'if' is an expression that always returns a value—both branches are required. For multi-way branches, guards are cleaner than nested if-then-else. if-then-else suits simple two-way choices.

haskell
-- 'if' is an expression that returns a value
abs' :: Int -> Int
abs' n = if n < 0 then -n else n

-- nested ifs (prefer guards for readability)
grade :: Int -> Char
grade n =
  if n >= 90 then 'A'
  else if n >= 80 then 'B'
  else if n >= 70 then 'C'
  else 'F'
08

리스트

리스트 기본

리스트는 같은 타입 요소의 연결 리스트입니다

haskell
-- where: definitions at the bottom, scoped to a function
area :: Double -> Double -> Double
area w h = w * h_adj
  where
    h_adj = h * 0.9    -- adjust height
    scale = 0.9        -- a helper

-- definitions can refer to each other
f x = a + b
  where a = x * 2
        b = a + 1      -- refers to a

범위 리스트

스텝과 무한 리스트를 지원합니다

haskell
-- let <bindings> in <expr>
cylinder :: Double -> Double -> Double
cylinder r h =
  let side = 2 * pi * r * h
      top  = pi * r^2
  in  side + 2 * top

-- scoped to the expression only
> let z = 5 in z * z
25
-- z is not in scope here

리스트 연산

기본 리스트 연산 함수

haskell
-- in do, 'let' needs no 'in'
main = do
  let x = 5
      y = 10
  putStrLn $ "sum = " ++ show (x + y)

-- in a list comprehension, no 'in'
> [x*y | x <- [1..3], let y = x+1]
[2, 6, 12]

리스트 연결

++는 연결, :는 head에 추가합니다

haskell
-- where bindings support pattern matching
describe :: [a] -> String
describe xs = "head is " ++ show first
  where (first:_) = xs

-- multiple where bindings
analyze :: [Int] -> String
analyze xs =
  "sum=" ++ show s ++ ", len=" ++ show l
  where s = sum xs
        l = length xs

리스트 컴프리헨션

수학적 집합 컴프리헨션과 유사합니다

haskell
-- let: an expression, available wherever expressions go
-- (needs 'in', except in do/comprehension)

-- where: a declaration, attached to a function
-- (cannot be nested as an expression)

-- same result, two styles:
-- with let:
f x = let y = x+1 in y*y

-- with where:
f' x = y*y where y = x+1
09

튜플

튜플 기본

튜플은 다른 타입을 포함할 수 있습니다

haskell
-- base case + recursive case
factorial :: Int -> Int
factorial 0 = 1
factorial n = n * factorial (n - 1)

> factorial 5
120

튜플 함수

fst/snd는 쌍에만 작동합니다

haskell
-- match [] then (x:xs)
length' :: [a] -> Int
length' []     = 0
length' (_:xs) = 1 + length' xs

sum' :: Num a => [a] -> a
sum' []     = 0
sum' (x:xs) = x + sum' xs

튜플 패턴 매칭

패턴 매칭이 튜플을 비구조화합니다

haskell
-- carry the running result in an accumulator
factorial :: Int -> Int
factorial n = go n 1
  where
    go 0 acc = acc
    go k acc = go (k-1) (k*acc)

-- sum with an accumulator
sumTo :: Int -> Int
sumTo n = go n 0
  where go 0 acc = acc
        go k acc = go (k-1) (k+acc)

Quick Sort

This famous example shows Haskell's expressiveness: list comprehensions split around a pivot, then recursively sort each part. Not the most efficient quicksort, but a beautiful demo of recursion and comprehensions.

haskell
-- elegant but not in-place quicksort
qsort :: Ord a => [a] -> [a]
qsort [] = []
qsort (p:xs) =
  qsort smaller ++ [p] ++ qsort larger
  where
    smaller = [x | x <- xs, x <  p]
    larger  = [x | x <- xs, x >= p]

> qsort [3,1,4,1,5,9,2,6]
[1,1,2,3,4,5,6,9]

Mutual Recursion

Mutual recursion is when two or more functions call each other. Here even' and odd' reduce toward the base case 0. Both must be defined (order doesn't matter in a module).

haskell
-- functions calling each other
even' :: Int -> Bool
even' 0 = True
even' n = odd' (n - 1)

odd' :: Int -> Bool
odd' 0 = False
odd' n = even' (n - 1)

> even' 4
True
10

고차 함수

map

map은 각 요소에 함수를 적용합니다

haskell
-- map applies a function to every element
> map (*2) [1,2,3]
[2,4,6]

> map (+1) [1..5]
[2,3,4,5,6]

-- with a lambda
> map (\x -> x*x) [1..4]
[1,4,9,16]

-- strings are lists of chars
> map toUpper "hello"
"HELLO"

filter

filter는 술어를 만족하는 요소를 유지합니다

haskell
-- keep elements satisfying a predicate
> filter even [1..10]
[2,4,6,8,10]

> filter (>3) [1,2,3,4,5]
[4,5]

-- with a lambda
> filter (\c -> c /= ' ') "hello world"
"helloworld"

-- count matches
> length (filter odd [1..100])
50

foldl과 foldr

foldl은 왼쪽 접기, foldr은 오른쪽 접기

haskell
-- foldl: left fold (function, seed, list)
> foldl (+) 0 [1..5]
15

-- foldr: right fold
> foldr (+) 0 [1..5]
15

-- associativity differs:
-- foldl: ((((0+1)+2)+3)+4)+5
-- foldr: 1+(2+(3+(4+(5+0))))

-- foldl1/foldr1 use the first element as seed
> foldl1 max [3,1,4,1,5]
5

zipWith

zipWith는 두 리스트를 함수로 결합합니다

haskell
-- apply a function to paired elements
> zipWith (+) [1,2,3] [10,20,30]
[11,22,33]

> zipWith (*) [1,2,3,4] [2,2,2,2]
[2,4,6,8]

-- combine strings
> zipWith (\a b -> a ++ b) ["a","b"] ["1","2"]
["a1","b2"]

-- stops at the shorter list
> zipWith max [1,5,3] [2,4,6,8]
[2,5,6]

함수 적용 $

$ 연산자는 괄호를 줄입니다

haskell
-- scanl returns all intermediate accumulators
> scanl (+) 0 [1,2,3]
[0,1,3,6]

-- scanr from the right
> scanr (+) 0 [1,2,3]
[6,5,3,0]

-- running maximum
> scanl max 0 [3,1,4,1,5]
[0,3,3,4,4,5]

takeWhile & dropWhile

takeWhile stops at the first element failing the predicate; dropWhile drops the prefix that satisfies it. span splits a list at the first failing element. Great for prefix-based processing.

haskell
-- takeWhile: take while the predicate holds
> takeWhile (<5) [1,2,3,4,5,4,3]
[1,2,3,4]

-- dropWhile: drop while the predicate holds
> dropWhile (<5) [1,2,3,4,5,4,3]
[5,4,3]

-- span splits at the first failing element
> span (<3) [1,2,3,4,5]
([1,2],[3,4,5])

-- first word of a string
> takeWhile (/= ' ') "hello world"
"hello"
11

Map / Filter / Fold

체인 연산

map/filter/fold 결합

haskell
-- '\' defines a lambda (resembles the λ symbol)
> (\x -> x + 1) 5
6

-- used with map
> map (\x -> x * x) [1..4]
[1,4,9,16]

-- used with filter
> filter (\x -> x > 2) [1,2,3,4]
[3,4]

scanl/scanr

scan은 모든 중간 결과를 유지합니다

haskell
-- parameters separated by spaces
> (\x y -> x + y) 3 4
7

-- in zipWith
> zipWith (\x y -> x * y + 1) [1,2,3] [10,20,30]
[11,21,31]

takeWhile/dropWhile

조건으로 요소 가져오기/버리기

haskell
-- lambdas can pattern match their one argument
> map (\(a,b) -> a + b) [(1,2),(3,4)]
[3,7]

-- only ONE pattern, no fallthrough
-- this crashes if it doesn't match:
> (\(x:_) -> x) []   -- runtime error

Lambdas in Higher-Order

Lambdas shine with fold, filter, and map. They can return other functions (\x -> \y -> ...), demonstrating currying. Keep them short—long lambdas are clearer as named functions.

haskell
-- combining with fold
> foldl (\acc x -> acc + x*2) 0 [1,2,3]
12

-- with filter (infix via backticks)
> length . filter (\x -> x `mod` 2 == 0) $ [1..10]
5

-- a lambda returning a function
> (\x -> \y -> x + y) 3 4
7

Point-free Style

Point-free style omits explicit arguments, expressing functions via composition and application. It's concise but can hurt readability if overdone. Prefer it for short chains where the flow is obvious.

haskell
-- explicit: name every argument
sumSquares xs = sum (map (^2) xs)

-- point-free: omit arguments via composition
sumSquares' = sum . map (^2)

-- more examples
count p  = length . filter p
oddNums  = filter odd
negateAll = map negate
12

람다

익명 함수

\는 람다를 정의, λ와 같습니다

haskell
-- all functions are curried (one arg at a time)
add :: Int -> Int -> Int
add x y = x + y

-- apply fewer args to get a function
add5 :: Int -> Int
add5 = add 5

> add5 10
15

-- with map
> map (add 5) [1,2,3]
[6,7,8]

다중 매개변수 람다

여러 매개변수는 공백으로 구분됩니다

haskell
-- left section (fix the left operand)
> map (+3) [1,2,3]
[4,5,6]

-- right section (fix the right operand)
> map (3/) [9, 12, 18]
[0.333..,0.25,0.166..]

-- (-3) is the NUMBER, not a section!
> map (subtract 3) [4,5,6]
[1,2,3]

-- comparison sections
> filter (>3) [1..6]
[4,5,6]

패턴 매칭이 있는 람다

람다에서 패턴 매칭

haskell
-- flip swaps the first two arguments
flip :: (a -> b -> c) -> b -> a -> c
flip f x y = f y x

-- fix the second argument instead of the first
> map (flip (-) 1) [3,4,5]
[2,3,4]

-- divide from the other side
> map (flip div 2) [4,6,9]
[2,3,4]

Currying Explained

a -> b -> c means a -> (b -> c): a function taking 'a' and returning a function. Application is left-associative: f x y = (f x) y. This is why partial application works naturally.

haskell
-- this signature:
max :: Int -> Int -> Int

-- is actually: Int -> (Int -> Int)
-- max takes an Int and returns a function

-- step by step
> :t max 5
max 5 :: Int -> Int

> (max 5) 10
10

-- application is left-associative
> max 5 10 == ((max 5) 10)
True

Functions Returning Functions

Functions can return other functions, naturally arising from currying. This enables function factories: multiplyBy 2 creates a doubler. Partial application like greet "Hi" configures a function for later use.

haskell
-- return a function
multiplyBy :: Int -> (Int -> Int)
multiplyBy n = \x -> n * x

-- or equivalently (currying)
multiplyBy' n x = n * x

> let double = multiplyBy 2
> double 21
42

-- configure a function via partial application
greet :: String -> String -> String
greet greeting name = greeting ++ ", " ++ name
sayHi = greet "Hi"   -- a configured greeter
13

커링

부분 적용

모든 함수는 자동으로 커링됩니다

haskell
-- Eq: equality
> 5 == 5
True
> "a" /= "b"
True

-- Ord: ordering
> 3 < 5
True
> compare 3 5
LT

-- Show: convert to a string
> show 5
"5"
> show [1,2,3]
"[1,2,3]"

-- Read: parse from a string
> read "5" + 2
7

중위 부분 적용

연산자의 부분 적용

haskell
-- '=>' constrains the type variables
(==) :: Eq a => a -> a -> Bool
(<)  :: Ord a => a -> a -> Bool
show :: Show a => a -> String

-- a function requiring Eq
elem :: Eq a => a -> [a] -> Bool
elem _ []     = False
elem x (y:ys) = x == y || elem x ys

-- multiple constraints
showMax :: (Ord a, Show a) => [a] -> String
showMax xs = show (maximum xs)

flip 함수

flip은 인자 순서를 바꿉니다

haskell
-- declare a typeclass with method signatures
class Eq a where
  (==) :: a -> a -> Bool
  (/=) :: a -> a -> Bool
  -- default implementations
  x == y = not (x /= y)
  x /= y = not (x == y)

-- minimal: define either (==) or (/=)

Instance Declarations

instance Typeclass Type where implements a typeclass for a type. Each method must be defined unless a default exists. After this, TrafficLight values can use ==, /=, and show.

haskell
data TrafficLight = Red | Yellow | Green

instance Eq TrafficLight where
  Red == Red         = True
  Yellow == Yellow   = True
  Green == Green     = True
  _ == _             = False

instance Show TrafficLight where
  show Red    = "Red light"
  show Yellow = "Yellow light"
  show Green  = "Green light"

Numeric Typeclasses

The numeric hierarchy includes Num (all numbers), Fractional (supports /), and Integral (supports div, mod). Int and Double are different types—use fromIntegral to convert between them safely.

haskell
-- Num: basic numeric operations
(+) :: Num a => a -> a -> a
(*) :: Num a => a -> a -> a
negate :: Num a => a -> a

-- Fractional: supports division
(/) :: Fractional a => a -> a -> a

-- Integral: integer division
div, mod :: Integral a => a -> a -> a

-- convert between numeric types
> fromIntegral (length [1,2,3]) + 0.5
3.5

Deriving

Many typeclasses can be derived automatically: Eq, Ord, Show, Read, Enum, Bounded. This avoids writing boilerplate instances. Enum and Bounded enable range syntax ([Red ..]) and minBound/maxBound.

haskell
-- automatically derive common typeclasses
data Color = Red | Green | Blue
  deriving (Eq, Ord, Show, Read, Enum, Bounded)

> show Red
"Red"
> Red < Green
True
> minBound :: Color
Red
> [Red ..]
[Red,Green,Blue]
14

합성

함수 합성

.는 오른쪽에서 왼쪽으로 합성합니다

haskell
-- sum type: alternatives separated by '|'
data Bool' = False' | True'

-- pattern match on constructors
not' :: Bool' -> Bool'
not' True'  = False'
not' False' = True'

-- value constructors with no fields
data Weekday = Mon | Tue | Wed | Thu | Fri

다중 함수 합성

여러 함수를 체인 합성

haskell
-- product type: constructors carry fields
data Point = Point Double Double

data Shape
  = Circle Double         -- radius
  | Rect Double Double    -- width, height

-- construct values
p = Point 1.0 2.0
c = Circle 5.0
r = Rect 3.0 4.0

포인트 프리 스타일

인자를 생략, 더 간결합니다

haskell
-- a type parameter 'a' (like Maybe)
data Maybe' a = Nothing' | Just' a

-- a boxed value
data Box a = Box a

-- use it with any type
> Just' 5
Just' 5
> Box "hello"
Box "hello"

-- model failure with Maybe
divide :: Double -> Double -> Maybe' Double
divide _ 0 = Nothing'
divide x y = Just' (x/y)

Recursive Types

A type can refer to itself, forming recursive structures. IntList is a singly-linked list of Ints. Tree a is a binary tree. Pattern matching naturally recurses over such structures.

haskell
-- a custom linked list
data IntList = INil | ICons Int IntList

-- build the list 1,2,3
xs = ICons 1 (ICons 2 (ICons 3 INil))

-- a binary tree
data Tree a = Leaf | Node a (Tree a) (Tree a)

tree = Node 1 (Node 2 Leaf Leaf) (Node 3 Leaf Leaf)

A BST with Functions

Algebraic data types pair naturally with recursive functions via pattern matching. This BST implementation inserts while keeping order, and inorder traversal flattens to a sorted list. ADTs plus recursion replace classes in OOP.

haskell
data Tree a = Leaf | Node a (Tree a) (Tree a)

-- insert into a binary search tree
insert :: Ord a => a -> Tree a -> Tree a
insert x Leaf = Node x Leaf Leaf
insert x (Node y l r)
  | x < y     = Node y (insert x l) r
  | otherwise = Node y l (insert x r)

-- inorder traversal yields a sorted list
inorder :: Tree a -> [a]
inorder Leaf         = []
inorder (Node x l r) = inorder l ++ [x] ++ inorder r

Enumerations

A sum type of nullary constructors acts like an enum. Deriving Enum enables succ/pred and range syntax ([North ..]); deriving Bounded gives minBound/maxBound. This is Haskell's idiomatic enumeration.

haskell
-- enum-like sum type
data Direction = North | East | South | West
  deriving (Eq, Ord, Enum, Bounded, Show)

-- enumerate via Enum
> [North .. West]
[North,East,South,West]

-- Bounded: the limits
> minBound :: Direction
North
> maxBound :: Direction
West

-- cycle through directions
next :: Direction -> Direction
next West = North
next d    = succ d
15

모나드

Maybe 모나드

Maybe는 실패 전파를 자동으로 처리합니다

haskell
-- without records (positional, error-prone)
data Person0 = Person0 String Int String

-- with records (named fields)
data Person = Person
  { name :: String
  , age  :: Int
  , city :: String
  }

-- construct with field names (any order)
p1 = Person { name = "Alice", age = 30, city = "NYC" }

리스트 모나드

리스트 모나드는 비결정적 계산을 나타냅니다

haskell
-- field names are accessor functions
> name p1
"Alice"
> age p1
30

-- their types
> :t name
name :: Person -> String
> :t age
age :: Person -> Int

-- record patterns also work
greet :: Person -> String
greet (Person { name = n }) = "Hi, " ++ n

IO 모나드

IO 모나드는 부작용을 격리합니다

haskell
-- functional update: change only some fields
p2 = p1 { age = 31 }

-- the original is unchanged (immutable)
> age p1
30
> age p2
31

-- update multiple fields at once
p3 = p1 { age = 31, city = "LA" }

bind 연산자

>>=는 핵심 모나드 연산입니다

haskell
-- match and bind fields by name
birthday :: Person -> Person
birthday p@Person { age = a } = p { age = a + 1 }

-- ignore unneeded fields
getName :: Person -> String
getName Person { name = n } = n

-- as-pattern keeps the whole record
isAdult :: Person -> Bool
isAdult p@Person { age = a } = a >= 18

return

return은 값을 모나드 컨텍스트에 넣습니다

haskell
-- field names share a top-level namespace
data Person  = Person  { name :: String }
data Company = Company { name :: String }
-- ERROR: 'name' clashes in the same module

-- workaround 1: prefix the fields
data Person'  = Person'  { personName :: String }
data Company' = Company' { companyName :: String }

-- workaround 2: language extensions
{-# LANGUAGE DuplicateRecordFields #-}
16

펑터

fmap

fmap은 Functor 내부의 값에 함수를 적용합니다

haskell
-- file Geometry.hs
module Geometry
  ( sphereVolume    -- explicit export list
  , cubeVolume
  ) where

sphereVolume :: Float -> Float
sphereVolume r = (4/3) * pi * r^3

cubeVolume :: Float -> Float
cubeVolume s = s^3

-- a helper not in the export list is private
privateHelper = ...

<$> 연산자

<$>는 fmap과 동등합니다

haskell
-- import everything from Data.List
import Data.List

-- names are now in scope
> sort [3,1,2]
[1,2,3]
> nub [1,1,2,2,3]
[1,2,3]

-- import specific functions
import Data.Maybe (fromMaybe)

-- qualified: use the full module name
import qualified Data.Map as M
> M.empty

함수 펑터

함수의 fmap은 .입니다

haskell
-- import only specific names
import Data.List (sort, nub, group)

-- import everything except some
import Data.List hiding (head, tail)

-- import a type without its constructors
import Data.Tree (Tree, Node)

-- import constructors too
import Data.Tree (Tree(..))

Qualified Imports

Qualified imports force the module prefix, preventing clashes (e.g., Prelude's filter vs Data.Map's). import qualified M as M is idiomatic. Use qualified for modules with many overlapping names.

haskell
-- avoid name clashes with qualified
import qualified Data.Map as Map
import qualified Data.Set as Set

m = Map.empty
s = Set.fromList [1,2,3]

-- Prelude's filter vs Data.Map's
> filter even [1..6]
[2,4,6]
> Map.filter (>2) (Map.fromList [(1,'a'),(3,'b')])
fromList [(3,'b')]

Prelude

Prelude is the default import, providing common functions and types. Hide entries to avoid clashes or to use better alternatives (e.g., foldl' from Data.List). Many projects use custom preludes.

haskell
-- Prelude is imported by default
-- provides: map, filter, length, (+), etc.

-- hide entries to avoid clashes
import Prelude hiding (head, tail, foldl)

-- use better alternatives
import Data.List (foldl')   -- strict fold

-- common standard modules
import Data.List
import Data.Maybe
import Data.Char
import Data.Either

Module Hierarchy

Module names are hierarchical and mirror the directory structure: A.B.C lives in A/B/C.hs. A module can re-export another with 'module M' in its export list, useful for bundling a public API.

haskell
-- hierarchical names mirror directories
-- module Data.Geometry.Sphere where
-- lives in: Data/Geometry/Sphere.hs

-- import nested modules
import Data.Geometry.Sphere
import Data.Geometry.Cube

-- re-export a whole module
module MyMod
  ( module Data.List    -- re-export
  , myFunc
  ) where

import Data.List
myFunc = sort
17

어플리커티브

<*> 연산자

<*>는 Functor 내부의 함수를 적용합니다

haskell
-- IO actions produce effects when run
greet :: IO ()
greet = do
  putStrLn "What's your name?"
  name <- getLine
  putStrLn ("Hello, " ++ name)

-- run in GHCi
> greet
What's your name?
Alice
Hello, Alice

순수 함수 적용

pure는 값을 어플리커티브에 넣습니다

haskell
-- every program starts in main
main :: IO ()
main = putStrLn "Hello, World!"

-- combine actions with do
main = do
  greet
  putStrLn "Done."

-- command-line arguments
import System.Environment
main = do
  args <- getArgs
  print args

리스트 어플리커티브

리스트의 <*>는 데카르트 곱입니다

haskell
-- do is sugar for sequential actions
echo = do
  s <- getLine
  putStrLn s

-- desugars to bind (>>=)
echo' = getLine >>= putStrLn

-- 'let' needs no 'in' inside do
greet = do
  let msg = "Hi!"
  putStrLn msg

Input Functions

getLine reads a line (without newline), getChar a single character, getContents lazily reads all of stdin. Use '<-' to bind results. getContents is lazy—great for streaming pipelines.

haskell
-- read a line (without the newline)
> line <- getLine
hello world
> line
"hello world"

-- read a single character
> c <- getChar
a> c
'a'

-- read all of stdin lazily
process = do
  text <- getContents
  putStr (map toUpper text)

Output Functions

putStrLn and putStr write a String to stdout, with/without a trailing newline. print equals putStrLn . show, so it adds quotes around strings. Choose based on newline needs and whether the value is a String.

haskell
-- with a trailing newline
putStrLn :: String -> IO ()
-- without a newline
putStr   :: String -> IO ()
-- any Show-able value
print    :: Show a => a -> IO ()

> putStrLn "hi"   -- hi
> putStr "hi"     -- hi (no newline)
> print 5         -- 5
> print "hi"      -- "hi" (with quotes)

File IO

readFile reads lazily, writeFile overwrites, appendFile adds to the end. Use bracket to ensure resources close on exceptions. Files are addressed by FilePath (an alias for String).

haskell
-- read a whole file (lazy)
readFile :: FilePath -> IO String

-- write a file (overwrites)
writeFile :: FilePath -> String -> IO ()

-- append to a file
appendFile :: FilePath -> String -> IO ()

main = do
  contents <- readFile "input.txt"
  writeFile "output.txt" (map toUpper contents)

-- safe resource handling
import Control.Exception (bracket)
18

IO

기본 IO

IO 연산은 IO 모나드에 있습니다

haskell
-- Functor: a context you can map over
class Functor f where
  fmap :: (a -> b) -> f a -> f b

-- '<$>' is fmap as an operator
> fmap (+1) (Just 5)
Just 6
> (+1) <$> Just 5
Just 6
> (+1) <$> Nothing
Nothing
> map (+1) [1,2,3]   -- map is fmap for lists
[2,3,4]

파일 읽기/쓰기

readFile/writeFile이 파일을 처리합니다

haskell
-- Applicative: functions inside a context
class Applicative f where
  pure  :: a -> f a
  (<*>) :: f (a -> b) -> f a -> f b

-- apply a wrapped function
> pure (+) <*> Just 3 <*> Just 4
Just 7
> Just (+1) <*> Just 5
Just 6

-- for lists, <*> is the Cartesian product
> [(+1), (*2)] <*> [1,2,3]
[2,3,4,2,4,6]

putStr/putChar

다른 출력 함수

haskell
-- Monad: sequential, dependent computations
class Monad m where
  return :: a -> m a
  (>>=)  :: m a -> (a -> m b) -> m b   -- bind

-- bind chains dependent computations
> Just 3 >>= \x -> Just (x+1)
Just 4
> Nothing >>= \x -> Just (x+1)
Nothing

-- '>>' ignores the left result
> putStrLn "a" >> putStrLn "b"
a
b

상호작용

getLine은 입력 한 줄을 읽습니다

haskell
-- do notation
greet = do
  name <- getLine
  putStrLn ("Hi " ++ name)

-- desugars to bind
greet' = getLine >>= \name ->
         putStrLn ("Hi " ++ name)

-- 'let' becomes a regular binding
compute = do
  let x = 5
  print (x * x)
-- desugars to:
compute' = let x = 5 in print (x*x)

Maybe Monad Chain

Maybe's Monad short-circuits: once a step returns Nothing, the whole computation is Nothing without further work. do hides the manual Nothing-checking, making failure propagation automatic and clean.

haskell
-- short-circuit on Nothing
safeDiv :: Double -> Double -> Maybe Double
safeDiv _ 0 = Nothing
safeDiv x y = Just (x / y)

-- chain with do (auto-propagates failure)
calc = do
  a <- safeDiv 10 2
  b <- safeDiv a 2
  return (b + 1)

> calc
Just 3.5

-- any Nothing stops the whole chain
> safeDiv 10 0 >>= \a -> safeDiv a 2
Nothing

List Monad

The list monad models non-determinism: each bind explores all possibilities, producing the Cartesian product. do over lists is equivalent to list comprehensions. This unifies comprehension and monadic styles.

haskell
-- list bind = non-deterministic computation
> [1,2] >>= \x -> [x, x*10]
[1,10,2,20]

-- do over lists == list comprehension
pairs = do
  x <- [1,2]
  y <- ['a','b']
  return (x, y)

> pairs
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]

-- equivalent comprehension
> [(x,y) | x <- [1,2], y <- ['a','b']]
19

Do 표기법

기본 do

do 구문 설탕이 모나드 연산을 단순화합니다

haskell
-- 'type' creates a synonym (not a new type)
type String = [Char]

type PhoneNumber = String
type Name = String

-- improves readability of signatures
printEntry :: Name -> PhoneNumber -> IO ()
printEntry name num = putStrLn (name ++ ": " ++ num)

do에서 let

do의 let은 in이 필요 없습니다

haskell
-- newtype: one constructor, one field, zero cost
newtype Dollars = Dollars Double
newtype Count   = Count Int

-- pattern match or unwrap the field
addDollars :: Dollars -> Dollars -> Dollars
addDollars (Dollars a) (Dollars b) = Dollars (a + b)

-- distinct type: can't mix with raw Double
total :: Dollars
total = addDollars (Dollars 10) (Dollars 20)

do 디슈가링

do는 >>=에 대한 구문 설탕입니다

haskell
-- type: just a synonym (no new type, no safety)
type Score = Int

-- newtype: distinct type, zero cost, one field
newtype Score' = Score' Int

-- data: full ADT, runtime cost,
-- can have multiple constructors/fields
data Score'' = Score'' Int | Bonus Int

-- newtype is strict: exactly one
-- constructor and one field, but a distinct type

newtype for New Instances

newtype lets you attach different typeclass instances to the same underlying type. Sum and Product wrap the same number but have different Monoid instances (addition vs multiplication). A common, powerful pattern.

haskell
-- wrap to attach different typeclass instances
newtype Sum a = Sum { getSum :: a }

instance Num a => Monoid (Sum a) where
  mempty = Sum 0
  Sum a `mappend` Sum b = Sum (a + b)

newtype Product a = Product { getProduct :: a }
-- different Monoid: multiplication

-- same underlying type, different behavior
> getSum (Sum 3 <> Sum 4)
7
> getProduct (Product 3 <> Product 4)
12

Parameterized Synonyms

Type synonyms can take parameters, acting as lightweight abstractions. AssocList k v is [(k,v)], making signatures self-documenting. Use them for clarity, but remember they're just aliases with no type safety.

haskell
-- type synonyms can take parameters
type AssocList k v = [(k, v)]

-- used in signatures (self-documenting)
lookup :: Eq k => k -> AssocList k v -> Maybe v
lookup _ [] = Nothing
lookup k ((k',v):rest)
  | k == k'   = Just v
  | otherwise = lookup k rest

-- more examples
type Parser a = String -> Maybe (a, String)
type Matrix a = [[a]]

Was this helpful?