필사 모드: 모던 Haskell 2026 — GHC 9.10 / 9.12 / GHCup / Cabal 3.14 / Stack / IHP / Servant / Effectful / Pandoc / Cardano 심층 가이드
한국어프롤로그 — 2026년에도 Haskell을 쓰는 사람들이 있다
매년 누군가는 묻는다. "Haskell, 아직도 써요?"
쓴다. 그것도 꽤 진지하게 쓴다.
- **GitHub**은 Semantic을 Haskell로 짰고, 코드 검색 일부 인프라가 그 코드 위에서 돈다.
- **Cardano**는 IOHK가 만든 Plutus 스마트 컨트랙트 플랫폼이고, 핵심 노드는 전부 Haskell이다.
- **Mercury Bank**, **Channable**, **Tweag**, **IOG**, **Standard Chartered**, **Anduril**, **Wire**, **Hasura**는 프로덕션 Haskell 코드베이스를 운영한다.
- **Pandoc** — John MacFarlane이 짠 문서 변환기는 학계와 출판계의 표준이다. 이 블로그도 마크다운에서 PDF로 갈 때 결국 Pandoc을 거친다.
- **Shellcheck**, **xmonad**, **darcs**, **postgrest**, **purescript**, **elm 컴파일러(2018년 0.19 기준)**, **dhall**, **agda**, **idris**도 Haskell이다.
2010년대 Haskell의 적은 "쓰기 어렵다"가 아니었다. 진짜 적은 **빌드 도구가 둘이고 — Cabal과 Stack — 둘 다 미묘하게 다른 방식으로 망가져 있다**는 것이었다. 그게 2026년에는 어떻게 되어 있을까.
답: **GHCup이 둘을 동시에 잘 관리한다**. 그래서 더 이상 "Cabal 파일을 받았는데 Stack 사람이 보내준 거라 안 빌드된다" 같은 일은 거의 없다. 그리고 GHC 자체도 9.10·9.12에서 한 단계 더 빨라졌고, 모듈식 컴파일과 다중 코어 활용이 개선되었다.
이 글은 2026년의 Haskell 스택을 한 호흡으로 본다. 컴파일러부터 빌드 도구, 풀스택 웹 프레임워크, 효과 라이브러리, ORM, 파서, JSON, 테스트, 정형 검증, ML, 문서 변환, 그리고 사람들까지.
1장 · 2026년 모던 Haskell — GHC 9.x의 시대
먼저 한 장의 풍경.
모던 Haskell 2026 스택
[설치] GHCup (권장) ── ghc / cabal / stack / hls
[컴파일러] GHC 9.10 (2024.5) / 9.12 (2024.12)
[빌드] Cabal 3.14 | Stack (LTS 22.x, Stackage)
[패키지] Hackage (전체) | Stackage (큐레이션 LTS)
[웹 풀스택] IHP (Rails 스타일, digitally induced)
[웹 API] Servant (타입드 HTTP DSL)
[웹 그 외] Yesod / Snap / Scotty / Spock
[효과] Effectful (mtl 대체, 2026 대세)
Polysemy / fused-effects / Bluefin
[ORM] Persistent / Beam / Esqueleto
[파서] Megaparsec / Attoparsec / Earley
[JSON] Aeson
[문자열·바이트] text / bytestring / vector
[테스트] hedgehog (property) / tasty / hspec / QuickCheck
[정형 검증] Liquid Haskell (refinement types)
[ML] Hasktorch (libtorch 바인딩)
[문서 변환] Pandoc (John MacFarlane)
[프로덕션 사용자] GitHub / Cardano / Mercury / IOG /
Hasura / Anduril / Channable / Tweag
이게 2026년의 Haskell 지형도다. 박스 하나하나 들어가 보자.
2장 · GHC 9.10 (2024.5) / 9.12 (2024.12)
GHC(Glasgow Haskell Compiler)는 Haskell 표준 구현이다. 사실상 유일한 산업용 구현이라고 봐도 무방하다.
GHC 9.10 (2024년 5월)
- **GHC2024 언어 에디션** — 1년 단위로 묶이는 언어 확장 묶음. 이제 `default-language: GHC2024`만 적으면 `LambdaCase`, `DataKinds`, `DerivingStrategies`, `DisambiguateRecordFields`, `ExplicitNamespaces`, `MonoLocalBinds`, `RoleAnnotations`, `ScopedTypeVariables` 등이 기본으로 켜진다.
- **Linear Types(`-XLinearTypes`)** 개선 — 함수형 자원 추적의 핵심.
- **GHC Proposals 511** — `let` 안에서 타입 시그니처를 더 자연스럽게 쓸 수 있다.
- **JavaScript backend** 안정화 — `wasm32` / `javascript` 백엔드가 정식 지원.
- **러닝타임 시스템(GC, threaded RTS)** 튜닝 — `-A` 기본값 변화, 64-bit 머신에서 콜드 캐시 성능 개선.
GHC 9.12 (2024년 12월)
- **CapiFFI calls** 개선 — C 헤더에서 가져온 매크로 함수와 더 안전한 인터페이스.
- **`OrPatterns` 확장** — 패턴 매칭에서 여러 생성자를 `|`로 묶을 수 있다.
- **`NamedDefaults`** — 타입 클래스의 기본 인스턴스를 명시적으로 선언.
- **`MultilineStrings`** — 따옴표 세 개 멀티라인 문자열, 다른 언어 사용자에게 친숙한 모양.
- **타입 추론 일관성** — 다형성 변수가 더 적게 모호해진다.
- **빌드 성능** — 약 5-10% 컴파일 시간 단축(평균치).
-- GHC 9.12의 MultilineStrings + OrPatterns 예시
{-# LANGUAGE MultilineStrings #-}
{-# LANGUAGE OrPatterns #-}
greeting :: String
greeting = """
Hello,
modern Haskell 2026.
"""
classify :: Int -> String
classify n = case n of
(0 | 1 | 2) -> "small"
(3 | 4 | 5) -> "medium"
_ -> "large"
어느 버전을 써야 하나
- **새 프로젝트**: GHC 9.10.x — 가장 균형 잡힌 선택. HLS·Stack LTS·IHP·Servant 모두 1급 지원.
- **모험가**: GHC 9.12.x — 새 문법(MultilineStrings, OrPatterns)을 원할 때.
- **엔터프라이즈/긴 의존성 트리**: GHC 9.8.x — 좀 더 보수적으로 가고 싶다면.
- **레거시**: GHC 9.6.x 미만은 2026년에 새로 시작할 이유가 없다.
3장 · GHCup — 권장 설치 도구
2020년쯤만 해도 Haskell 설치는 "Stack 깔고 끝"이었다. Cabal 사용자는 별도로 깔았고, 둘은 서로 미묘하게 안 맞았다.
2026년에는 **GHCup이 공식 권장 설치 도구**다. `haskell.org/ghcup`에서 한 줄 설치 스크립트를 실행하면, GHC·Cabal·Stack·HLS(Haskell Language Server)가 다 깔린다.
공식 설치 한 줄
curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh
또는 Windows PowerShell
Set-ExecutionPolicy Bypass -Scope Process -Force
Invoke-WebRequest ... ghcup-windows-... .ps1 | Invoke-Expression
설치 후 `ghcup tui`를 실행하면 텍스트 기반 UI가 뜨고, 거기서 원하는 GHC·Cabal·Stack·HLS 버전을 골라 설치/제거할 수 있다.
ghcup install ghc 9.10.1
ghcup install ghc 9.12.1
ghcup set ghc 9.10.1 # 시스템 기본 GHC를 9.10으로
ghcup install cabal 3.14.1.0
ghcup install hls 2.9.0.0 # Haskell Language Server (LSP)
ghcup install stack 3.1.1
ghcup list # 설치된 / 설치 가능한 버전 보기
GHCup의 핵심 가치는 **하나의 호스트에 GHC를 여러 버전 공존시킬 수 있다는 점**이다. 프로젝트마다 `cabal.project`나 `stack.yaml`에 지정된 GHC를 자동으로 골라 쓴다.
> 참고: macOS·Linux는 위 스크립트가 표준이고, Windows는 GHCup가 chocolatey를 통하지 않고 자체 설치한다. WSL 사용자도 동일 스크립트를 쓰면 된다.
4장 · Cabal 3.14 / Stack — 빌드 도구
GHC가 컴파일러라면, Cabal·Stack은 그 위의 패키지·프로젝트 관리자다.
Cabal 3.14
Cabal은 두 가지다.
1. **Cabal 라이브러리** — `*.cabal` 파일 포맷을 읽고 빌드 그래프를 만드는 코어.
2. **cabal-install CLI** — 그 라이브러리를 쓰는 명령행 도구. 보통 우리가 "cabal"이라고 부를 때는 이쪽.
Cabal 3.14는 2024년 말 릴리스로, 다음이 개선되었다.
- **`cabal repl`** 멀티 컴포넌트 지원 — 라이브러리·실행파일·테스트 스위트를 한 REPL에서.
- **`cabal-install`의 의존성 해결기** 속도 향상 — 큰 의존성 트리에서 체감.
- **`cabal.project.local`** 우선순위 명확화.
- **재현 가능한 빌드** — `index-state` 핀이 더 안정적으로 동작.
-- my-app.cabal (Cabal 3.14)
cabal-version: 3.14
name: my-app
version: 0.1.0.0
synopsis: A sample modern Haskell app
license: BSD-3-Clause
build-type: Simple
common warnings
ghc-options: -Wall -Wcompat -Widentities -Wincomplete-uni-patterns
library
import: warnings
exposed-modules: MyApp
MyApp.Server
build-depends: base ^>= 4.20
, servant ^>= 0.20
, servant-server ^>= 0.20
, warp ^>= 3.4
, aeson ^>= 2.2
, text ^>= 2.1
hs-source-dirs: src
default-language: GHC2024
executable my-app
import: warnings
main-is: Main.hs
build-depends: base, my-app
hs-source-dirs: app
default-language: GHC2024
test-suite my-app-test
import: warnings
type: exitcode-stdio-1.0
main-is: Spec.hs
build-depends: base, my-app, hspec ^>= 2.11, tasty ^>= 1.5
hs-source-dirs: test
default-language: GHC2024
Stack
Stack은 FP Complete가 만들었고 지금은 Haskell Foundation 산하 커뮤니티가 유지한다. Cabal과 다른 점은 **Stackage라는 큐레이션된 LTS 스냅샷을 기본으로 쓴다**는 것.
stack.yaml
resolver: lts-22.30 # Stackage LTS — 같이 빌드되는 패키지들이 검증된 묶음
packages:
- .
extra-deps: []
Stackage LTS는 매주 새 마이너 릴리스가, 6개월마다 새 메이저가 나온다. "이 패키지 묶음은 같이 빌드되고 같이 테스트가 통과한다"가 보장이다.
어느 도구를 쓸까
- **새 프로젝트, 빠르게**: Stack (Stackage LTS) — 의존성 충돌 걱정이 적다.
- **라이브러리·메인스트림 OSS**: Cabal — Hackage 직접 배포가 자연스럽다.
- **둘 다 지원**: 대부분의 큰 프로젝트는 `cabal.project`와 `stack.yaml` 둘 다 둔다.
2026년에는 Cabal 진영이 더 빠르게 발전했지만, Stack의 "잡음 적은" UX는 여전히 매력적이다.
5장 · Hackage / Stackage — 패키지 생태계
Hackage
`hackage.haskell.org` — Haskell의 중앙 패키지 저장소. 2026년 기준 약 18,000개 패키지. 누구나 올릴 수 있고, 버전 핀은 SemVer 비슷한 PVP(Package Versioning Policy)를 따른다.
cabal update # Hackage 인덱스 새로고침
cabal install --installdir=./bin pandoc
cabal info aeson # 패키지 정보
Stackage
`stackage.org` — Hackage 위에 큐레이션 레이어를 얹은 것. LTS(장기 지원)와 Nightly로 나뉜다.
- **LTS-22.x** (2024년 말 시작) — GHC 9.8/9.10 기반 묶음
- **LTS-23.x** (2025년 중반) — GHC 9.10/9.12 기반
- **Nightly** — 매일 새로 빌드, 가장 최신 패키지 묶음
Stackage가 중요한 이유: "X 패키지를 쓰면 Y와 의존성 충돌이 난다"가 큐레이션 단계에서 걸러진다. 라이브러리 저자가 `stack.yaml`에 `resolver: lts-22.30`만 적으면, 그 묶음 안의 모든 패키지가 같이 빌드된다.
6장 · IHP (Integrated Haskell Platform) — 풀스택 웹
`ihp.digitallyinduced.com` — 독일의 digitally induced GmbH가 만든 풀스택 웹 프레임워크. **Rails / Phoenix / Laravel 같은 경험을 Haskell에서 제공**하는 것이 목표.
IHP의 철학
- **convention over configuration** — 디렉터리 구조와 명명 규약을 따르면 코드량이 줄어든다.
- **type-safe** — 라우터, 폼, DB 쿼리, 뷰가 전부 타입으로 묶인다.
- **dev tooling** — 개발 서버, 마이그레이션 GUI(IHP IDE라 부른다), 코드 생성기.
새 IHP 프로젝트
nix-shell -p ihp-new --run "ihp-new my-blog"
cd my-blog
./start # http://localhost:8000
IHP 모델 예시
-- Application/Schema.sql (SQL로 직접 정의)
CREATE TABLE posts (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
title TEXT NOT NULL,
body TEXT NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL
);
-- Web/Controller/Posts.hs (생성된 컨트롤러를 손본 것)
module Web.Controller.Posts where
instance Controller PostsController where
action PostsAction = do
posts <- query @Post |> fetch
render IndexView { .. }
-- Web/View/Posts/Index.hs
module Web.View.Posts.Index where
data IndexView = IndexView { posts :: [Post] }
instance View IndexView where
html IndexView { .. } = [hsx|
{forEach posts renderPost}
|]
where
renderPost post = [hsx|<li>{get #title post}</li>|]
`hsx` 따옴 안에서는 IHP 전용 quasi-quoter가 동작하기 때문에, 중괄호 보간이 Haskell 값으로 안전하게 해석된다. (이건 IHP의 DSL 안에서만 그렇고, 일반 Haskell 파일과는 다르다.)
IHP가 잘 어울리는 경우
- **혼자 만드는 사이드 프로젝트** — Rails처럼 빠르게 CRUD를 굴리고 싶을 때.
- **소규모 팀의 SaaS 백오피스** — 마이그레이션·인증·관리 화면이 기본 제공.
- **Haskell 초보자 진입로** — 모나드 변환자 같은 개념을 정면으로 부딪치지 않고 "어쨌든 동작하는 웹앱"을 만들 수 있다.
7장 · Servant — 타입드 HTTP API
IHP가 풀스택이라면, Servant는 **API 전용 라이브러리**다. 핵심 아이디어 하나: **API를 타입 레벨로 표현한다**.
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
module Api where
data User = User
{ userId :: Int
, userName :: String
} deriving (Generic)
instance ToJSON User
instance FromJSON User
-- API를 타입으로
type UserAPI =
"users" :> Get '[JSON] [User]
:<|> "users" :> Capture "id" Int :> Get '[JSON] User
:<|> "users" :> ReqBody '[JSON] User :> Post '[JSON] User
-- 그 타입의 핸들러
userAPI :: Server UserAPI
userAPI = listUsers :<|> getUser :<|> createUser
where
listUsers = pure [User 1 "alice", User 2 "bob"]
getUser uid = pure (User uid "alice")
createUser u = pure u
이 코드의 마법은 `UserAPI`라는 **타입**이 진실의 원천이라는 것이다.
- 핸들러를 잘못 짜면 **컴파일이 안 된다**. 예를 들어 `Get '[JSON] [User]`로 선언된 라우트가 `Int`를 돌려주면 GHC가 거부한다.
- 클라이언트 — `servant-client` — 도 같은 타입에서 자동 생성된다.
- OpenAPI/Swagger 문서 — `servant-openapi3` — 도 같은 타입에서 자동 생성된다.
Servant + warp로 서버 띄우기
main :: IO ()
main = do
putStrLn "Listening on http://localhost:8081"
run 8081 (serve (Proxy :: Proxy UserAPI) userAPI)
누가 Servant를 쓰는가
- **Hasura GraphQL Engine** 일부 내부 서비스
- **Channable**, **Mercury Bank**, **Wire**
- 수많은 OSS 백엔드
API 변경이 곧 컴파일러 에러가 되는 안전망이 핵심이다.
8장 · Yesod / Snap / Scotty / Spock — 그 외 웹
Yesod (Michael Snoyman)
`yesodweb.com` — Stack/Stackage의 만든이이기도 한 Michael Snoyman이 만든 풀스택 프레임워크. IHP보다 일찍 시작했고, **Type-safe URL**과 **Type-safe widget** 같은 무거운 타입 사용으로 유명하다.
-- Foundation.hs (Yesod 라우팅 스니펫)
mkYesodData "App" [parseRoutes|
/ HomeR GET
/posts PostsR GET POST
/posts/#PostId PostR GET DELETE
|]
Snap
`snapframework.com` — 학술적 뿌리가 강한 프레임워크. **Snaplets**라는 모듈 시스템이 특징. 활발한 신규 개발은 줄었지만 기존 프로젝트에서 쓰인다.
Scotty
`hackage.haskell.org/package/scotty` — **Sinatra 스타일의 마이크로 프레임워크**. 짧고 가벼운 API를 빠르게 띄우는 데 좋다.
{-# LANGUAGE OverloadedStrings #-}
main :: IO ()
main = scotty 3000 $ do
get "/" $ text "hello scotty"
get "/hello/:name" $ do
n <- captureParam "name"
text ("hello, " <> n)
Spock
Scotty와 비슷한 마이크로 프레임워크지만 세션·CSRF·DB 풀 같은 미들웨어가 기본 제공된다.
어느 걸 쓰지
- **타입드 API 중심**: Servant.
- **풀스택 / DB 마이그레이션까지 한 번에**: IHP.
- **풀스택 / 깊은 타입 사용**: Yesod.
- **마이크로 서비스 / 사이드카**: Scotty.
- **세션·인증 포함 마이크로**: Spock.
9장 · 효과 시스템 — Effectful (mtl 대체) / Polysemy / fused-effects / Bluefin
Haskell의 매력이자 진입장벽이 효과(effects) 시스템이다. 부수 효과 — IO, 상태, 예외, 로깅, DB 접근 — 를 타입으로 추적한다.
전통은 **mtl 스타일**이었다. 모나드 변환자(`StateT`, `ReaderT`, `ExceptT`)를 쌓아 올린다. 강력하지만 컴파일 메시지가 늘어지고 성능 트레이드오프가 있다.
2026년에는 **Effectful**이 사실상 mtl의 자리를 빼앗고 있다.
Effectful
`hackage.haskell.org/package/effectful` — Polysemy/fused-effects보다 단순한 모델, 컴파일러 친화적 성능, mtl과 거의 같은 사용감.
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE GADTs #-}
-- 효과를 명시: Reader + Error
loadUser :: (Reader String :> es, Error String :> es) => Int -> Eff es String
loadUser uid = do
prefix <- ask
if uid > 0
then pure (prefix ++ ": user-" ++ show uid)
else throwError "invalid uid"
main :: IO ()
main = do
result <- runEff
. runError @String
. runReader "PREFIX"
$ loadUser 42
print result -- Right "PREFIX: user-42"
Polysemy
`hackage.haskell.org/package/polysemy` — "Free Monad + Free Algebra"의 학술적 정통. 강력한 인터프리터 변경 능력, 무거운 타입.
fused-effects
`hackage.haskell.org/package/fused-effects` — Carrier 기반 효과 시스템. 컴파일 시 효과 합성을 펼쳐서 성능을 끌어올린다.
Bluefin
`hackage.haskell.org/package/bluefin` — 2024-25년 등장한 새 효과 라이브러리. **scoped effects**와 **delimited continuations**를 1급으로. GHC 9.10의 새 RTS 기능을 활용한다.
-- Bluefin 스타일 (개념적 예시)
bumpCounter :: e :> es => State Int e -> Eff es Int
bumpCounter ref = do
modify ref (+ 1)
get ref
누가 어느 걸 써야 하나
- **새 코드, 익숙한 mtl 감각**: Effectful.
- **연구·교육·강한 표현력**: Polysemy.
- **성능에 민감, 정형 호출 그래프**: fused-effects.
- **새것 좋아함, scoped effects**: Bluefin.
10장 · ORM — Persistent / Beam / Esqueleto
Persistent (Yesod 진영)
Michael Snoyman의 `persistent` — 데이터 모델을 DSL로 적고, 컴파일러가 그걸 받아 데이터 타입과 마이그레이션을 만든다. SQL과 NoSQL 백엔드 둘 다 지원.
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
Post
title Text
body Text
deriving Show
User
name Text
posts [PostId]
deriving Show
|]
Beam
`haskell-beam.github.io` — Generic 기반의 더 타입 안전한 SQL. Persistent보다 SQL에 가까운 추상화, Postgres·MySQL·SQLite 백엔드.
Esqueleto
Persistent 위에 얹어 **타입 안전한 JOIN과 서브쿼리**를 표현하는 EDSL.
-- 사용자의 게시글 수
usersWithPostCount = E.select $ do
(u E.:& p) <- E.from $ E.table @User
`E.leftJoin` E.table @Post
`E.on` (\(u E.:& p) -> E.just (u E.^. UserId) E.==. p E.?. PostUserId)
E.groupBy (u E.^. UserId)
pure (u, E.count (p E.?. PostId))
어느 걸 쓰지
- **빠른 CRUD**: Persistent.
- **JOIN 많은 분석 쿼리**: Esqueleto + Persistent.
- **SQL 가까이, 큰 도메인**: Beam.
11장 · Megaparsec — 파서 콤비네이터
`hackage.haskell.org/package/megaparsec` — 2026년 표준 파서 콤비네이터 라이브러리. Parsec의 후계자, Attoparsec과 달리 **사람이 읽기 좋은 에러 메시지**가 핵심.
{-# LANGUAGE OverloadedStrings #-}
type Parser = Parsec Void Text
data JsonValue
= JNull
| JBool Bool
| JNumber Double
| JString Text
| JArray [JsonValue]
deriving Show
sc :: Parser ()
sc = L.space space1 (L.skipLineComment "//") empty
lexeme :: Parser a -> Parser a
lexeme = L.lexeme sc
jsonNull :: Parser JsonValue
jsonNull = JNull <$ lexeme (string "null")
jsonBool :: Parser JsonValue
jsonBool = JBool True <$ lexeme (string "true")
<|> JBool False <$ lexeme (string "false")
jsonNumber :: Parser JsonValue
jsonNumber = JNumber <$> lexeme L.signed L.float
jsonValue :: Parser JsonValue
jsonValue = choice [jsonNull, jsonBool, jsonNumber]
Megaparsec의 장점:
- **자동 위치 추적** — 에러가 어디서 났는지 줄·칸으로 표시.
- **사용자 정의 에러** — `customFailure`로 도메인 특화 에러 추가.
- **백트래킹 제어** — `try`로 명시적으로.
Pandoc도 내부에서 Megaparsec(과 그 전 세대인 Parsec)을 광범위하게 쓴다.
12장 · Aeson — JSON
`hackage.haskell.org/package/aeson` — Haskell의 사실상 표준 JSON 라이브러리. 2026년 2.2 메이저 라인이 안정.
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
data Post = Post
{ postId :: Int
, postTitle :: String
} deriving (Show, Generic)
instance ToJSON Post where
toEncoding = genericToEncoding defaultOptions
instance FromJSON Post
main :: IO ()
main = do
let p = Post 1 "Hello"
bs = encode p
print bs
print (decode bs :: Maybe Post)
Aeson 2 핵심
- **KeyMap** — 객체 키가 `Text`에서 `Key`로 바뀌면서 충돌·해시 보안 강화.
- **Generic 디폴트가 충분히 빠르다** — Template Haskell 매크로 안 써도 된다.
- **Custom Options** — `fieldLabelModifier`로 `snake_case` ↔ `camelCase` 변환 자연스럽게.
options :: Options
options = defaultOptions
{ fieldLabelModifier = camelTo2 '_' -- postId → post_id
, omitNothingFields = True
}
13장 · 테스트 — hedgehog / tasty / hspec
Haskell 테스트 생태계는 세 축이다.
hspec (BDD)
main :: IO ()
main = hspec $ do
describe "sum" $ do
it "sums an empty list to 0" $
sum ([] :: [Int]) `shouldBe` 0
it "sums [1,2,3] to 6" $
sum [1, 2, 3 :: Int] `shouldBe` 6
tasty (테스트 러너)
다양한 테스트 라이브러리를 하나의 트리에 묶어주는 러너. `tasty-hspec`, `tasty-hedgehog`, `tasty-hunit` 등의 어댑터.
main :: IO ()
main = defaultMain $ testGroup "all"
[ testCase "trivial" $ (1 + 1) @?= (2 :: Int)
]
hedgehog (property testing)
QuickCheck의 후계자. **integrated shrinking** — 실패 케이스가 자동으로 최소 반례까지 줄어든다.
prop_reverse_involutive :: Property
prop_reverse_involutive = property $ do
xs <- forAll $ Gen.list (Range.linear 0 100) (Gen.int Range.linearBounded)
reverse (reverse xs) === xs
property test가 강력한 이유: "케이스 5개"가 아니라 "수천 개의 임의 입력에서 불변식이 성립함"을 확인한다. 그리고 깨지면 hedgehog가 자동으로 줄여서 사람이 보기 좋은 반례를 준다.
누가 어느 걸
- **BDD 단위 테스트**: hspec.
- **러너 통합**: tasty.
- **속성·반례 자동 축소**: hedgehog.
14장 · Liquid Haskell — Refinement Types
`ucsd-progsys.github.io/liquidhaskell-blog/` — 일반 Haskell 위에 **refinement types**(부분 타입)를 얹는 정형 검증 도구.
핵심 아이디어: 타입 옆에 술어를 적으면, SMT 솔버(Z3 등)가 그 술어가 항상 성립함을 컴파일 시간에 증명한다.
{-@ measure llen @-}
llen :: [a] -> Int
llen [] = 0
llen (_:xs) = 1 + llen xs
{-@ head :: { xs:[a] | llen xs > 0 } -> a @-}
head :: [a] -> a
head (x:_) = x
head [] = error "won't happen" -- LH가 이 라인이 도달 불가임을 증명
LH는 "이 함수는 빈 리스트로 호출될 수 없다"는 사실을 **타입의 일부**로 적게 하고, 호출처에서 그 조건을 만족시키지 못하면 컴파일러가 거부한다.
학계와 일부 산업(블록체인, 항공·자동차 안전 쪽)에서 사용한다.
15장 · Hasktorch — 머신러닝
`hasktorch.org` — PyTorch의 libtorch 라이브러리를 Haskell에서 바인딩. Tweag·HaskellML이 적극 개발.
{-# LANGUAGE DataKinds #-}
main :: IO ()
main = do
x <- randn' [2, 3] -- Tensor [2, 3]
y <- randn' [3, 4]
let z = matmul x y
print z
Hasktorch가 매력적인 이유는 **타입으로 텐서 차원을 표현**할 수 있다는 점이다.
-- 컴파일 타임에 차원이 검증되는 행렬곱
matmul' :: Tensor '[2, 3] Float -> Tensor '[3, 4] Float -> Tensor '[2, 4] Float
matmul' = matmul
차원이 안 맞는 행렬을 곱하려고 하면 GHC가 거부한다. PyTorch 사용자에게는 익숙한 실수가 컴파일 타임에 잡힌다.
16장 · Pandoc — John MacFarlane
`pandoc.org` — UC Berkeley 철학과 교수 **John MacFarlane**이 2006년부터 메인테인 중인 **만능 문서 변환기**. 입력 포맷 약 40개, 출력 포맷 약 60개. Markdown · LaTeX · DOCX · HTML · ePub · PDF · MediaWiki · RST 같은 사이를 자유롭게 오간다.
마크다운 → PDF
pandoc README.md -o readme.pdf
마크다운 → DOCX, 참고문헌 자동
pandoc paper.md --citeproc --bibliography refs.bib -o paper.docx
HTML → EPUB
pandoc book.html -o book.epub --metadata title="My Book"
Pandoc의 내부 구조도 멋지다.
- **PandocAST** — 입력 포맷이 무엇이든 이 AST로 정규화.
- **Reader** — 입력 포맷별 파서 (Markdown reader, LaTeX reader, ...).
- **Writer** — 출력 포맷별 시리얼라이저 (HTML writer, PDF writer via LaTeX, ...).
- **Filter** — Lua 또는 Haskell로 AST를 변환.
Haskell 생태계에서 가장 가시적인 "최종 사용자가 매일 쓰는" 프로젝트다.
17장 · Cardano — Charles Hoskinson IOHK
`cardano.org` — Ethereum 공동창립자 **Charles Hoskinson**이 떠나서 만든 블록체인 플랫폼. 개발사 **IOG**(Input Output Global, 옛 IOHK)가 운영. 노드는 **전부 Haskell**, 스마트 컨트랙트는 **Plutus**(Haskell DSL)로 짠다.
왜 Haskell이었나:
- **정형 검증** — 금융을 다루기 때문에 "이 코드가 정해진 명세를 만족함"을 수학적으로 보여야 했다.
- **순수성** — 합의 알고리듬은 결정적이어야 한다. IO를 격리한 Haskell이 자연스럽다.
- **타입 시스템** — 검증된 변경을 보장.
Cardano 코어는 다음으로 구성된다.
- `cardano-node` — 노드 데몬, Haskell.
- `cardano-ledger` — 원장 규칙, Haskell 명세에서 직접 구현.
- `plutus-core` — 스마트 컨트랙트 코어 언어.
- **Plutus Tx** — Plutus로 컴파일되는 Haskell 부분 집합.
-- Plutus Tx (개념적 예시)
{-# INLINABLE mkValidator #-}
mkValidator :: () -> Integer -> ScriptContext -> Bool
mkValidator _ guess _ = guess == 42
이게 온체인에 올라가서 트랜잭션을 검증한다. Plutus는 Haskell 부분집합을 Plutus Core라는 자체 코어 언어로 컴파일하고, 그 코어 언어를 노드가 평가한다.
18장 · 한국 / 일본 — 한국어 Haskell, ja-haskell, IOHK Japan
한국
- **Haskell Korea (페이스북 그룹·디스코드)** — 2010년대 중반부터 유지된 커뮤니티. 분기마다 비정기 모임.
- 출판물: 김정민·이형주 외의 번역서(예: "Haskell Programming from First Principles" 등 일부), 그리고 국내 저자가 쓴 "Programming Haskell"류 책.
- 대학: KAIST·서울대·POSTECH의 PL 그룹이 Haskell을 교육·연구에 쓴다.
- 산업 사용은 제한적이지만 핀테크·시큐리티 일부 팀이 도입.
일본
- **ja-haskell** — 일본어 Haskell 커뮤니티 슬랙·X. 매주 활발.
- **Haskell Day**, **Haskell Symposium Japan**(부정기) — 발표 중심 이벤트.
- 책: 山本和彦(Kazu Yamamoto, IIJ Lab)의 "プログラミングHaskell" 번역, 그 외 다수.
- **IOHK Japan** — Cardano IOG의 일본 지사가 도쿄에 있고, Haskell 채용이 가장 활발한 곳 중 하나.
- 산업: **Tsuru Capital**, **HERP**, **Drivemode(Honda 인수)** 등이 Haskell 사용.
영어 커뮤니티(`r/haskell`, Haskell Discourse, Discord) 외에 두 지역 커뮤니티의 자료를 같이 보면 진입이 부드럽다.
19장 · 누가 Haskell을 배워야 하나
Haskell을 추천하는 두 가지 경우다.
1. 함수형 프로그래밍을 정면으로 배우고 싶을 때
JavaScript·Kotlin·Scala·OCaml에서 함수형 스타일을 쓰는 것과, **순수성과 게으름**이 디폴트인 환경에서 쓰는 것은 다르다. Haskell은 도망갈 곳이 없다. 모든 부수효과가 타입에 드러난다. 이게 배우는 입장에서는 처음엔 답답하지만, 한 번 익숙해지면 **다른 언어를 짤 때도 머릿속 모델이 달라진다**.
2. 검증·DSL이 가치가 큰 도메인
- 금융(파생상품 가격 계산, 정산)
- 블록체인 스마트 컨트랙트
- 컴파일러·언어 도구 (Pandoc, ShellCheck, Idris, dhall 같은 도구들이 다 Haskell)
- 정형 검증이 필요한 항공·자동차·의료 일부
- 복잡한 도메인 규칙이 있는 보험·세무
Haskell이 안 어울리는 경우
- 팀이 큰데 Haskell을 아는 사람이 본인뿐 — 채용·온보딩 비용이 크다.
- 매우 짧은 MVP, 라이브러리가 부족한 도메인.
- 시스템 프로그래밍 / 임베디드 — Rust가 더 자연스럽다.
20장 · 참고 / References
- GHC 9.10 release notes — `downloads.haskell.org/ghc/9.10.1/docs/users_guide/9.10.1-notes.html`
- GHC 9.12 release notes — `downloads.haskell.org/ghc/9.12.1/docs/users_guide/9.12.1-notes.html`
- GHCup — `www.haskell.org/ghcup/`
- Cabal — `cabal.readthedocs.io`
- Stack — `docs.haskellstack.org`
- Stackage — `www.stackage.org`
- Hackage — `hackage.haskell.org`
- IHP — `ihp.digitallyinduced.com`
- Servant — `docs.servant.dev`
- Yesod — `www.yesodweb.com`
- Snap — `snapframework.com`
- Scotty — `hackage.haskell.org/package/scotty`
- Effectful — `hackage.haskell.org/package/effectful`
- Polysemy — `hackage.haskell.org/package/polysemy`
- fused-effects — `hackage.haskell.org/package/fused-effects`
- Bluefin — `hackage.haskell.org/package/bluefin`
- Persistent — `hackage.haskell.org/package/persistent`
- Beam — `haskell-beam.github.io`
- Esqueleto — `hackage.haskell.org/package/esqueleto`
- Megaparsec — `hackage.haskell.org/package/megaparsec`
- Aeson — `hackage.haskell.org/package/aeson`
- hedgehog — `hedgehog.qa`
- tasty — `hackage.haskell.org/package/tasty`
- hspec — `hspec.github.io`
- Liquid Haskell — `ucsd-progsys.github.io/liquidhaskell-blog/`
- Hasktorch — `hasktorch.org`
- Pandoc — `pandoc.org`
- Cardano — `cardano.org`
- IOG — `iohk.io`
- Haskell Foundation — `haskell.foundation`
- Haskell Korea — Facebook group / Discord
- ja-haskell — `haskell.jp`
현재 단락 (1/459)
매년 누군가는 묻는다. "Haskell, 아직도 써요?"