GN⁺: 왜 Haskell인가?
(gtf.io)-
"비실용적", "학문적", "니치하다"
- 사람들이 내가 가장 좋아하는 프로그래밍 언어가 Haskell임을 알았을 때 보이는 반응임
- 취미 프로젝트뿐만 아니라 실제 웹 서버를 구축하는 데도 사용함
- Converge에서 Haskell로 작업하는 팀들을 이끌고 있음
-
Haskell에 대한 오해
- 범용 프로그래밍 언어로 해결할 수 있는 문제는 다른 언어에서도 해결 가능함
- Python, Rust, Typescript 등에 도입되는 많은 기능들이 Haskell에서 영감을 받았거나 더 견고하게 구현되었음
- "지루한 기술을 선택하라"는 이념의 변형으로 보임
- 프로그래밍은 수학이 아니며, 수학적인 요소를 배제해야 한다는 잘못된 생각이 있음
-
이 글의 목적
- Haskell이 대부분의 프로그래머에게 최고의 선택인 이유를 논리적으로 설명하려 함
- 견고한 소프트웨어를 생산적으로 작성하고자 하는 사람들에게 특히 유용함
- 소프트웨어 작성의 재미있는 측면도 강조함
-
언러닝과 리러닝
- 대부분의 프로그래머는 명령형 패러다임에 익숙함
- Haskell은 순수 함수형 언어로 학습 곡선이 가파름
- Haskell 언어 자체는 단순한 부분집합으로 제한하면 배우기 쉬움
- 함수형 프로그래밍은 프로그램을 구성하는 방법에 대한 완전한 전환을 요구함
- 이 과정은 프로그래머로서 성장하는 데 도움이 됨
- Alan Perlis의 말 인용:
프로그래밍에 대한 생각에 영향을 주지 않는 언어는 알 가치가 없음.
문법에 대한 간단한 설명
-
::
는 타입 시그니처를 나타냄 (예:myThing :: String
) -
함수 호출은 괄호를 사용하지 않고 함수명 뒤에 인수를 공백으로 구분하여 나열함 (예:
doSomething withThis withThat
) -
타입 시그니처에서 소문자는 타입 변수이며, 임의의 타입을 나타냄 (예:
head :: [a] -> a
) -
두 종류의 화살표
->
와=>
가 있음:-
->
는 함수의 타입을 설명함 (예:add1 :: Int -> Int
) -
=>
는 타입 변수에 대한 제약을 설명하며 항상 먼저 옴 (예:add1 :: Num a => a -> a
)
-
-
주석은
--
로 시작함 -
return
은 일반 함수이며, 예상하는 의미가 아님 -
do
는 명령형처럼 보이게 하는 문법적 설탕임 -
지역 변수에 값을 할당하는 여러 가지 방법이 있음:
let x = <something> in <expression>
또는
x <- <something>
-
실수를 줄이기
- 많은 언어에서 코드를 "올바르게" 만들기 위해 많은 테스트 케이스를 작성함
- Haskell은 타입 시스템과 순수 함수형 프로그래밍으로 이 부담을 크게 줄임
- Haskell의 강력한 타입 시스템은 프로그램에 대한 구체적인 보증을 제공하고 엄격하게 적용함
- 타입 시스템의 특징:
- 널러블 타입이 없음
- 실패할 수 있는 계산을 표현할 수 있음
- 패턴 매칭과 완전성 체크
- 원시 집착을 무료로 회피함
-
널 값이 없는 것의 이점
-
null
값이 존재하지 않아 값이 예상하는 타입인지 항상 알 수 있음 - 런타임 오류를 방지하고 오류 표면을 줄임
-
-
실패 가능한 계산의 표현
-
Maybe
와Either
타입을 사용하여 실패할 수 있는 계산을 명시적으로 표현함 -
Maybe
는 결과가 있을 수도 없을 수도 있는 계산을 나타냄safeHead :: [a] -> Maybe a
-
Either
는 두 가지 값을 가질 수 있음 (Left a
또는Right b
)validateAddress :: String -> Either AddressParseError ValidAddress
-
-
패턴 매칭과 완전성 체크
- 모든 입력 도메인을 처리해야 하며, 그렇지 않으면 컴파일러가 오류를 발생시킴
- 이는 런타임 오류를 방지하고 프로그램의 신뢰성을 높임
-
원시 집착의 회피
-
newtype
을 사용하여 의미론적으로 더 의미 있는 타입을 쉽게 생성할 수 있음
newtype VenueName = VenueName String newtype EventName = EventName String
-
-
순수 함수형 프로그래밍의 장점
- 데이터가 불변이며, 상태 변이를 걱정할 필요가 없음
- 부수 효과가 명시적으로 처리되며, 함수는 부수 효과 없이 입력에만 의존함
- 이는 프로그램의 예측 가능성과 안정성을 높임
-
부수 효과의 명시적 처리
-
IO
모나드를 사용하여 부수 효과를 코드에서 분리하고 제어함 - 함수의 타입 시그니처를 통해 부수 효과를 일으키는 함수임을 알 수 있음
sendGreetings :: User -> IO Response
-
-
모나드와 효과 제어
- 타입클래스와 모나드를 사용하여 함수가 수행할 수 있는 효과를 정확하게 인코딩함
- 이는 의도하지 않은 부수 효과를 방지하고 코드의 안정성을 높임
-
생산성 향상 요소
- 강력한 타입 시스템과 순수 함수형 특성으로 인해 코드 재사용과 개념의 일반화가 용이함
-
Functor
와Monoid
와 같은 개념을 통해 다양한 데이터 구조에 동일한 패턴을 적용할 수 있음
fmap (+2) [1, 2, 3] -- [3, 4, 5] fmap (+2) (Just 2) -- Just 4
-
두려움 없는 리팩토링
- 컴파일러의 엄격함으로 인해 코드 변경 시 새로운 버그가 발생할 위험이 적음
- 타입 시스템이 프로그램 도메인을 정확하게 표현할 수 있게 해주어 안심하고 코드를 수정할 수 있음
-
프로그램에 대한 이해 향상
- 선언형 프로그래밍으로 문제 도메인을 정확하게 표현할 수 있음
- 프로그램의 의미를 쉽게 이해하고 신뢰성을 높일 수 있음
- 불필요한 복잡성을 제거하여 프로그램의 합리적인 추론이 가능해짐
-
알게브라적 데이터 타입과 타입클래스
- 도메인에 특화된 언어를 Haskell 내에서 구축할 수 있음
- 이는 프로그램을 이해하고 유지 보수하는 데 도움이 됨
-
예시 프로그램
- 간단한 회계 도구를 작성하여 Haskell의 개념을 실용적으로 적용함
에필로그
- Haskell을 사용하는 것은 즐겁고 생산적임
- 강력하고 표현력 있는 타입 시스템과 순수 함수형 프로그래밍의 결합이 Haskell을 특별하게 만듦
- 다른 언어들도 이러한 기능을 도입하고 있지만, Haskell은 이러한 특징들이 근본에 자리 잡고 있음
- Haskell을 배우는 것은 프로그래밍에 대한 사고 방식을 변화시켜 줄 것임
GN⁺의 의견
-
Haskell의 학습 가치
- 프로그래머로서 사고의 폭을 넓히는 데 도움이 됨
- 함수형 프로그래밍 패러다임을 이해하면 다른 언어에서도 더 나은 코드를 작성할 수 있음
-
함수형 프로그래밍의 부상
- 병렬 처리와 동시성에 강점이 있어 현대 컴퓨팅 환경에 적합함
- 부수 효과의 제어로 인해 예측 가능한 코드 작성이 가능함
-
다른 언어와의 비교
- Rust나 Scala와 같이 함수형 프로그래밍을 지원하는 언어들도 있지만, Haskell의 순수성과 타입 시스템은 독보적임
- 새로운 언어를 배울 때 Haskell의 개념이 도움이 될 수 있음
-
실무 적용 가능성
- 초기 학습 곡선은 가파르지만, 투자한 시간만큼 생산성이 향상됨
- 복잡한 시스템이나 오류에 민감한 도메인에서 유용함
-
커뮤니티와 생태계
- Haskell 커뮤니티는 활발하며, 다양한 라이브러리와 도구가 지속적으로 개발되고 있음
- 오픈 소스 프로젝트에 참여하여 실력을 향상시킬 수 있음
Hacker News 의견
-
Haskell은 부분 함수 대신 전체 함수를 작성하도록 강제함
- Haskell은 무한 재귀를 방지하지 않음
- 종속 타입으로 이동하는 FP 생태계에서 타입 검사기가 무한히 실행되지 않도록 하는 것이 중요함
- Haskell의 많은 임시 확장 기능들이 문제를 일으킴
- Haskell 철학을 좋아한다면 Haskell에만 국한되지 말아야 함
- Haskell의 표준화는 실패했음
- GHC의 고유한 가치 제안은 GHC 런타임 시스템일 수 있음
-
Haskell을 10년 동안 사용해왔으며 도구가 크게 개선되었음
- ghcup, cabal sandboxing, HLS가 안정적임
- 라이브러리 생태계에서 부족한 점을 많이 발견하지 못했음
- Haskell의 컴파일 시간이 여전히 불편함
- 의존성 컴파일 시간이 길어짐
-
Haskell의 타입 시스템은 함수가 전체임을 증명하지 않음
- 일반적인 프로그래밍에서는 전체성 증명이 유용하지 않음
- 대부분의 사람들은 테스트를 통해 프로그램이 실제로 작동하는지 확인함
-
Haskell 언어는 좋지만 생태계는 아직 갈 길이 멀음
- 컴파일러가 느림
- 오류 보고 능력이 부족함
- 첫 번째 오류가 나머지 컴파일을 중단시킴
- 도구가 다른 함수형 언어에 비해 여전히 부족함
- 라이브러리 생태계가 부족함
- Haskell의 아이디어는 다른 많은 언어에 영향을 미쳤음
-
Haskell이나 다른 함수형 언어를 직업적으로 사용하고 싶음
- Go와 같은 언어는 쉽게 배울 수 있었음
- 함수형 언어에서 코드베이스를 구축하는 방법을 배우고 싶음
-
Haskell은 프로그래밍 사고방식과 코드 아키텍처에 큰 영향을 미쳤음
- Haskell의 타입 시스템은 매우 강력하고 이해하기 쉬움
- Haskell 코드를 미세 최적화하는 것이 재미있었음
- 도구는 여전히 부족함
-
Haskell은 언어 수준에서 게으름을 실험함
- 게으름은 표준 라이브러리 수준에서 얻을 수 있음
-
Haskell의 극단적인 순수성과 불변성이 문제임
- 많은 프로그래머들이 절차적/가변 루프를 더 쉽게 표현함
- Rust는 유능한 타입 시스템과 많은 함수형 관용구를 사용하지만, 루프와 가변성을 사용할 수 있음
-
Haskell은 비즈니스 로직 소프트웨어(BLOBS)에 매우 적합함
- 간단한 타입과 패턴 매칭으로 대부분의 비즈니스 로직을 모델링할 수 있음
- 간단한 부분을 유지하면 비기술적 기여자에게도 쉽게 가르칠 수 있음
- Haskell은 재미있음