Skip to content
Published on

모던 Haskell 2026 — GHC 9.10 / 9.12 / GHCup / Cabal 3.14 / Stack / IHP / Servant / Effectful / Pandoc / Cardano 심층 가이드

Authors

프롤로그 — 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 511let 안에서 타입 시그니처를 더 자연스럽게 쓸 수 있다.
  • 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.projectstack.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.projectstack.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.yamlresolver: 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
import Web.Controller.Prelude
import Web.View.Posts.Index

instance Controller PostsController where
    action PostsAction = do
        posts <- query @Post |> fetch
        render IndexView { .. }
-- Web/View/Posts/Index.hs
module Web.View.Posts.Index where
import Web.View.Prelude

data IndexView = IndexView { posts :: [Post] }

instance View IndexView where
    html IndexView { .. } = [hsx|
        <h1>Posts</h1>
        <ul>
            {forEach posts renderPost}
        </ul>
    |]
        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

import Data.Aeson (FromJSON, ToJSON)
import GHC.Generics (Generic)
import Servant

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로 서버 띄우기

import Network.Wai.Handler.Warp (run)
import Servant

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 URLType-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/scottySinatra 스타일의 마이크로 프레임워크. 짧고 가벼운 API를 빠르게 띄우는 데 좋다.

{-# LANGUAGE OverloadedStrings #-}
import Web.Scotty

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 #-}

import Effectful
import Effectful.Reader.Static (Reader, ask, runReader)
import Effectful.Error.Static (Error, throwError, runError)

-- 효과를 명시: 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 effectsdelimited continuations를 1급으로. GHC 9.10의 새 RTS 기능을 활용한다.

-- Bluefin 스타일 (개념적 예시)
import Bluefin.Eff
import Bluefin.State

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 #-}

import Database.Persist.TH

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.

import qualified Database.Esqueleto.Experimental as E

-- 사용자의 게시글 수
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 #-}
import Data.Void (Void)
import Text.Megaparsec
import Text.Megaparsec.Char
import qualified Text.Megaparsec.Char.Lexer as L
import Data.Text (Text)

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 #-}

import Data.Aeson
import GHC.Generics

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 OptionsfieldLabelModifiersnake_casecamelCase 변환 자연스럽게.
options :: Options
options = defaultOptions
  { fieldLabelModifier = camelTo2 '_'   -- postId → post_id
  , omitNothingFields  = True
  }

13장 · 테스트 — hedgehog / tasty / hspec

Haskell 테스트 생태계는 세 축이다.

hspec (BDD)

import Test.Hspec

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 등의 어댑터.

import Test.Tasty
import Test.Tasty.HUnit

main :: IO ()
main = defaultMain $ testGroup "all"
  [ testCase "trivial" $ (1 + 1) @?= (2 :: Int)
  ]

hedgehog (property testing)

QuickCheck의 후계자. integrated shrinking — 실패 케이스가 자동으로 최소 반례까지 줄어든다.

import Hedgehog
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range

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 #-}
import Torch

main :: IO ()
main = do
  x <- randn' [2, 3]     -- Tensor [2, 3]
  y <- randn' [3, 4]
  let z = matmul x y
  print z

Hasktorch가 매력적인 이유는 타입으로 텐서 차원을 표현할 수 있다는 점이다.

import Torch.Typed

-- 컴파일 타임에 차원이 검증되는 행렬곱
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