Sign in

박준규

@curry.hackers.pub.ap.brid.gy
3 followers 2 following 74 posts

자기소개 글 링크 🌉 bridged from hackers.pub/@curry on the fediverse by fed.brid.gy

PostsRepliesMedia
박준규 @curry.hackers.pub.ap.brid.gy · 02/06/2026
hackers.pub
이 글은 펜실베니아 대학 컴퓨터공학부 하스켈 강의에 포함된 스타일 가이드라인 문서를 한글로 옮긴 것입니다. 원문 링크: https://www.cis.upenn.edu/~cis1940/spring13/ ## 바람직한 하스켈 스타일 제출하는 모든 프로그래밍 과제는 다음 스타일 가이드라인에서 창의적으로 나와야 합니다. 프로그래밍은 공학 분야인 만큼이나 사회적 예술 형식이며 모든 예술가가 아는 바와 같이 제약은 창의성을 억누르기보다는 오히려 강화하는 역할을 합니다. 이 지침들은 학기가 진행됨에 따라 확장될 것입니다. * 함수와 변수 이름에는 반드시 카멜케이스를 사용하세요. * 함수 이름에는 반드시 설명적인 이름을 사용하세요. 이름은 필요한만큼 길되 꼭 그 이상으로는 길지 않게 해야 합니다. * 좋은 예: `solveRemaining` * 나쁜 예: `slv` * 보기 흉한 예: `solveAllTheCasesWhichWeHaven'tYetProcessed` * 탭 문자를 사용하지 마세요. 하스켈은 레이아웃에 민감한 언어이며 탭을 사용하면 모든 것이 엉망이 됩니다. 다른 언어에서 탭을 어떻게 생각하는지는 상관 없습니다. 이 부분은 그냥 저를 믿어 주세요. 참고로 이는 매번 스페이스바를 수천 번 눌러서 들여쓰기 해야 한다는 뜻이 아닙니다. 여러분이 즐겨 쓰는 편집기는 탭 대신 스페이스를 사용한 자동 들여쓰기 기능을 지원해야 합니다. * 모든 줄을 80자 미만으로 유지하려고 노력하세요. 이것은 절대적인 규칙은 아니지만 편집기가 자동으로 줄 바꿈을 해서 생기는 코드는 정말 보기 흉합니다. * 반드시 모든 최상위 함수에 타입 시그니처를 붙이세요. 타입 시그니처는 문서화를 강화하고 생각을 명확하게 해주며 멸종 위기 조류 종을 위한 둥지 장소도 제공합니다.[1] 최상위 타입 시그니처는 더 좋은 에러 메시지를 만들어주기도 합니다. 타입 시그니처가 없으면 타입 에러가 실제 문제와 멀리 떨어진 곳에서 나타나는 경향이 있습니다. 명시적인 타입 시그니처는 타입 에러를 정확한 위치에서 찾아낼 수 있게 도와줍니다. let 표현식이나 where 절 안에 정의된 지역 함수와 상수들은 타입 시그니처를 붙이지 않아도 되지만 붙여도 전혀 해가 되지 않습니다.(특히 위에서 말한 타입 에러 국지화 효과는 여전히 적용됩니다.) * 반드시 모든 최상위 함수 앞에 그 함수가 무엇을 하는지 설명하는 주석을 붙이세요. * 반드시 `-Wall`을 사용하세요. ghc 명령줄에 `-Wall`을 직접 전달하거나(더 쉬운 방법으로) `.hs` 파일 맨 위에 다음을 추가하세요. `{-# OPTIONS_GHC -Wall #-}` 제출하는 모든 프로그램은 경고 없이 컴파일되어야 합니다. * 반드시 가능한 한 프로그램을 한 가지 일만 하는 작은 함수들로 쪼개고 그것들을 조합하여 더 복잡한 함수를 만들도록 하세요. * 반드시 모든 함수를 전함수[2]로 만드세요. 즉, 가능한 모든 입력에 대해 합리적인 결과를 반환하고 절대 크래시하지 않아야 합니다. * * * 1. 무슨 농담인지 누가 설명 좀 해주세요. ↩︎ 2. total function. ↩︎
012 0
박준규 @curry.hackers.pub.ap.brid.gy · 16/04/2026
@x_nuk 그런데 여기에는 지원 언어 목록에 하스켈이 있습니다. 아마 과거에 한 번이라도 지원했으면 다 적어 놓은 것 같아요. www.goodbye-boj.com
goodbye-boj.com
Good Bye! BOJ! - 백준 메모리얼
16년간 대한민국 알고리즘 트레이닝의 뿌리가 되어준 백준 온라인 저지의 마지막을 기억하며.
000 0
박준규 @curry.hackers.pub.ap.brid.gy · 05/02/2026
야 아니 그런데 지금 보니까 이때도 LLM이 알려준 거였잖아? 그때는 왜 sumEncoding 안 알려줬어...
000 0
박준규 @curry.hackers.pub.ap.brid.gy · 05/02/2026
이때 막 프라그마 써가면서 Either의 ToJSON 인스턴스를 원하는대로 구현하려고 애썼는데 알고 보니까 더 쉬운 다른 방법이 있었다. 다음과 같이 sumEncoding이라는 옵션을 이용하면 된다. toJSON = genericToJSON defaultOptions { sumEncoding = UntaggedValue } 전에는 이렇게 좋아하는 언어의 소소한 팁을 라이브러리 문서에서 찾았을 때 SNS에 공유하는 게 삶의 낙이었는데 요즘 같은 LLM 에이전트 딸깍 시대에는 이런 게 다 무슨 소용인가 […]
hackers.pub
Original post on hackers.pub
111 0
박준규 @curry.hackers.pub.ap.brid.gy · 19/01/2026
@hongminhee 앗! 기존 라이브러리 업데이트 하시나요, 새로운 프로젝트인가요?ㅎ
100 0
Reposted by 박준규
치즈쥐🧀스우 @psudots.bsky.social · 25/12/2025
실크송, 셀레스트 등의 게임을 좋아하구, 인외를 좋아해요. 인외러들 많이 와주세요😋 가끔 가뭄에 콩 나듯 도트를 찍으며 약간의 게임 코딩을 할 줄 알아요 현재 블친님들 몇 분과 함께 팀을 이루어, 행성 탐험과 가게 경영을 곁들인 sf 인외 게임을 만들고있어요! 흔적 남기면 찾아갈게요, 특히 저희 게임, 스크립터와 코딩을 구하고 있으니 많은 관심 또한..! #블친소 #그림러_블친소 #개발자_블친소 #퍼리_블친소 #인외러_블친소
04334 0
박준규 @curry.hackers.pub.ap.brid.gy · 16/12/2025
hackers.pub
하스켈 책 보충 자료
책 《하스켈 병렬 및 동시성 프로그래밍(원제: Parallel and Concurrent Programming in Haskell》 8장을 읽다 보면 다음과 같은 문구가 나옵니다. > We will use the following function to download a web page: > > > getURL :: String -> IO ByteString > > This function is provided by the module GetURL in GetURL.hs, which is a small wrapper around the API provided by the HTTP package. 그런데 GetURL.hs라는 파일을 찾을 수가 없어요. 저처럼 예제 코드 하나라도 실행 안 되면 진도를 못나가는 사람을 위해서 안내를 남깁니다.(사실은 미래의 저를 위한 글입니다.) 인터넷에서 검색을 좀 해보면(예전에 찾아 놓은 거고 지금은 원본 링크를 찾을 수가 없지만) 메릴랜드 대학의 CSMC 433이라는 과정에서 공교롭게 책과 같은 내용이 있고 그곳에서 다음과 같은 코드를 소개합니다. -- For the example to work you need to have -- the `bytestring` and the `download-curl` -- haskell packages installed import Data.ByteString as B import Network.Curl.Download getURL :: String -> IO ByteString getURL uri = do res <- openURI uri case res of Left _ -> error "Oh no..." Right bs -> return bs '됐다! 이거다!' 싶지만 막상 이 코드를 넣고 8장의 예제를 실행하면 기대했던대로 결과가 안 나오고 다음과 같이 페이지 응답 본문의 크기가 0으로 나옵니다. (0,0) 이건 왜 그럴까? curl로 응답 헤더를 보니 https로 리다이렉트하고 있었네요. $ curl -I http://www.wikipedia.org/wiki/Shovel HTTP/1.1 301 Moved Permanently content-length: 0 location: https://www.wikipedia.org/wiki/Shovel server: HAProxy x-cache: cp5024 int x-cache-status: int-tls connection: close 그래서 본문 내용이 없었기 때문에 크기가 0으로 나온 것입니다. 이때는 openURI 대신 openURIWithOpts를 쓰면 됩니다. 여러 옵션이 있는데 그중 CurlFollowLocation True를 사용합니다. res <- openURIWithOpts opts uri ... where opts = [ CurlFollowLocation True ] '와, 이제 진짜 되겠지?' 해도 안 되는데 이번에는 404 응답이 옵니다. '어? 브라우저랑 curl로 요청했을 때는 존재하는 페이지인데 왜 404래?' 아마 위키백과에서 요청 헤더에 User-Agent가 없으면 차단을 하는 것 같습니다. 마지막으로 다음과 같이 옵션을 추가합니다.(이건 GPT가 알려줬어요.) opts = [ CurlFollowLocation True , CurlUserAgent "foo bar" ] 여기까지 하면 이제 책에 나온 예제가 잘 실행됩니다. 즐거운 하스켈 코딩 하세요!
031 0
박준규 @curry.hackers.pub.ap.brid.gy · 11/12/2025
www.surveymonkey.com/r/6M3Z6NV
surveymonkey.com
State of Haskell 2025
Take this survey powered by surveymonkey.com. Create your own surveys for free.
011 0
박준규 @curry.hackers.pub.ap.brid.gy · 05/12/2025
연합우주라는 번역은 어느 분이 처음 하셨나요? 엄청난 초월 번역인 것 같습니다. 한편 Gemini 프로토콜을 통해 제공되는 모든 파일의 전체 모음을 Geminispace라고 부르는데 어떻게 번역하는 게 좋을까요?
021 0
박준규 @curry.hackers.pub.ap.brid.gy · 04/12/2025
rosettalens.com/s/ko/how-to-kill-de…
rosettalens.com
탈중앙화 네트워크(예: 페디버스)를 말살하는 법 - RosettaLens 번역
GAFAM 제국이 지배하는 인터넷에서, 메타와 같은 거대 기업이 어떻게 XMPP와 같은 과거의 사례처럼 탈중앙화 네트워크와 페디버스를 무력화하고 파괴할 수 있는지, 그리고 이를 막기 위해 어떤 태도가 필요한지에 대한 분석
013 0
박준규 @curry.hackers.pub.ap.brid.gy · 26/11/2025
rosettalens.com/s/ko/gsoc-2025
rosettalens.com
Google Summer of Code 2025 회고 | Haskell 프로그래밍 언어 블로그 - RosettaLens 번역
Haskell.org가 Google Summer of Code 2025에서 수행한 프로젝트 결과를 정리합니다. Haskell Language Server, Liquid Haskell, Cmm 관련 프로젝트가 성공적으로 완수되었습니다.
000 0
박준규 @curry.hackers.pub.ap.brid.gy · 25/11/2025
hocket이라고 지어야...
010 0
박준규 @curry.hackers.pub.ap.brid.gy · 21/11/2025
`gemini://`에서는 인라인 링크도 인라인 코드 블록도 안 되는데 이게 읽기를 방해하지 않는다는 점이 좋다. `goto` 없는 마크다운이라고나 할까?
010 0
박준규 @curry.hackers.pub.ap.brid.gy · 21/11/2025
GBIC에 * 1310 nm라고 적혀 있으면 싱글이고 * 850 nm라고 적혀 있으면 멀티라고 한다. 광케이블의 직경을 의미하는데 단위는 나노미터이다. ... 인 줄 알았는데 찾아보니까 아니네. 직경이 아니라 파장이네.
000 0
박준규 @curry.hackers.pub.ap.brid.gy · 20/11/2025
인터넷 프로토콜 제미니와 페디버스를 연결한 프로젝트 github.com/dimkr/tootik
github.com
GitHub - dimkr/tootik: A federated, text-based social network with a Gemini frontend
A federated, text-based social network with a Gemini frontend - dimkr/tootik
020 0
박준규 @curry.hackers.pub.ap.brid.gy · 17/11/2025
rosettalens.com/s/ko/8bbc0343bf794c…
rosettalens.com
모나드는 포켓몬이다: 수학 없이 배우는 모나드 입문 - RosettaLens 번역
수학적 형식주의를 최대한 배제하고, 다양한 구체적 예로 모나드를 실용적으로 이해하는 입문서. Maybe/Either/리스트/ST/Identity/Free/IO 모나드와 do 표기법, 그리고 모나드를 효과적으로 학습·사용하는 방법을 다룹니다.
020 0
Reposted by 박준규
juxtapose @xt.hackers.pub.ap.brid.gy · 16/11/2025
`System.IO.readFile` 쓰지 마세요. `System.IO.readFile` 쓰지 마세요. `System.IO.readFile` 쓰지 마세요. 첫째, `System.IO.readFile` 은 `String` 을 반환합니다. 이건 심각한 문제입니다. `String` 을 쓰지 마세요. 외부 세계와 I/O 를 할 때에는 `ByteString` 을 써야 합니다. 유니코드 문자열을 다룰 때에는 `Text` 를 쓸 수 있습니다. `String` 은 시간적으로도 공간적으로 비효율적입니다. 이건 어쩔 수 없습니다 […]
hackers.pub
Original post on hackers.pub
026 0
박준규 @curry.hackers.pub.ap.brid.gy · 14/11/2025
`:grep`도 너무 편함!
000 0
박준규 @curry.hackers.pub.ap.brid.gy · 14/11/2025
이때 `auto write`를 이용하면 코드 수정 후 따로 `:w`로 저장하지 않아도 되고 autocmd QuickFixCmdPost make cwindow 를 쓰면 에러가 있을 때 Quickfix List가 자동으로 나타남.
010 0
박준규 @curry.hackers.pub.ap.brid.gy · 14/11/2025
Vim에서 Quickfix List라는 걸 처음 알게 되었다. 기존 코딩 흐름은 G 코딩 코딩 Vim을 닫는다. Vim을 닫는다. 코딩->Vim을 닫는다. cabal build cabal build Vim을 닫는다.->cabal build 에러 확인 에러 확인 cabal build->에러 확인 Vim을 연다. Vim을 연다. 에러 확인->Vim을 연다. 에러가 발생한 행(row)으로 간다. 에러가 발생한 행(row)으로 간다. Vim을 연다.->에러가 발생한 행(row)으로 간다. 에러가 발생한 행(row)으로 간다 […]
hackers.pub
Original post on hackers.pub
231 0
박준규 @curry.hackers.pub.ap.brid.gy · 14/11/2025
하스켈을 금지한다! RE: yodangang.express/notes/af1sb2vw3c8…
yodangang.express
112 0
박준규 @curry.hackers.pub.ap.brid.gy · 13/11/2025
하스켈에서 다음과 같은 에러를 만날 경우에 withFile: resource busy (file is locked) `readFile` 대신 `readFile'`을 써보셔요! * `readFile`은 lazy 버전이고 * `readFile'`은 strict 버전입니다! `System.IO` 모듈 문서에 다음과 같은 설명이 있습니다. > 경고: `readFile` 연산은 파일의 전체 내용을 모두 소비할 때까지 그 파일에 대해 부분적으로 닫힌(semi-closed) 핸들을 유지한다. 따라서 이전에 […]
hackers.pub
Original post on hackers.pub
022 0
Reposted by 박준규
洪 民憙 (Hong Minhee) :nonbinary: @hongminhee.hollo.social.ap.brid.gy · 11/11/2025
12月(월) 6日(일) 서울에서 開催(개최)되는 liftIO 2025에서 〈Optique: TypeScript에서 CLI 파서 컴비네이터를 만들어 보았다〉(假題(가제))라는 主題(주제)로 發表(발표)를 하게 되었습니다. 아직 liftIO 2025 티켓은 팔고 있으니, 函數型(함수형) 프로그래밍에 關心(관심) 있으신 분들의 많은 參與(참여) 바랍니다!
event-us.kr
liftIO 2025 - 이벤터스
2021년부터 매년 말에 개최되고 있는 liftIO는 함수형 프로그래밍 언어를 사용하는 생생한 경험담과 인사이트를 제공하는 개발 컨퍼런스입니다.
142 0
박준규 @curry.hackers.pub.ap.brid.gy · 11/11/2025
liftIO에서 `liftIO`를 설명하면 재밌을 것 같은데 그러려면 모나드 트랜스포머를 설명해야 하고 그러려면 모나드를 설명해야 하고...
011 0
박준규 @curry.hackers.pub.ap.brid.gy · 03/11/2025
hackers.pub
참 쉽죠?
어제 그림 그리기를 좋아하는 딸래미와 함께 유튜브로 밥 아저씨 영상을 봤습니다. 밥 아저씨 특징이 그림에 '공을 들이지 않는다.'라고 하는데 저도 해커즈 퍼브에 남기는 글에 공을 들이지 않아 보려고 합니다. 왜냐면 며칠 전부터 아래 적을 주제를 어떻게 어디서부터 적을까 고민만 하다가 끝내 쓰지 못하고 있었기 때문입니다. 제가 쓰려는 건 엄청 간단한 건데 예상하셨겠지만 역시 하스켈 이야기입니다. `ghc`, `cabal` 등은 이미 `ghcup`으로 설치했다고 전제하고 이야기를 시작합니다. 하스켈 프로젝트를 처음 시작하면 `cabal`을 어떻게 써야 할지 모르는 경우가 많습니다. 그럴 때는 저처럼 시작하면 좋습니다. 우선 프로젝트 이름을 정합니다. 저는 `bar`로 하겠습니다. 프로젝트 이름을 정했으면 그 이름으로 디렉터리를 생성하고 이동합니다. $ mkdir bar $ cd bar 그 다음에는 크게 두 가지 중에 하나를 골라야 하는데 * 실행 파일을 만들 건지 * 라이브러리를 만들 건지 정해야 합니다. 여기서는 복잡하니까 실행 파일을 만들기로 하겠습니다. 다음과 같이 입력하면 프로젝트에 필요한 기본적인 파일과 디렉터리가 생성됩니다.(여기까지 썼는데 생각한 것보다 글이 길어져서 긴 글로 옮겨 적습니다.) cabal init --minimal --simple --exe 생성된 파일과 디렉터리는 다음과 같습니다. % tree . ├── app │   └── Main.hs ├── bar.cabal └── CHANGELOG.md 2 directories, 3 files `app/Main.hs`에 적힌 코드는 다음과 같습니다. module Main where main :: IO () main = putStrLn "Hello, Haskell!" 이제 다음과 같이 입력하면 프로젝트를 빌드하고 앱을 실행할 수 있습니다. cabal build cabal run 출력 화면은 다음과 같습니다. % cabal run Warning: The package list for 'hackage.haskell.org' is 34 days old. Run 'cabal update' to get the latest list of available packages. Resolving dependencies... Build profile: -w ghc-9.6.7 -O1 In order, the following will be built (use -v for more details): - bar-0.1.0.0 (exe:bar) (first run) Configuring executable 'bar' for bar-0.1.0.0... Preprocessing executable 'bar' for bar-0.1.0.0... Building executable 'bar' for bar-0.1.0.0... [1 of 1] Compiling Main ( app/Main.hs, /Users/vincent/work/bar/dist-newstyle/build/aarch64-osx/ghc-9.6.7/bar-0.1.0.0/x/bar/build/bar/bar-tmp/Main.o ) [2 of 2] Linking /Users/vincent/work/bar/dist-newstyle/build/aarch64-osx/ghc-9.6.7/bar-0.1.0.0/x/bar/build/bar/bar Hello, Haskell! 후, 공을 들이지 않기로 했으니까 여기까지만 적겠습니다. 참 쉽죠?
051 0
Reposted by 박준규
초무 @hackers.pub.chomu.dev · 26/10/2025
해커스펍 커모지로 언어&프레임워크 로고 추가해주세요
011 0
Reposted by 박준규
초무 @hackers.pub.chomu.dev · 26/10/2025
하스켈 붐은 온다 discourse.haskell.org/t/ghc-now-run…
discourse.haskell.org
Ghc now runs in your browser
ghc itself can now run purely client-side in the browser, here’s a haskell playground demo. terms and conditions apply, and i’ll write up more detailed explanation some time later, but i thought this is a cool thing to show off how far the ghc wasm backend has advanced 🙂
023 0
박준규 @curry.hackers.pub.ap.brid.gy · 23/10/2025
hackers.pub
《하스켈로 배우는 프로그래밍》(2009, 대림) 옮긴이의 말
이 글은 2009년에 대림출판사에서 낸 책 《하스켈로 배우는 프로그래밍》 옮긴이의 말을 안기영 교수님의 허락을 받아 공유하는 것입니다. ## 옮긴이 이미 하스켈에 관심을 갖고 이 책을 선택한 분들에게 이 옮긴이의 말은 그저 지은이 머리말과 책 1장 내용의 장황한 사족에 지나지 않을 것입니다. 특히 프로그래밍을 처음 배울 목적으로 이 책을 선택하신 분들은 이 장황한 글을 건너뛰어도 좋습니다. 그럼에도 굳이 옮긴이로서 머리말을 쓰는 까닭은, 현업 IT 전문가의 입장에서 하스켈이라는 프로그래밍 언어가 어떤 의미로 다가올 수 있을지 좀 더 구구절절한 이야기를 풀어놓고 싶기 때문입니다. 하스켈은 원래 더 나은 프로그래밍 언어를 고안하려는 프로그래밍 언어 연구자들이 느긋한 계산법을 따르는 순수 함수형 언어를 표준화함으로써, 연구자들끼리 아이디어를 더욱 효과적으로 교류하려는 연구 목적으로 만들어지기 시작한 언어입니다.[1] 그래서 하스켈은 자연히 프로그래밍 언어 분야의 연구 성과를 충실하게 반영하도록 설계되었고, Hugs나 GHC와 같은 하스켈 구현에 연구자들이 꾸준히 참여하여 하스켈 98 표준 이외에도 최근의 프로그래밍 언어 연구 결과를 적용한 여러 가지 확장 기능을 추가로 제공하고 있습니다. 이러한 설계와 구현은 언어 자체의 학술적 심미성으로만 그치지 않았으며, 안목 있는 IT 전문가들의 눈에 하스켈은 빠르고 정확하게 소프트웨어를 개발할 가능성을 열어주는 프로그래밍 언어로 돋보이게 했습니다. 이런 IT 전문가들이 업무에 하스켈을 도입했고 그 과정에서 축적된 경험의 일부를 오픈 소스로 공개하여 활발한 하스켈 개발자 공동체를 만들기에 이르렀습니다. 그리하여 하스켈은 오늘날 연구자들에게 각광받는 학술적 프로그래밍 언어인 동시에 범용적인 프로그래밍을 빠르고 정확하게 작성할 수 있는 실무적 프로그래밍 언어로 발전하였습니다. ## 실무적인 범용 언어로서의 하스켈 하스켈은 군용 보안 소프트웨어 작성(예: Galois), 금융상품 분석(예: Credit Suisse), 하드웨어 설계(예: Bluespec), DNA 및 고분자 화합물을 이용한 신약 개발(예: Amgen) 등 다양한 업계에서 이미 많은 이윤을 창출하는 상용 소프트웨어의 핵심 기술 구현에 요긴하게 쓰이고 있습니다. 예로 든 대표적인 기업 외에도 매년 함수형 프로그래밍 국제 학회(The ACM SIGPLAN International Confrerence on Functional Programming)와 함께 열리는 함수형 프로그래밍 사용 기업(Commercial Users of Functional Programming) 모임이 최근 몇 년간 성황을 이루며 하스켈을 비롯한 함수형 언어를 업무에 성공적으로 도입한 기업들의 사례 보고가 잇따르고 있는데, 그 중 하스켈을 업무에 쓰는 기업들의 목록[2]이 하스켈 홈페이지에 정리되어 있습니다. 그리고 하스켈을 업무에 도입하려는 기업에 자문을 제공하는 컨설턴트들[3]도 생겨나고 있습니다. 하스켈 오픈 소스 소프트웨어 공동체의 활동 또한 요즘 들어 더욱 활기를 띠고 있습니다. 널리 쓰이는 스크립트 언어인 펄의 차세대 판 Perl 6 구현 Pugs[4], 패치 이론 바탕의 분산 버전 관리 시스템 Darcs[5], 그리고 하스켈 개발에 가장 많이 쓰이는 글래스고우 하스켈 컴파일러[6](Glasgow Haskell Compiler, GHC) 또한 하스켈로 개발이 진행되는 오픈 소스 프로젝트입니다. 이런 굵직한 프로젝트 외에도 각종 하스켈 라이브러리 및 응용 프로그램, 그리고 C 등 다른 언어로 작성된 기존 라이브러리와의 연동 등 요긴한 중소 규모 프로젝트가 활발히 진행되고 있습니다. 아치 리눅스는 현재 500개 이상의 하스켈 관련 배포판 패키지를 제공하고 있으며[7], 데비안이나 우분투 리눅스도 약 250개 가량의 배포판 패키지를 제공하고 있습니다. 이렇게 하스켈 오픈 소스 개발 활동이 활기를 띠는 것은 최근 몇 년간에 걸쳐 BSD의 port 패키지 관리 시스템과 유사한 하스켈 패키지 관리 시스템인 Cabal[8]이 하스켈 라이브러리와 프로그램을 배포하는 사실상의 표준으로 자리잡도록 했으며, 펄의 CPAN과 같은 Hackage[9]라는 하스켈 패키지 저장소를 중심으로 하스켈 개발자들이 만든 오픈소스 패키지들을 한데 모아 배포하는 등 하스켈 개발자 공동체의 노력이 모인 결과입니다. 참고로 이 책은 처음 프로그래밍을 배우는 분들도 읽을 수 있도록 구성된 프로그래밍 입문서이므로 하스켈을 실무에 응용하는 보기를 다루지는 않습니다. 이 책을 읽고 하스켈을 실무에 응용하는데 관심을 갖게 될 IT 전문가에게는 최근 출간된 『Real World Haskell』[10]을 이 책 다음으로 살펴볼 것을 추천합니다. 그 책은 실제로 하스켈을 업무에 쓰는 개발자들이 자신들의 경험을 정리해 놓은 책은 책으로 데이터베이스, 웹, 시스템, GUI, 네트워크, 병렬 프로그래밍 등을 아우르는 실무적인 예제를 포함합니다. ## 프로그래밍 언어 연구 성과를 충실히 반영한 하스켈의 우수성 그렇다면 여러 분야에서 업계를 선도하는 기업의 IT 전문가들이 더 잘 알려진 다른 프로그래밍 언어들을 두고 왜 굳이 하스켈을 업무에 도입했을까요? 업계마다 기업마다 업무가 판이하게 다르기 때문에 정확히 어떤 이유라고 억측할 수는 없습니다. 하지만 우리가 하스켈을 써 보면서 발견한 것과 마찬가지로 그들도 하스켈을 쓰면서 다른 프로그래밍 언어와 대조되는 장점을 발견하여 하스켈에 매료되었음이 틀림없습니다. 옮긴이들의 제한된 경험으로 하스켈의 면면을 모두 비출 수는 없겠지만, 우리가 하스켈을 직접 쓰면서 그리고 하스켈을 도입하여 성공한 프로젝트들을 접하면서 알게 된 바를 간단히 정리해 보았습니다. ### 효율성 높은 컴파일 언어이면서도 스크립트 언어처럼 잽싼 하스켈 하스켈은 스크립트 언어의 장점과 컴파일 언어의 장점을 동시에 갖는 언어입니다. 스크립트 언어는 개발을 신속하게 진행할 수 있다는 장점을 내세우고 있습니다. 이는 스크립트 언어가 대개 고급 언어를 직접 실행하는 해석기(Interpreter)를 바탕으로 하기 때문인데, 프로그램을 수정한 후 바로 해석기에 불러들여 그 수정한 부분만 따로 검사하기에 매우 편리합니다. 하지만 해석기를 바탕으로 한 언어는 번역기(Compiler)로 미리 저급 언어로 번역한 다음 실행하는 컴파일 언어보다 효율이 확연히 떨어지기 때문에 반복적인 수치연산 등 계산량이 많은 코드를 직접 작성하기에 알맞지 않다는 단점이 있습니다. 반면 컴파일 언어는 스크립트 해석기로 돌리는 것보다 훨씬 효율이 뛰어난 실행 파일을 생성할 수 있다는 것이 장점입니다. 그러나 C와 같은 컴파일 언어는 작은 부분을 고치더라도 전체 프로그램을 실행 파일로 다시 연결한 다음 돌려보아야 하며, 프로그램 시작 지점부터 항상 실행을 시작하기 때문에 수정한 부분만 따로 떼어 검사하기 번거롭다는 단점이 있습니다. 하스켈과 같은 함수형 언어의 경우, 프로그래밍 언어 연구자들이 이미 수십 년 전부터 확립한 해석기와 번역기의 경계를 넘나드는 기술을 구현하고 있었습니다. 그렇기 때문에 마치 스크립트 언어처럼 신속한 개발도 가능하고 저급 언어로 번역된 효율성 높은 실행 파일도 얻을 수 있습니다. 최근에는 고급 언어와 그를 번역한 보면 저급 언어를 연동하는 기술이 함수형 언어 구현 뿐 아니라 가상머신 기반의 언어나 스크립트 언어가 가지는 효율상의 한계를 극복하기 위한 JIT 번역(Just-In-Time compile) 등에도 응용되고 있습니다. 많은 설명보다 간단한 보기로 대표적인 스크립트 언어인 파이썬과 하스켈을 비교해 보면 하스켈 역시 신속한 개발을 지원하는 언어라는 사실이 와닿을 것입니다. 예컨대, 파이썬 스크립트 `test.py`에 제곱을 구하는 함수를 아래와 같이 잘못 정의한 다음 def square(x): return x + x 해석기에서 불러들여 함수를 시험해 보면 잘못된 결과를 얻습니다. >>> import test >>> test.square(3) 6 이제 `test.py`의 `square` 함수 정의를 다음과 같이 바르게 고친 다음 def square(x): return x * x 해석기에서 다시 불러들이면 바뀐 함수 정의대로 올바른 결과를 얻습니다. >>> reload(test) <module 'test' from 'test.py'> >>> test.square(3) 9 이렇듯 신속하게 오류를 수정하고 스크립트를 다시 불러들여 바로 시험해 볼 수 있다는 장점이야말로 스크립트 언어가 각광받게 된 가장 큰 이유라고 생각합니다. 하스켈과 같은 함수형 언어의 대화식 환경[11]에서도 이와 마찬가지로 개발을 신속하게 진행할 수 있습니다. 하스켈 스크립트 `Main.hs`에 제곱을 구하는 함수를 다음과 같이 잘못 정의한 다음 square x = x + x GHC의 대화식 환경인 ghci에서 불러들여 함수를 시험해 보면 잘못된 결과를 얻습니다. Prelude> :load Main Main> square 3 6 이제 `Main.hs`의 `square` 함수 정의를 다음과 같이 바르게 고친 다음 square x = x * x 다시 불러들이면 바뀐 함수 정의대로 올바른 결과를 얻습니다. Main> :reload Main> square 3 9 하지만 GHC에는 대화식 환경 뿐만 아니라 ghc 번역기가 있으므로 완전히 기계어로 번역된 실행 파일을 얻을 수도 있다는 것이 일반적인 스크립트 언어와 차별화되는 점입니다. 다음과 같이 `Main.hs`에 프로그램 시작을 나타내는 간단한 `main` 함수를 추가한 후 square x = x * x main = print (square 3) GHC 번역기로 실행 파일을 만들어 돌려볼 수 있습니다. $ ghc Main.hs $ ./a.out 9 ### 안전한 정적 타입 언어이면서도 동적 타입 언어처럼 유연한 하스켈 하스켈은 안전한 정적 타입 시스템을 바탕으로 하고 있으면서도 동적 타입 언어에 가까운 유연성을 갖는 언어입니다. 그 이유를 전문 용어를 동원해 설명하자면 하스켈은 Hindley-Milner 타입 시스템을 바탕으로 할 뿐 아니라, 타입 클래스라는 독특한 기능을 갖고 있기 때문이라 할 수 있습니다. Hindley-Milner 타입 시스템을 바탕으로 하는 언어는 타입 유추(Type Inference)를 바탕으로 인자 여러모양새(Parametric Polymorphism)를 자연스럽게 구사함으로써 동적 타입 언어처럼 유연하게 포괄적인 코드 작성이 가능합니다. 타입 클래스는 여러 의미(Overloaded) 함수를 Hindley-Milner 타입 시스템과 잘 어우러지면서도 깔끔하게 정의하기 위해 하스켈에서 도입한 독특한 기능으로, 물건 중심 언어의 인터페이스(Interface)와 비슷하지만 그보다 더 유연하고 확장성이 있습니다. 여러모양새(Polymorphism)란 정적 타입 언어에서 타입에 따른 코드 중복을 줄이고자 함수를 한 번만 정의하여 여러 모양으로 쓸 수 있도록 하는 기능을 일컫는데, 그 방법에 따라 여러 종류가 있습니다. 함수형 언어에서는 대개 인자 여러모양새를 구사하며 물건 중심(Object Oriented) 언어에서는 대개 하위 타입 여러모양새(Subtype Polymorphism)를 구사합니다. 대표적인 물건 중심 언어인 자바(Java)에서 아래와 같이 입력 스트림으로부터 내용을 읽어들이는 `getContents`[12] 함수를 한 번만 정의하면 String readContents(InputStream is) { /* ... */ } 인자 타입으로 선언한 `InputStream` 타입의 물건(Object)인 `System.in`은 물론 그 하위 타입인 `FileInputStream`이나 `DataInputStream` 타입의 물건인 `fis`나 `dis`에도 적용할 수 있는데 FileInputStream fis; DataInputStream dis; // ... String s1 = readContents(System.in); // InputStream String s2 = readContents(fis); // FileInputStream String s3 = readContents(dis); // DataInputStream 이러한 기능을 바로 하위 타입 여러모양새라고 합니다. 한편, 현대적인 타입 시스템을 갖춘 함수형 언어인 하스켈에서는 아래와 같이 `length` 함수를 한 번만 정의하면 length [] = 0 length (x:xs) = 1 + length xs 정수, 글자 등 아무 타입 원소를 갖는 리스트에 모두 적용할 수 있는데 Main> length [2,3,5,7] -- [Int] (정수 리스트) 4 Main> length ['a','b','c'] -- [Char] (글자 리스트) 3 Main> length [[],[0]] -- [[Int]] (정수 리스트의 리스트) 2 이러한 기능을 바로 인자 여러모양새라고 합니다. 과거에 인자 여러모양새를 직접 구현할 수 없었던 정적 타입 물건 중심 언어들이 다른 방법으로 에둘러 인자 여러모양새를 시늉내느라 큰 불편을 겪다 결국 포괄적 프로그래밍(Generic Programming)을 지원하는 언어 기능을 덧붙일 수밖에 없었습니다. 초기 C++의 경우, 인자 여러모양새를 흉내내기 위해 C에서처럼 조악한 전처리 매크로나 타입 안정성이 전혀 보장되지 않는 `void *`를 거치는 임의적 형변환 방식에 의존해야 했습니다. 이후 C++ 표준 라이브러리를 제대로 설계하면서 템플릿을 언어에 추가하여 템플릿 메타프로그래밍으로 인자 여러모양새를 직접 표현할 수 있게 됩니다. Java의 경우 모든 물건들의 최상위 타입인 `Object`에 대한 하위 타입 여러모양새를 이용하되 필요에 따라 동적으로 `Object` 타입으로부터 원래의 상위 타입으로 강제 변환을 하는 방식을 쓸 수 밖에 없었습니다. C의 `void *` 보다야 낫지만 동적인 하위 타입으로의 강제 변환 역시 타입 안정성을 보장하지 못하므로 정적 타입 언어의 장점을 거스릅니다. 결국 Java도 뒤늦게 제너릭을 추가면서[13] 타입을 검사하고 나중에 지우는 방식으로 인자 여러모양새를 직접 표현할 수 있게 됩니다. 이러한 역사적 사실을 종합해 보면 C++ 템플릿이나 Java 제너릭과 같은 포괄적 프로그래밍의 유래가 바로 탄탄한 이론을 바탕으로 설계된 정적 타입 함수형 언어에서 일상적으로 사용하던 인자 여러모양새라는 것을 알 수 있습니다. 함수형 언어의 인자 여러모양새와 오브젝트 중심 언어의 포괄적 프로그래밍이 다른 점은 함수형 언어에서는 인자 여러모양새가 걸음마 프로그래머도 구사하는 기본 초식인 반면 오브젝트 중심 언어에서는 포괄적 프로그래밍을 상대적으로 많은 노력을 요하는 고급 기술로 여긴다는 것입니다. 오브젝트 중심 언어에서 포괄적 함수나 클래스를 작성하려면 보통 함수나 클래스를 작성할 때와 달리 포괄적 인자를 별도로 표시하는 등의 노력이 추가로 필요합니다. 그렇기에 라이브러리 설계자라면 C++나 Java와 같은 언어로는 여유를 두고 포괄적 프로그래밍을 잘 지원하는 라이브러리를 만들 수 있을 것입니다. 하지만 상대적으로 신속한 개발을 요하는 응용 프로그램 계발자라면 이미 만들어진 포괄적 라이브러리를 즐겨 쓸지는 몰라도 포괄적 프로그래밍의 장점을 살리는 코드 작성을 선뜻 내켜하지 않을 가능성이 높습니다. C++나 Java를 다루는 책들도 대개 포괄적 프로그래밍을 고급 기법으로 분류하곤 합니다. 정적 타입 오브젝트 중심 언어에만 익숙했던 개발자들이 파이썬과 같은 동적 타입 스크립트 언어에 열광한 또 하나의 이유가 바로 이것이라 생각합니다. 동적 타입 스크립트 언어는 타입을 무시하고 상대적으로 적은 노력으로 포괄적 프로그래밍을 할 수 있습니다. 그래서 이런 스크립트 언어가 유행한 후 동적 타입 언어만이 유연하며 정적 타입 언어는 유연하지 않다는 생각이 퍼지게 되었습니다. 그러나 현대적 타입 시스템을 갖춘 함수형 언어를 접해 보았다면 정적 타입 언어가 유연할 수 없다는 것은 섣부른 편견이 아닌가 의문을 제기할 수밖에 없을 것입니다. 왜냐하면 앞서 살펴본 `length` 함수처럼 하스켈에서는 타입 정보를 전혀 표시하지 않고도 여러모양 함수를 작성할 수 있으며, 타입 시스템이 자동으로 그 타입을 유추해 주기 때문입니다. 함수형 언어를 전혀 접해 보지 않은 개발자가 `length`의 정의를 보면 하스켈을 동적 타입 스크립트 언어로 오해할 정도입니다. 이렇듯 하스켈로 프로그래밍을 배우기 시작하면 인자 여러모양새를 자신도 모르게 구사하며 유연한 포괄적 프로그래밍을 하게 된다는 것을 이 책을 통해 발견하게 될 것입니다. 그리고 비록 당장의 업무에 하스켈을 도입하지 않더라도 하스켈을 익히면서 오브젝트 중심 언어에서 고급 기법으로 여기는 포괄적 프로그래밍을 자신있게 구사할 내공을 쌓는 재미 또한 쏠쏠할 것입니다. 타입 클래스(Type Class)란 말 그대로 타입(Type)의 분류(Class)로써 덧셈이나 곱셈과 같이 모든 타입에 다 적용 가능한 여러모양 함수는 아니지만 공통점이 있는 많은 타입에 적용 가능한 함수의 타입을 딱 떨어지게 정의하기 위해 하스켈에서 도입한 독특한 기능입니다. 오브젝트 중심 언어의 인터페이스와 유사한 개념이지만 그보다 더 확장성이 있다는 점에서 뛰어나다는 것을 하스켈의 타입 클래스와 자바의 인터페이스를 비교함으로써 알아보기로 합시다. 하스켈의 타입 클래스와 Java의 인터페이스를 비교하기 전에 한 가지 짚고 넘어갈 것은, 오브젝트 중심 언어에서 클래스라 불리는 기능은 같은 이름이지만 완전히 다른 개념이라는 것입니다. Java와 같은 오브젝트 중심 언어에서는 클래스 정의가 곧 하나의 타입에 대응되지만 하스켈에서 타입 클래스는 타입을 모아 놓은 집합을 대표한다고 생각하면 이해하기 좋습니다. 하스켈의 타입 클래스는 오브젝트 중심 언어에서 주로 쓰는 인터페이스라는 기능과 비슷합니다. 예를 들면 Java에는 `java.lang.Comparable<T>`라는 인터페이스가 있는데 이것이 바로 하스켈의 `Ord` 클래스에 해당한다고 볼 수 있습니다. 예컨대, Java에서는 `MyType`이라는 새로운 타입을 정의하면서 다음과 같이 인터페이스를 상속받아 `Compare` 메서드를 구현합니다. // Java 인터페이스 정의 interface Comparable<T> { int compareTo(T x); // ... } // 타입을 정의하며 인터페이스 상속 public class MyType implements Comparable<T> { public int compareTo(MyType x) { // ... 메서드 구현 ... } // ... } 하스켈도 이와 비슷하게 새로 정의한 `MyType` 타입을 `Ord` 클래스의 인스턴스로 선언하고 메서드를 구현합니다. -- 하스켈 타입 클래스 정의 class Ord a where compare :: a -> a -> Ordering (<), (<=), (>=), (>) :: a -> a -> Bool max, min :: a -> a -> a -- 타입 정의 data MyType = -- ... -- 타입 정의와는 별도로 인스턴스 선언 instance Ord MyType where compare x y = -- ... 메서드 구현 ... 여기서 눈여겨 볼 점은 Java 인터페이스와 하스켈 타입 클래스의 차이점입니다. Java에서는 인터페이스 상속이 타입 정의의 일부이지만 하스켈에서는 타입을 정의한 다음 별도로 인스턴스 선언을 합니다. 작은 차이 같지만 타입 정의에 종속된 Java 인터페이스에 비해 타입 정의로부터 자유로운 하스켈 타입 클래스는 월등한 확장성을 갖습니다. 하스켈 타입 클래스는 타입을 정의한 후 언제라도 인스턴스 선언을 추가할 수 있습니다. 다른 모듈에 타입이 정의되어 있고 심지어 그 소스를 수정할 수 없는 라이브러리에 정의된 타입이라도 클래스 인스턴스로 선언하는 데는 아무 문제가 없습니다. 아직 하스켈에서 타입 정의와 타입 클래스가 느슨한 결합을 함으로써 갖는 장점이 실무에서 어떤 의미가 있을까 갸우뚱하는 분들은 위해 좀 더 피부에 와닿는 구체적인 상황을 상상해 보겠습니다. > OO기업 홍보부 지원 담당 데이터베이스 프로그래머 김대리는 Java에 능숙합니다. 홍보부에서 데이터베이스 검색은 웹 인터페이스로 자동화되어 있지만 검색한 고객 정보로 통계를 내기 위해 검색 결과를 매번 스프레드시트로 일일이 옮겨적어야 하는 애로사항이 있었습니다. 김대리는 이를 자동화하는 프로젝트를 Java로 시작하여 `Xmlizable`이라는 인터페이스를 정의하고 데이터베이스 엔트리 등 각종 데이터 구조를 `Xmlizable` 인터페이스를 상속받아 엑셀에서 읽어들일 수 있는 XML로 변환하도록 깔끔하게 처리했습니다. 이후 추가되는 테이블 엔트리도 `Xmlizable` 인터페이스를 상속해 XML 변환 메서드만 정의하면 되는 확장성 있는 시스템을 구축했다는 생각에 김대리는 행복합니다. 이 일로 김대리는 홍보부에서 신임을 받게 되었고 부장님은 더 정확한 통계 분석을 위해 이전 텍스트 기반 시스템에 남아있는 데이터도 스프레드시트로 불러올 수 있게 통합하는 프로젝트를 김대리에게 맡깁니다. 이전 시스템을 관리하던 개발자는 한참 전에 그만뒀지만 이전 시스템도 Java로 작성되어 있고 소스도 남아있다니 다행이었습니다. 업무가 갑자기 늘어나긴 했지만 김대리에게 큰 문제는 아닙니다. 기존 시스템에서 사용하던 소스코드에서 필요한 타입들이 `Xmlizable` 인터페이스를 상속하도록 고쳐 XML 변환 메소드만 구현해 주면 되므로 늘 하던 작업과 별다를 것이 없었습니다. > > 문제는 OO기업이 XX기업을 인수합병하면서부터 시작됩니다. XX기업 홍보부에서 쓰던 시스템을 통합하는 일이 김대리에게 떨어졌고 부장님은 당연히 김대리가 이번에도 금방 처리해 주겠거니 기대합니다. 김대리도 XX기업 역시 Java를 썼다는 보고에 따라 예전처럼 일정을 잡습니다. 그런데 XX기업의 홍보부는 효율을 매우 중시해서 비공개 상용 라이브러리를 구입해 쓰고 있었으며 데이터도 일반 DBMS가 아닌 Berkeley DB에 들어 있었던 것입니다. 이전처럼 인터페이스를 상속해 XML 변환 메소드만 추가하면 될 줄 알았는데, 소스도 없는 라이브러리라 인터페이스 추가는 언감생심입니다. 김대리는 임베디드 파일 DB란 게 있다는 말만 들었지 한번도 다뤄 본 일이 없어 DBMS 수준의 데이터 통합을 하려 해도 일정을 맞출 자신이 없습니다. 인수합병 과정의 칼바람같은 구조조정으로 XX기업 홍보부 전산팀은 온데간데 없고 회사 분위기도 워낙 어수선해져 어디 하소연할 데도 없습니다. 김대리는 분명 오브젝트 중심 디자인 원칙을 충실히 따랐는데 뭐가 잘못됐는지 영문을 알 수 없습니다. 내키지는 않지만 밤샘을 해서라도 그 상용 라이브러리에서 필요한 모든 타입을 한 겹씩 감싸안은 데이터 타입을 한 벌 새로 정의하는 방법밖에 없는 듯 합니다. 이런 걸 디자인 패턴에서 데코레이터(decorator) 패턴이라고도 한다니 그래도 아주 잘못된 방법은 아닐 것이라 애써 스스로를 위안해 봅니다만 뭔가 찜찜하고 억울하다는 느낌은 지울 수 없습니다. 만일 OO기업과 XX기업이 하스켈을 쓰고 있었고 김대리가 `Xmlizable`이라는 타입 클래스를 쓰고 있었다면 어땠을까요? 김대리는 고민할 필요도 없이 예전에 하던 대로 XX기업의 데이터 타입들을 `Xmlizable` 클래스의 인스턴스로 선언하여 XML 변환 메서드만 추가로 구현하기만 하면 될 것입니다. 왜냐하면 하스켈 타입 클래스는 타입 정의에 묶여 있지 않기 때문에 이전의 소스코드를 건드릴 필요가 없기 때문입니다. 마지막으로 하스켈의 타입 클래스와 Java의 인터페이스를 활용하는 보기를 통해 하스켈 타입 클래스가 오브젝트 중심 언어의 진화에 미친 영향을 짚어 보겠습니다. 하스켈에서 특정 클래스와 관련된 타입에만 제한적으로 적용 가능한 함수를 여러 의미 함수(Overloaded Function)라 합니다. 크기 비교가 가능한 타입으로 이루어진 리스트를 정렬해 주는 라이브러리 함수 `sort`가 바로 대표적인 여러 의미 함수의 예입니다. ghci 대화식 환경에서 sort의 타입을 알아보면 다음과 같습니다. List> :t sort sort :: Ord a => [a] -> [a] `Ord a =>`라는 클래스 제약이 있기 때문에 아무 리스트 `[a]`에 다 적용 가능한 것이 아니라 타입 인자 `a`가 `Ord` 클래스의 인스턴스일 때만 함수 적용이 가능합니다. Java에서도 하스켈의 `sort`에 상응하는 `sort` 함수를 다음과 같이 자바 제너릭 인자 `T`가 만족해야 할 인터페이스 제약 `extends Comparable<T>`를 표시하여 정의할 수 있습니다. public static <T extends Comparable<T>> List<T> sort(List<T> list) { // ... 아무 정렬 알고리즘으로 list를 정렬 ... return list; } 이와 같이 제너릭과 인터페이스 제약을 함께 사용해 하스켈의 여러 의미 함수에 대응하는 자바 함수를 정의할 수 있는 것은 결코 우연이 아닙니다. 하스켈 타입 클래스 디자인에 핵심적인 역할을 한 필립 와들러(Philip Wadler)라는 유명한 프로그래밍 언어 연구자가 이후 자바 제너릭을 디자인하는 데도 핵심적인 역할을 했기 때문입니다. 또한 참고로 현재 C++ 표준안(C++ 98)에는 Java의 인터페이스 제약 표시처럼 하스켈의 클래스 제약에 해당하는 개념을 직접 표현할 방법이 없었는데, 최근에 디자인된 컨셉(Concept)이라는 기능이 새 C++ 표준의 초안(C++ 0x draft)에 들어갔다고 합니다. 따라서 다음 C++ 표준부터는 어떤 의미에서는 Java보다 하스켈 타입 클래스와 같은 개념을 더 잘 표현할 수 있는 강력한 기능이 추가될 것으로 기대하고 있습니다. 이와 관련해 더 자세히 알아보려면 필립 와들러 본인이 하스켈 타입 클래스와 자바 제너릭에 대해 구글에서 강연한 동영상[14]을 추천합니다. * * * 1. http://research.microsoft.com/~simonpj/papers/history-of-haskell/ ↩︎ 2. http://www.haskell.org/haskellwiki/Haskell_in_industry ↩︎ 3. http://www.haskell.org/haskellwiki/Consultants ↩︎ 4. http://www.pugscode.org/ ↩︎ 5. http://darcs.net/ ↩︎ 6. http://haskell.org/ghc/ ↩︎ 7. http://kldp.org/node/97330 ↩︎ 8. http://www.haskell.org/cabal/ ↩︎ 9. http://hackage.haskell.org/ ↩︎ 10. http://www.realworldhaskell.org ↩︎ 11. 대화식 환경을 해석기라고 부르지 않는 이유는 대화식 환경도 엄밀히 말하자면 번역기이기 때문입니다. 고급 언어를 직접 실행하는 해석기와는 달리 대화식 환경을 통해 불러들인 스크립트는 하스켈과 기계어의 중간 단계인 바이트코드로 번역됩니다. ↩︎ 12. 박준규: 이어지는 예제에서 `getContents`가 아니라 `readContents`를 사용하므로 이 부분은 옮긴이의 실수인 것 같다. ↩︎ 13. 박준규: '추가하면서'의 오기이다. ↩︎ 14. 동영상 http://video.google.com/videoplay?docid=-4167170843018186532 슬라이드 http://homepages.inf.ed.ac.uk/wadler/papers/oopsla/oopsla.pdf ↩︎
010 0
Reposted by 박준규
Jaeyeol Lee @kodingwarrior.hackers.pub.ap.brid.gy · 16/10/2025
한국의 함수형 프로그래밍 컨퍼런스 LiftIO 2025가 슬슬 열린다고 한다. * liftIO 2025 연사 신청 링크: (forms.gle/RsmowMdtNxH1Pchc6) * 컨퍼런스 신청 링크(https://event-us.kr/liftioconf/event/114005)
docs.google.com
liftIO 2025 발표자 신청
liftIO 2025가 곧 찾아옵니다! 행사일시: 2025-12-06 토요일 13:00~17:00 올해는 [Simplicity & Functional Vibe] 를 주제로, 단순성과 함수형 프로그래밍, AI 까지 다양한 주제와 주제간의 연결성을 다루고자 합니다. 발표자 선정은 11월 셋째주에 공지될 예정입니다. 많은 관심과 참여 부탁드립니다.
022 0
박준규 @curry.hackers.pub.ap.brid.gy · 12/10/2025
지인이 오타를 제보해주셨는데 수정이 안 되네… instnace -> instance
000 0
박준규 @curry.hackers.pub.ap.brid.gy · 12/10/2025
하스켈에서 레코드 타입을 json으로 인코딩할 때 타입이 `Maybe`인 필드의 값이 `Nothing`이면 그 타입의 값은 `null`로 변환된다.(`deriving Generic` 했다면) 이때 `null`이 아니라 해당 필드 자체를 포함하지 않게 하려면 다음과 같이 하면 된다. {-# LANGUAGE TemplateHaskell #-} import Data.Aeson.TH data Foo = ... deriveJSON defaultOptions { omitNothingFields = […]
hackers.pub
Original post on hackers.pub
111 0
박준규 @curry.hackers.pub.ap.brid.gy · 12/10/2025
하스켈에서 `Either a b`의 `ToJSON` 인스턴스 구현이 약간 애매하다. json 객체의 키 값으로 `"Left"`나 `"Right"`를 넣는 것 같다. 그런 방식으로 사용하는 사람은 거의 없을 것 같은데... 아무튼 그래서 `newtype`은 쓰지 않고 인스턴스를 오버라이딩 하고 싶은데 대충 찾아보면 인터넷이나 LLM의 첫번째 대답은 `OverlappingInstances` 확장을 쓰라고 안내하는데 이건 deprecated 됐다. 대신 다음과 같은 프라그마(pragma)를 쓰면 된다. * […]
hackers.pub
Original post on hackers.pub
021 0
박준규 @curry.hackers.pub.ap.brid.gy · 11/10/2025
www.linkedin.com/posts/leechanwoo_개…
linkedin.com
Under 6,000은 close 됐고, 이번에는 Upper 6,000입니다. - 함수형 프로그래밍에 목마른 분들은 여기가 최고의 놀이터입니다 - 저희는 회사를 미국에 매각하는 목표가 뚜렷한 팀입니다. Micro-조직에서 Giant-조직까지 Elevation 하는 커리어를 토모로우에서 만들어보세요 - 민첩한 장인정신이 있는 팀입니다. 시장에 빠르게… | Chanwoo Lee
Under 6,000은 close 됐고, 이번에는 Upper 6,000입니다. - 함수형 프로그래밍에 목마른 분들은 여기가 최고의 놀이터입니다 - 저희는 회사를 미국에 매각하는 목표가 뚜렷한 팀입니다. Micro-조직에서 Giant-조직까지 Elevation 하는 커리어를 토모로우에서 만들어보세요 - 민첩한 장인정신이 있는 팀입니다. 시장에 빠르게 반응하면서 고품질의 제품을 시장에 내놓을 수 있는 개발자는 극소수입니다. 특히 창업을 하고싶은 개발자라면 저희 팀에서 신세계를 경험할 수 있으리라 확신합니다. 값어치를 매길 수 없는 유니크하고 값비싼 경험입니다. 무기력하고 무능한 조직에서 벗어나 성장 에너지가 넘치는 정예 팀에서 아주 중요한 role을 맡고 싶으신 분은 많이 지원해주세요.
020 0
박준규 @curry.hackers.pub.ap.brid.gy · 03/10/2025
@hongminhee 축하드립니다!
100 0
Reposted by 박준규
洪 民憙 (Hong Minhee) :nonbinary: @hongminhee.hollo.social.ap.brid.gy · 02/10/2025
早晩間(조만간) 블로그 글로 仔細(자세)히 풀겠지만, Fedify 프로젝트가 STF로부터 投資(투자)를 받게 되어, 제가 앞으로 約(약) 一年(일년) 동안 Fedify 프로젝트에만 專念(전념)할 수 있게 되었습니다.
456 0
박준규 @curry.hackers.pub.ap.brid.gy · 29/09/2025
* 하스켈 리포트: WellTyped에서 작성한 하스켈 생태계 활동 보고서 * 언어 서버: HLS(Haskell Language Server) * 번역 링크: rosettalens.com/s/ko/haskell-ecosys… RE: hackers.pub/@lionhairdino/01998e6c-…
hackers.pub
011 0
박준규 @curry.hackers.pub.ap.brid.gy · 28/09/2025
@hongminhee @2chanhaeng 그걸 배워서 어딘가에 구현하시려고 하는 거군요!
100 0
박준규 @curry.hackers.pub.ap.brid.gy · 27/09/2025
@pbzweihander 안녕하세요! 재밌는 상상이네요! 그때를 대비해서 우리 모두 아마추어 무선 자격증을 땁시다!
010 0
박준규 @curry.hackers.pub.ap.brid.gy · 27/09/2025
@hongminhee macOS에서도 일부러 Bash를 쓰시는 건가요?
100 0
박준규 @curry.hackers.pub.ap.brid.gy · 27/09/2025
@hongminhee 어디서 뭘 배우고 계세요!ㅎㅎ
100 0
박준규 @curry.hackers.pub.ap.brid.gy · 24/09/2025
> `lens` 라이브러리의 타입들은 `optics`(광학기구)라고 불립니다. 여기에는 `Prism`, `Lens`, `Iso`, `Traversal`, `Fold`, `Getter`, `Setter` 같은 것들이 포함됩니다. 이 이름들 중 일부는 빛의 굴절에 대한 말장난에서 비롯되었습니다. 예를 들어, 렌즈(lens)는 망원경이 풍경의 작은 일부를 시야 가득히 채워 보이게 해주는 것이고, 프리즘(prism)은 흰빛을 분리하여 그 구성 색들을 드러내 줍니다. 어느 정도는 은유가 담겨 있지만, 이런 이름들은 깨달음을 주기보다는 […]
hackers.pub
Original post on hackers.pub
032 0
박준규 @curry.hackers.pub.ap.brid.gy · 24/09/2025
@eatch.dev 네?ㅎㅎ
000 0
박준규 @curry.hackers.pub.ap.brid.gy · 24/09/2025
@gnh1201 어떤 서비스를 관제하려고 하시나요?
100 0
박준규 @curry.hackers.pub.ap.brid.gy · 24/09/2025
이거 위험하지 않나... 관리 주체가 어딜까? 경찰이 하나? RE: fedi.computernewb.com/@vncresolver/…
fedi.computernewb.com
000 0
박준규 @curry.hackers.pub.ap.brid.gy · 24/09/2025
hackers.pub
레코드 생성자
* 저자: 가브리엘라 곤잘레스(Gabriella Gonzalez) * 링크: https://www.haskellforall.com/2020/07/record-constructors.html * 저작권: 크리에이티브 커먼즈 저작자표시 4.0 국제 라이선스 이 글은 하스켈 생태계에서 레코드와 관련된 다양한 관용구를 정리한 짧은 문서입니다. 패키지를 처음 사용하는 사람들은 이 글을 통해 실제에서 접하게 되는 레코드 API 관용구를 더 잘 이해할 수 있습니다. 패키지 작성자를 위해, 글의 끝부분에는 제가 개인적으로 선호하는 관용구가 무엇인지 설명하는 간단한 권고도 포함했습니다. ## 예제 이 글에서 계속 사용할 예제로 다음 레코드 타입을 사용하겠습니다. module Example where data Person = Person{ name :: String , admin :: Bool } 패키지 작성자가 레코드 생성자를 내보내는 경우, `Person` 레코드를 생성하는 몇 가지 방법이 있습니다. 가장 간단한 방법은 확장 기능이 필요 없습니다. 모든 필드의 값을 하나의 표현식에서 초기화할 수 있습니다. 예를 들어 다음과 같습니다. example :: Person example = Person{ name = "John Doe", admin = True } 일부 레코드 리터럴은 꽤 커질 수 있기 때문에, 하스켈은 레코드 조립을 돕는 두 가지 확장 기능을 제공합니다. 먼저, `NamedFieldPuns` 확장 기능을 사용하여 다음과 같이 레코드를 작성할 수 있습니다. {-# LANGUAGE NamedFieldPuns #-} example :: Person example = Person{ name, admin } where name = "John Doe" admin = True 이 방식이 가능한 이유는 `NamedFieldPuns` 확장이 `Person { name, admin }`을 `Person { name = name, admin = admin }`으로 변환해주기 때문입니다. `RecordWildCards` 확장은 한 단계 더 나아가, 모든 필드를 다시 명시하지 않고도 레코드 리터럴을 초기화할 수 있게 해줍니다. 예를 들어 다음과 같습니다. {-# LANGUAGE RecordWildCards #-} example :: Person example = Person{..} where name = "John Doe" admin = True 반대로, 레코드 리터럴을 여러 방법으로 구조 분해할 수도 있습니다. 예를 들어, 접근자 함수를 사용하여 레코드 필드에 접근할 수 있습니다. render :: Person -> String render person = name person ++ suffix where suffix = if admin person then " - Admin" else "" ... 또는 레코드 리터럴에 대해 패턴 매칭을 사용할 수도 있습니다. render :: Person -> String render Person{ name = name, admin = admin } = name ++ suffix where suffix = if admin then " - Admin" else "" ... 또는 `NamedFieldPuns` 확장을 사용하여(역방향으로도 작동) 다음과 같이 할 수 있습니다. render :: Person -> String render Person{ name, admin } = name ++ suffix where suffix = if admin then " - Admin" else "" ... 또는 `RecordWildCards` 확장을 사용하여(역방향으로도 작동) 다음과 같이 할 수 있습니다. render :: Person -> String render Person{..} = name ++ suffix where suffix = if admin then " - Admin" else "" 또한, `RecordDotSyntax` 확장이 사용 가능해지면 일반 점 문법을 사용하여 레코드 필드에 접근할 수 있습니다. render :: Person -> String render person = person.name ++ suffix where suffix = if person.admin then " - Admin" else "" ## 불투명(opaque) 레코드 타입 일부 하스켈 패키지는 레코드 생성자를 내보내지 않기로 선택합니다. 이러한 경우, 대신 모든 필수 필드를 초기화하고 나머지 필드는 기본값으로 설정하는 함수를 제공합니다. 예를 들어, `Person` 타입에서 `name` 필드는 필수이고, `admin` 필드는 선택적이며 기본값이 `False`라고 가정해봅시다. 이 경우 API는 다음과 같이 생겼을 수 있습니다. module Example ( Person(name, admin) , makePerson ) where data Person = Person{ name :: String, admin :: Bool } makePerson :: String -> Person makePerson name = Person{ name = name, admin = False } 주의할 점은, 이 모듈이 `Person` 타입과 모든 필드를 내보내지만 `Person` 생성자는 내보내지 않는다는 것입니다. 따라서 사용자가 `Person` 레코드를 생성할 수 있는 유일한 방법은 `makePerson` “스마트 생성자”를 사용하는 것입니다. 일반적인 관용구는 다음과 같습니다. example :: Person example = (makePerson "John Doe"){ admin = True } 다시 말해, 사용자는 필수 필드를 “스마트 생성자”를 통해 초기화하고, 나머지 선택적 필드는 레코드 문법을 사용하여 설정해야 합니다. 이는 생성자가 내보내지지 않았더라도, 내보낸 필드를 사용하여 레코드 타입을 업데이트할 수 있기 때문에 가능합니다. `wai` 패키지는 이 관용구를 따르는 대표적인 패키지 중 하나입니다. 예를 들어, `Request` 레코드는 불투명하지만 접근자는 여전히 내보내지므로, `defaultRequest`를 생성한 뒤 레코드 문법을 사용하여 `Request`를 업데이트할 수 있습니다. example :: Request example = defaultRequest{ requestMethod = "GET", isSecure = True } ... 그리고 내보낸 접근자 함수를 사용하여 필드에 여전히 접근할 수 있습니다. requestMethod example 이 방법은 레코드 조립 시 `NamedFieldPuns`와 함께 사용할 수도 있습니다.(구조 분해에는 적용되지 않음) 따라서 다음과 같이 작성할 수 있습니다. example :: Request example = defaultRequest{ requestMethod, isSecure } where requestMethod = "GET" isSecure = True 하지만, 이 방법은 `RecordWildCards` 언어 확장과는 함께 사용할 수 없습니다. 일부 다른 패키지는 한 걸음 더 나아가, 접근자를 내보내는 대신 접근자 필드에 대한 렌즈를 내보냅니다. 예를 들어, `amazonka-*` 계열 패키지가 이러한 방식을 사용하며, 이에 따른 레코드 생성 코드는 다음과 같이 작성됩니다. example :: PutObject example = putObject "my-example-bucket" "some-key" "some-body" & poContentLength .~ Just 9 & poStorageClass .~ ReducedRedundancy ... 그리고 필드에는 렌즈를 사용하여 접근할 수 있습니다. view poContentLength example ## 제 권고 저는 패키지 작성자가 스마트 생성자 대신 레코드 생성자를 내보내는 것을 선호해야 한다고 생각합니다. 특히, 스마트 생성자 관용구는 레코드를 생성하기 위해 지나치게 많은 특화된 언어 지식을 요구하는데, 레코드 생성은 함수형 프로그래밍 언어에서 입문자가 수행해야 하는 기본적인 작업이어야 합니다. 패키지 작성자들은 일반적으로 스마트 생성자를 사용하면 새로 추가된 기본값 필드를 하위 호환 방식으로 제공할 수 있기 때문에 API 안정성을 높일 수 있다고 주장합니다. 하지만 저는 개인적으로 이러한 안정성을 크게 중요하게 여기지 않습니다.(패키지 작성자이자 사용자 모두로서) 그 이유는 하스켈이 정적 타입 언어이기 때문에, 타입 체커의 도움으로 이러한 변경을 역의존에서도 쉽게 처리할 수 있기 때문입니다. 저는 새로운 기여자들의 경험을 개선하는 데 더 큰 가치를 둡니다. 그래야 하스켈 프로젝트가 다양한 언어가 공존하는 조직 내에서도 더 쉽게 자리 잡을 수 있습니다. 다른 팀이 하스켈 코드에 자신 있게 기여할 수 있다고 느낄 때, 경영진은 조직 내에서 하스켈 프로젝트를 수용하는 데 덜 주저하게 됩니다. ## 미래 방향 장기적인 해결책으로, 언어가 기본값을 가지는 필드를 일급으로 지원한다면 두 가지 장점을 모두 누릴 수 있을 것입니다. 즉, 다음과 같이 레코드 타입을 정의할 수 있는 방법입니다. data Person = Person{ name :: String , admin :: Bool = False } ... 그러면 레코드를 초기화할 때 기본값 필드는 안전하게 생략할 수 있게 됩니다. 물론, 이러한 변경이 가져올 영향에 대해서는 아직 충분히 검토하지 않았습니다.
021 0
박준규 @curry.hackers.pub.ap.brid.gy · 23/09/2025
> 너무 이른 시점에 일반화를 걱정하는 것은 위험할 수 있습니다. 자신이 코드를 작성하든 다른 사람의 코드를 이해하려 하든, 우리는 이 책에서 취한 접근 방식을 따를 것을 권장합니다. 즉, 일반적인 것부터 시작하지 말고 구체적인 예시부터 시작하세요. 그런 다음 그들이 어떤 측면을 공유하는지 관찰하세요. 학습자로서 이러한 순서는 더 쉬운 구체적인 예시를 토대로 더 어려운 추상적 개념을 이해할 기회를 더 많이 제공합니다. 저자로서 이러한 순서는 더 추상적인 발상이 타당성을 갖고 목적을 지닐 수 있도록 도와줍니다. — […]
hackers.pub
Original post on hackers.pub
043 0
박준규 @curry.hackers.pub.ap.brid.gy · 22/09/2025
rosettalens.com/s/ko/the-golden-rul…
rosettalens.com
소프트웨어 품질의 황금률 - RosettaLens 번역
소프트웨어 품질에 관한 ‘황금률’—문제를 다운스트림에서 우회하기보다 업스트림으로 수정을 올리기—을 제시하고, 서드파티 의존성, 타입 설계, 사회적 분열의 사례로 그 함의를 설명합니다.
032 0
박준규 @curry.hackers.pub.ap.brid.gy · 22/09/2025
rosettalens.com/s/ko/ewd667
rosettalens.com
"자연어 프로그래밍"의 어리석음에 대하여 (EWD 667) - RosettaLens 번역
자연어로 컴퓨터를 프로그래밍하자는 발상의 문제점을 지적하며, 형식적 기호 체계의 장점과 역사적 교훈을 통해 자연어 인터페이스의 한계를 논한다.
000 0
박준규 @curry.hackers.pub.ap.brid.gy · 22/09/2025
rosettalens.com/s/ko/the-end-of-his…
rosettalens.com
프로그래밍의 ‘역사의 종말’ - RosettaLens 번역
프로그래밍 패러다임의 종착지를 수학적 DSL과 런타임의 분리로 전망하고, 사용자 영역의 다양한 관심사가 런타임(혹은 타입 검사)으로 이전되는 흐름과 그 파급효과를 논한다.
000 0
박준규 @curry.hackers.pub.ap.brid.gy · 22/09/2025
hackers.pub
하스켈을 잘 모르는 프로그래머도 이해하기 쉬운 하스켈 코드 작성법
* 저자: 가브리엘라 곤잘레스(Gabriella Gonzalez) * 링크: https://www.haskellforall.com/2015/09/how-to-make-your-haskell-code-more.html * 라이선스: Creative Commons Attribution 4.0 International License 이 글에서는 제가 겪은 경험을 바탕으로 하스켈 코드의 가독성을 높이는 몇 가지 팁을 정리합니다. 각 지침에는 그에 해당하는 이유도 함께 제시됩니다. 이 글이 모든 하스켈 코드를 반드시 이렇게 작성해야 한다는 의미로 받아들이지는 마세요. 여기서 제시하는 지침들은, 생소한 문법 때문에 사람들을 겁주지 않고 언어를 소개하기 위해 예시용으로 작성하는 코드에 적용할 수 있는 가이드라인입니다. ## 규칙 1: ($)를 사용하지 마세요 이 지침은 아마도 가장 논란이 될 만한 내용이지만, 저는 가독성 향상에 가장 큰 영향을 주는 권장 사항이라고 생각합니다. 이 문제의 전형적인 예시는 다음과 같습니다. print $ even $ 4 * 2 ... 이는 다음 코드와 동일합니다. print (even (4 * 2)) 달러 기호(`$`)의 가장 큰 문제는 대부분의 사람들이 이를 연산자로 인식하지 못한다는 점입니다! 다른 어떤 언어에서도 달러 기호를 연산자로 사용하는 관례가 없기 때문입니다. 실제로 대부분의 개발자는 자바스크립트, 자바, C++, 파이썬처럼 새로운 연산자를 추가할 수 없는 언어로 프로그래밍을 하기 때문에, 달러 기호가 연산자라는 사실을 바로 이해할 것이라고 기대하는 것은 합리적이지 않습니다. 이로 인해 사람들은 달러 기호가 일종의 내장 문법이라고 오해하게 되고, 결과적으로 하스켈의 문법이 불필요하게 복잡하며 가독성보다는 간결함에 최적화되어 있다고 느끼게 됩니다. 이러한 인식은 하스켈 외에서 달러 기호가 가장 많이 사용되는 곳이 펄(Perl)이라는 점 때문에 더욱 강화됩니다. 펄은 흔히 ‘작성만 가능하고 읽기 어렵다(write-only)’고 악명 높은 언어입니다. 설사 그들이 달러 기호가 연산자를 나타낸다는 것을 인식한다고 하더라도, 그 연산자가 무엇을 의미하는지는 쉽게 짐작할 수 없습니다. 통화 기호로 사용되는 기호와 함수 적용 사이에는 명확한 직관적 연결이 없기 때문입니다. 또한 하스켈 언어 외부에서는 이러한 연결을 참고할 만한 전례도 없습니다. 설사 하스켈을 처음 접하는 사람이 운 좋게도 달러 기호가 함수 적용을 나타낸다는 것을 짐작한다고 해도, 이 기호가 왼쪽 결합인지 오른쪽 결합인지 알 수 없기 때문에 여전히 모호합니다. 하스켈을 좀 더 깊이 공부하는 사람조차도 `$`의 동작 방식을 이해하는 데 어려움을 겪고, 종종 합성 연산자 `.`와 혼동하곤 합니다. 정성껏 언어를 배우는 사람조차 `$`를 이해하기 어렵다면, 회의적인 사람들은 도대체 얼마나 이해할 수 있을까요? 이 시점에서 이미 잠재적으로 언어에 관심을 가질 수 있었던 많은 사람들을 잃게 됩니다. 그런데도 달러 기호를 사용해야 할 이유가 무엇일까요? 달러 기호는 표현식을 더 짧게 만들어주지도 않습니다. ## 규칙 2: 연산자는 적절히 사용하세요 규칙 1은 규칙 2의 특수한 경우라고 볼 수 있습니다. 제가 연산자 사용에 대해 대략적으로 제시하는 지침은, 결합 가능한 연산자는 괜찮고, 그 외의 연산자는 피하라입니다. 괜찮은 연산자는 다음과 같습니다. * `(.)` * `(+)` / `(*)` * `(&&)` / `(||)` * `(++)` 피해야 할 연산자는 다음과 같습니다. * `(<$>)` / `(<*>)` - 앞으로는 `liftA{n}` 또는 `ApplicativeDo`를 사용 * `(^.)` / `(^..)` / `%~` / `.~` - `view` / `toListOf` / `over` / `set`을 대신 사용 제가 제시한 특정 연산자를 반드시 따라야 하는 것은 아닙니다. 중요한 점은 하스켈을 가르칠 때 연산자를 보다 절제해서 사용하는 것입니다. 연산자와 관련된 문제는 본질적으로 달러 기호 문제와 매우 유사합니다. * 일부 사람들에게 연산자로 인식되지 않을 수 있습니다. 특히 다른 언어에 대응되는 연산자가 없다면 더욱 그렇습니다. * 연산자의 의미가 즉시 명확하지 않습니다. * 우선순위와 결합 방식이 명확하지 않습니다. 특히 하스켈 전용 연산자의 경우 더욱 그렇습니다. 제가 결합 가능한 연산자를 약간 더 선호하는 주된 이유는, 이러한 연산자는 결합 방식이 중요하지 않고, 일반적으로 수학에서 널리 쓰이는 연산자로서 다른 언어에서도 전례가 있기 때문입니다. ## 규칙 3: do 표기법을 적극적으로 사용하세요 가능하다면 `(>>=)`나 `fmap`보다 `do` 표기법을 사용하는 것을 권장합니다. 코드가 몇 줄 더 길어지더라도 괜찮습니다. 사람들은 장황하다는 이유로 언어를 거부하지 않습니다.(Java와 Go가 그 증거입니다.) 하지만 생소한 연산자나 함수 때문에 언어를 거부할 수는 있습니다. 즉, 다음과 같이 작성하는 대신에 example = getLine >>= putStrLn . (++ "!") 다음과 같이 작성하는 것이 좋습니다. example = do str <- getLine putStrLn (str ++ "!") 정말 한 줄로 작성하고 싶다면, 세미콜론을 사용해 `do` 표기법을 그대로 활용할 수 있습니다. example = do str <- getLine; putStrLn (str ++ "!") `do` 표기법과 세미콜론은 외부인에게 즉시 인식됩니다. 이는 서브루틴 문법과 유사하기 때문이며, 가장 흔한 경우인 IO에서는 실제로 서브루틴 문법과 동일합니다. 이와 관련된 추가 권장 사항으로, 최근 GHC 메인라인에 통합되어 다음 GHC 버전에서 사용 가능하게 된 `ApplicativeDo` 확장을 사용하는 것입니다. 저는 `ApplicativeDo`가 `<$>`와 `<*>` 연산자보다 외부인에게 더 읽기 쉬울 것이라고 생각합니다. ## 규칙 4: 렌즈를 사용하지 마세요 오해하지 마세요. 저는 렌즈의 가장 큰 지지자 중 한 명이며, 렌즈가 하스켈의 주류 관용구로 확고히 자리 잡아야 한다고 생각합니다. 하지만 초보자에게는 적절하지 않다고 느낍니다. 가장 큰 문제점은 다음과 같습니다. * 초보자에게 렌즈의 동작 방식을 설명하기 어렵습니다. * Template Haskell이나 반복적인 렌즈 정의가 필요합니다. * 함수 접근자와 렌즈 각각에 별도의 이름을 지정해야 하며, 그 중 하나는 보기 좋지 않게 됩니다. * `lens-family-core`의 보다 단형 버전을 사용하더라도 추론된 타입과 오류 메시지가 불명확해집니다. 렌즈는 훌륭하지만, 이를 가르칠 급한 필요는 없습니다. 렌즈를 언급하기도 전에 하스켈 언어에는 이미 배울 만한 독특하고 놀라운 요소가 충분히 많이 있습니다. ## 규칙 5: where와 let을 적극적으로 사용하세요 여러 줄에 걸친 하나의 거대한 표현식을 작성하고 싶은 유혹을 참으세요. 대신, `where`나 `let`을 사용하여 각각의 하위 표현식을 별도의 줄에 정의하면서 코드를 나누는 것이 좋습니다. 이 규칙은 주로 명령형 프로그래머가 함수형 프로그래밍에 쉽게 적응하도록 돕기 위해 존재합니다. 이러한 프로그래머들은 코드를 읽을 때 문장 경계와 같은 시각적 “구두점”에 익숙합니다. `let`과 `where`는 실제로는 하위 표현식일 뿐이지만, 시각적으로 큰 프로그램을 더 작은 “문장”으로 나누는 것처럼 보여 줍니다. ## 규칙 6: 포인트 프리 스타일은 적절히 사용하세요 모든 하스켈 프로그래머는 변수 이름을 모두 없앨 수 있는지 시도하는 시기를 겪습니다. 스포일러: 항상 없앨 수는 있지만, 이렇게 하면 코드가 지나치게 간결해져서 읽기 어려워집니다. 예를 들어, 조금 생각하고 종이와 펜을 사용하지 않고서는 다음 코드가 무슨 의미인지 도저히 알 수 없습니다. ((==) <*>) 하지만 다음과 같은 동등한 표현식은 한눈에 이해할 수 있습니다. \f x -> x == f x 참고로, 이것은 실제 예제입니다. 포인트 프리 스타일을 어디까지 사용할지는 엄격한 규칙이 있는 것은 아니지만, 고민될 때는 덜 포인트 프리하게 작성하는 편이 안전합니다. ## 결론 이상입니다! 이 여섯 가지 간단한 규칙만 지켜도 하스켈 코드를 외부인에게 훨씬 읽기 쉽게 만들 수 있습니다. 하스켈은 사실, 지배적인 관용구에 익숙해지면 매우 읽기 쉬운 언어입니다. 그 이유는 다음과 같습니다. * 순수성 * 명확하게 정의된 함수형 패러다임 * 불필요한 부작용과 상태의 최소화 하지만, 하스켈을 전혀 접해보지 않은 완전한 외부인조차도 코드를 읽기 쉽도록 특별히 신경 써야 합니다. 진입 장벽은 이 언어에 대한 가장 흔한 비판 중 하나이며, 저는 간단하고 깔끔한 코딩 스타일이 그 장벽을 낮출 수 있다고 믿습니다.
043 0