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 · 05/02/2026
이때 막 프라그마 써가면서 Either의 ToJSON 인스턴스를 원하는대로 구현하려고 애썼는데 알고 보니까 더 쉬운 다른 방법이 있었다. 다음과 같이 sumEncoding이라는 옵션을 이용하면 된다. toJSON = genericToJSON defaultOptions { sumEncoding = UntaggedValue } 전에는 이렇게 좋아하는 언어의 소소한 팁을 라이브러리 문서에서 찾았을 때 SNS에 공유하는 게 삶의 낙이었는데 요즘 같은 LLM 에이전트 딸깍 시대에는 이런 게 다 무슨 소용인가 […]
hackers.pub
Original post on hackers.pub
111 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
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
하스켈에서 레코드 타입을 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
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 · 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
이거 위험하지 않나... 관리 주체가 어딜까? 경찰이 하나? 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
박준규 @curry.hackers.pub.ap.brid.gy · 19/09/2025
rosettalens.com/s/ko/monads
rosettalens.com
하스켈 코드 없이 모나드를 평이한 말로 설명 - RosettaLens 번역
JS의 Promise, 파서 콤비네이터, Optional/Result, CPS, LINQ 같은 익숙한 체이닝 직관을 통해 모나드가 무엇인지, 왜 하스켈에서 특히 유용한지 간단히 풀어 설명한다.
010 0
Reposted by 박준규
洪 民憙 (Hong Minhee) @hongminhee.hackers.pub.ap.brid.gy · 19/09/2025
hackers.pub
내가 LLM과 함께 코딩하는 방식
요즘 많은 사람들이 그러하듯이 나 역시 최근에는 LLM과 함께 코딩하는 일이 많아졌다. 내가 LLM과 함께 코딩한다고 말하면 의외라는 듯이 반응하는 사람들도 있고, 어떤 식으로 LLM을 활용하냐는 질문도 종종 받는다. 그래서 생각난 김에 내가 LLM을 코딩에 어떻게 활용하는지를 대략적으로 적어보고자 한다. ## 전제 당연하지만 내가 LLM을 활용하는 방식은 내가 주로 다루는 종류의 작업에 맞춰져 있다. 따라서 일반적인 다른 코딩에는 잘 맞지 않을 수도 있다. 내가 주로 다루는 작업이란 협업자들이나 소비자들과 주로 서면을 통해 비동기로 의사 소통을 하는 오픈 소스 프로젝트이며, 그것도 주로 애플리케이션 개발이 아닌 라이브러리 개발이다. 주로 사용하는 프로그래밍 언어는 TypeScript로 상당히 LLM이 잘 다루는 축에 속한다. 한편으로는 비교적 기존 지식의 도태가 빠르게 이뤄지는 생태계이기도 해서, 어떤 면에서는 불리한 점도 있다고 볼 수 있다. 어찌 되었든 내 LLM 활용 방식은 내가 주로 다루는 종류의 작업에 맞춰져 있으나, 그럼에도 이 글에서는 일반적인 코딩에 두루 적용할 수 있는 팁을 공유하려고 노력했다. ## 맥락이 왕이다 LLM 활용에 있어서 모델 자체의 성능 등 여러 고려 사항들이 있지만, 사람들이 LLM을 사용하는 것을 관찰했을 때 가장 흔히 놓치는 것이 충분한 맥락의 제공이다. 사람들은 자신이 무언가를 판단할 때 얼마나 많은 맥락에 의존하는지 의식하지 못한다. 머릿속 사소한 기억 조각부터 내가 접근 가능한 최신 문서들, 이슈에 담긴 캡처 이미지까지… 이들 대부분은 LLM이 기본적으로는 혼자서 접근할 수 없는 경우가 많다. 제아무리 LLM이 똑똑하다고 해도 필요한 맥락이 주어지지 않으면 엉뚱한 결과를 내놓기 마련이다. 주변에서 「LLM은 코딩을 너무 못한다」고 토로하는 경우를 보면 정말로 LLM이 풀기 어려운 문제를 주로 다루는 분들도 계셨지만, 대부분의 경우에는 LLM에게 충분히 맥락을 제공하지 못해서 그럴 때가 많았다. 아마도 이 글에서 다룰 대부분의 이야기는 결국 「어떻게 LLM에게 맥락을 잘 제공할까?」라는 고민에서 나온 팁들이라고 봐도 무방하다. ## 내가 쓰는 모델과 코딩 에이전트 2025년 9월 현재, 나는 거의 대부분의 작업에 Claude Code를 사용한다. 지난 몇 달 간 Claude Code를 주로 사용해 왔으며, 주기적으로 다른 도구들도 시도해 보고 있으나 여전히 Claude Code가 나에게 가장 맞다고 판단했다. 실은, 나는 대부분의 프로그래머에게 Claude Code가 가장 적합할 거라고 생각한다. 엄밀한 벤치마크에 근거한 것은 아니고, 체감일 따름이지만… 이유는 다음과 같다: * 다른 모델들은 도구 호출을 잘 못한다. 도구를 제공하면 필요한 순간에 도구를 활용할 수 있어야 한다. Claude 모델들은 이 부분에서 확실히 뛰어나다. 도구 호출은 풍부한 맥락을 제공하기 위해서는 반드시 필요하기 때문에, 도구 호출의 성능이 떨어지면 결과적으로 LLM을 동반한 코딩 자체의 성과가 떨어진다. * 다른 모델들은 질답이 길어질 때 성능이 떨어진다. 적어도 나는 LLM과 코딩할 때 한 방(one-shot)에 결과를 내놓는 방식, 즉 바이브 코딩(vibe coding)을 선호하지 않는다. 따라서 모델의 멀티턴(multi-turn) 성능이 중요한데, 다른 모델들은 질답이 길어짐에 따라 앞서 했던 이야기를 망각하는 경향이 있다. * 똑같이 Claude의 모델을 사용하더라도 Claude Code 자체의 성능이 다른 LLM 코딩 에이전트에 비해 뛰어나다. 이건 파인 튜닝이나 시스템 프롬프트 등에 비법 소스가 있기 때문이라고 여겨진다. 이 때문에 Claude Code를 Claude가 아닌 모델과 함께 쓸 수 있게 해주는 비공식 프록시 등도 존재한다. 물론, Claude 및 Claude Code에도 단점이 있다: * 상대적으로 문맥 윈도(context window)가 짧다. 그래서 토큰을 아껴야 한다. Claude Code의 대화 압축(conversation compaction)은 그럭저럭 잘 동작하는 편이긴 해도, 여전히 스트레스이긴 하다. (이를테면, 대화 압축은 영어로 이뤄지기 때문에 후속 세션에서는 갑자기 영어로 대답하기 시작한다. 아, 나는 프롬프트를 한국어로 적는다.) * 일부 LLM 코딩 에이전트들이 제공하는 LSP 지원이 아직 없다. 따라서 타입 오류나 린트 오류 등을 명령어 실행 등을 통해 따로 보여줄 수 있어야 한다. 대신 Claude Code에서는 훅(hooks) 기능이 제공되므로, 이를 잘 활용하면 어느 정도는 비슷한 효과를 볼 수 있다. 하지만 장점이 단점을 압도하기 때문에 앞으로도 상황에 큰 변화가 없다면 Claude Code를 주로 쓸 것 같다. ## 세부 지시는 서면으로 지시(prompt)는 세부적일 수록 좋다. 지시가 한 문장으로 끝난다면 좋은 지시가 아닐 가능성이 높다. 때로는 지시를 만드는 지시가 필요하기도 하다. 나의 지시 방식은 다음과 같다. 우선 대부분의 지시는 GitHub 이슈에 작성한다. 필요한 충분한 링크를 제공해야 하고, 가급적이면 캡처 이미지나 도표와 같은 시각적 정보에는 크게 의존하지 않아야 한다. 물론, 이슈는 기본적으로 LLM을 위한 것이 아니라 사람을 위한 것이므로 LLM에게만 필요한 정보는 이슈에 담고 싶지 않을 수 있다. 그런 건 이슈에 담지 않아도 된다.[1] 이미 남이 만든 이슈가 있다면 그 이슈를 활용해도 된다. 남이 만든 이슈에 맥락이 충분하지 않다고 여겨지면, 댓글로 맥락을 보충한다. 때로는 이슈 자체도 LLM으로 작성하기도 한다. 관련 문서나 상황을 충분히 공유하여 이슈를 작성해 달라고 하는 식이다. 예를 들면, 다음은 내가 만든 이메일 전송 라이브러리인 Upyo에 Plunk 트랜스포트를 추가하는 이슈 #11 _Plunk transport_를 작성하기 위해 사용했던 지시문이다: > Plunk라는 이메일 전송 프로바이더가 있습니다. Plunk의 트랜스포트를 Upyo에 추가하면 좋을 것 같은데요. 이슈 트래커에 일단 이슈를 먼저 만들고자 합니다. 이슈 제목과 내용을 영어로 작성해 주실 수 있을까요? 너무 문제 정의 및 해결책 제시가 구분되는 식의 형식적인 글 대신, 좀 더 사람이 쓴 것 같은 자연스러운 톤으로 부탁드립니다. 너무 길 필요도 없고요. 한두 문단 정도면 충분할 것 같습니다. > > 참고 링크: > > * https://www.useplunk.com/ > * https://docs.useplunk.com/getting-started/overview > * https://docs.useplunk.com/api-reference/transactional/send > 단, 이 때 나는 Claude의 프로젝트 기능을 이용해서 Upyo의 기존 문서를 RAG로 제공한 상태에서 지시를 했다는 점을 밝힌다. 그 다음에 Claude Code의 계획 모드(plan mode)[2]에서 다음과 같이 지시한다. > https://github.com/dahlia/upyo/issues/11 이슈를 구현해야 합니다. 이슈 본문과 본문에서 링크된 관련 링크들을 모두 살펴본 뒤, Upyo 프로젝트에 Plunk 트랜스포트를 추가할 구현 계획을 세부적으로 세워주세요. 정확히는, 나는 이슈 링크를 제공하는 대신 이슈 번호만 제공하고 GitHub MCP를 이용해서 이슈를 직접 읽도록 하는 걸 선호한다. HTML이 아니라 Markdown 형식으로 이슈를 읽기 때문에 본문에 걸린 링크 등을 더 잘 따라가기 때문이다. 링크가 정말 중요한 경우 Claude Code의 지시에서 한 번 더 적기도 한다. 이슈에 미처 적지 못했던 LLM만을 위한 정보도 이 때 모두 적는다. 구현할 때 살펴봐야 할 소스 파일이 무엇인지 잘 알고 있다면, 그런 정보도 함께 제공하면 더 좋다. LLM이 코드베이스를 탐색하느라 삽질을 훨씬 덜 하고, 토큰도 덜 쓰기 때문이다. 나는 GitHub 이슈를 세부 지시를 적는 용도로 썼지만, _PLAN.md_ 같은 문서 파일을 만들어서 거기다 적는 방법도 많이 쓰인다고 알고 있다. ## 설계는 사람이, 구현은 LLM이 내가 LLM을 코딩에 활용할 때의 기본적인 원칙은 큰 설계는 내 스스로 하고, 세부적인 구현은 LLM에게 맡긴다는 것이다. 지시할 때는 설계 의도를 정확히 제시하고, 구현 과정에서 실수할 수 있을 것 같은 우려점에 대해서 충분히 짚고 넘어간다. 특히, 나는 프로젝트를 처음 시작할 때 디렉터리나 패키지 구조를 여전히 직접 손으로 할 때가 많다. (하지만 이 부분은 내가 원체 LLM 시대 이전부터 쿠키커터 류의 프로젝트 템플릿도 좋아하지 않았기 때문일 수도 있다. 템플릿을 쓰든 LLM을 쓰든 내 마음에 들게 나오지 않기에.) LLM은 자신이 가장 익숙한 기술로 문제를 해결하려는 경향이 있기 때문에, 기술 선택에 있어서도 명시적으로 지시를 하는 것이 좋다. 아직은 사소한 라이브러리 하나조차도 사람이 검토할 필요가 있다. LLM에게 모든 것을 맡기다 보면 보안 패치도 안 된 옛날 버전을 가져다 쓰거나 하는 일이 흔하기 때문이다. 나 같은 경우에는 후술할 _AGENTS.md_ 문서에서 라이브러리를 설치하기 전에는 `npm view` 명령어를 통해 해당 패키지의 최신 버전이 무엇인지 먼저 확인하라는 지시를 포함시키기도 한다. 추상화를 할 때도, 적어도 API 설계는 여전히 내가 직접할 때가 많다. 어째서일까, LLM이 설계한 API는 아직은 별로일 때가 많다. 내가 받은 인상은, LLM은 구현해 나가며 필요할 때 API를 즉석(ad-hoc)에서 설계할 때가 잦다는 것이다. 물론, 사람이라도 이런 방식을 선호할 수 있는데, 그런 경우에는 LLM이 설계한 API에 불만이 없을지도 모른다. 하지만 적어도 내 경우에는 불만족스러울 때가 많다. 결국에는 LLM을 만능 노예가 아니라 똑똑하기도 하지만 미숙한 점도 많은 동료로 보고 LLM이 서툰 부분에는 최대한 사람이 도와서 일을 해낸다는 관점이 필요한 것 같다. ## _AGENTS.md_ 대부분의 LLM 코딩 에이전트들은 _AGENTS.md_ 내지는 그에 준하는 기능을 제공하고 있다. 예를 들어 Claude Code는 _CLAUDE.md_ 파일을 바라보며, Gemini CLI는 _GEMINI.md_ 를 바라보는 식인데, 점차 _AGENTS.md_ 파일로 표준화되고 있는 추세이다. 나는 특정 벤더에서만 쓰는 파일들을 모조리 _AGENTS.md_ 로 심볼릭 링크를 건 다음 _AGENTS.md_ 파일만을 정본으로 삼게 하고 있다. 이렇게 하면 나와 다른 LLM 코딩 에이전트를 사용하는 협업자들과 같은 지침을 공유할 수 있다. 아무튼, _AGENTS.md_ 문서의 역할은 간단하다. 이 프로젝트에 대한 지침, 즉 시스템 프롬프트이다. 대부분의 LLM 코딩 에이전트들은 이 파일을 자동으로 생성하는 기능을 제공하는데, 안 쓸 이유는 없다. 자동으로 생성하게 한 후, 잘못된 부분만 고쳐서 써도 된다. 그보다 더 중요한 건 시간이 흐르면서 _AGENTS.md_ 문서가 낡게 되는 것을 피하는 것이다. _AGENTS.md_ 문서는 꾸준히 갈고 닦아야 한다. 특히, 대규모 리팩터링이 있거나 한 뒤에는 반드시 _AGENTS.md_ 문서를 갱신해야 한다. 이 지시 자체도 _AGENTS.md_ 문서에 넣어두는 게 좋다. 그러면 _AGENTS.md_ 문서에는 어떤 내용을 넣을까? 나의 경우에는 다음과 같은 내용을 넣는다: * 프로젝트의 목표와 개요. 저장소 URL을 적어두는 것도 의외로 GitHub MCP 등을 활용할 때 쓸모가 있다. * 프로젝트가 사용하는 개발 도구. 예를 들어 `npm`은 절대 쓰지 않으며 `pnpm`만 쓴다는 식의 지시를 포함한다. * 디렉터리 구조와 각 디렉터리의 역할. * 빌드 및 테스트 방법. 특히, 그 프로젝트만의 특수한 절차가 있다면 반드시 기술한다. 예를 들어, 빌드나 테스트 전에 반드시 코드 생성을 해야 한다면, 이에 관해 적어야 한다—물론, 가장 좋은 것은 빌드 스크립트로 그러한 절차를 자동화하는 것이다. 그 편이 사람에게도 좋고, 토큰을 아끼는 데에도 좋다. * 코딩 스타일이나 문서 스타일. 포매터를 쓰는 방법을 적는 것도 좋다. * 개발 방법론. 이를테면 버그를 고칠 때는 회귀 테스트를 먼저 작성하고, 테스트가 실패하는 것으로 버그가 재현되는 것을 확인한 다음에 버그 수정을 하라는 지침 같은 것들. 일단은 위와 같이 시작하고, LLM 코딩 에이전트를 활용하면서 눈에 밟히는 실수들을 LLM이 할 때마다 지침을 추가하는 것을 권한다. 예를 들어, 나는 TypeScript 프로젝트에서 `any` 타입이나 `as` 키워드를 피하라는 지침을 추가하는 편이다. 다음은 내가 관리하는 프로젝트들의 _AGENTS.md_ 문서들이다: * Fedify의 _AGENTS.md_ 문서 * Upyo의 _AGENTS.md_ 문서 * Optique의 _AGENTS.md_ 문서 ## 문서 제공 LLM에 지식 컷오프가 있다는 건 잘 알려져 있다. 방금 갓 나온 모델이 아닌 한, 내가 쓰는 라이브러리나 런타임 등의 API, CLI 도구 등에 대해 다소 낡은 지식을 가지고 있을 가능성이 높다는 뜻이다. 게다가 만약 비주류 프로그래밍 언어나 프레임워크 등을 쓰고 있다면 이 문제는 더욱 커진다. 따라서 내가 사용하는 프로그래밍 언어나 프레임워크 등에 대한 지식을 제공해야 하는데, 가장 쉽고 효율적인 방법은 Context7을 MCP로 붙이는 것이다. Context7은 다양한 기술 문서를 비교적 최신판으로 유지하면서 벡터 데이터베이스에 색인하고, LLM이 요청할 경우 관련된 문서 조각을 제공하는 RAG 서비스이다. 새로운 문서를 추가하는 것도 쉬워서, 만약 내가 필요한 문서가 등록되어 있지 않다면 얼마든지 새로 추가해서 사용할 수도 있다. 다만, 특별히 지시하지 않는 한 Context7을 따로 활용하지 않을 때도 있기 때문에, 「Context7 MCP를 통해 관련 문서를 확인해 보라」는 식의 지시가 필요할 수 있다. RFC 같은 기술 명세 문서를 제공할 때는 평문(plain text) 형식이 제공되므로 평문 문서의 링크를 제공하는 게 좋다. 나의 경우 연합우주(fediverse) 관련 개발을 많이 하다 보니 FEP 문서를 제공해야 할 일이 많은데, 이 때도 HTML으로 렌더링 된 웹 페이지가 아닌 Markdown 소스 파일의 링크를 직접 제공하는 식으로 사용하고 있다. 이 외에도 웹사이트에서 _/llms.txt_ 및 _/llms-full.txt_ 파일을 제공하는 관행이 퍼지고 있으므로, 이를 활용하는 것도 좋다. (내가 만든 소프트웨어 라이브러리들의 경우 프로젝트 웹사이트에서 모두 _/llms.txt_ 및 _/llms-full.txt_ 파일을 제공하고 있다.) 다만 아무리 LLM에게 친화적인 평문이라고 해도 기술 문서 전체를 다 제공하는 건 아무래도 토큰 낭비가 심하기 때문에, Context7을 쓸 수 있다면 Context7을 쓰는 것을 추천한다. ## 계획 모드의 활용 Claude Code를 포함해 최근의 많은 LLM 코딩 에이전트는 계획 모드를 제공한다. 냅다 구현하는 것을 방지하고, 구현 계획을 LLM 스스로 세우게 한 뒤 사람이 먼저 검토할 수 있게 하는 것이다. 특히, 나는 계획 모드에서는 비싼 Claude Opus 4.1을 사용하고 실제 구현에서는 비교적 저렴한 Claude Sonnet 4를 사용하는 “Opus Plan Mode”를 사용하고 있다.[3] 나는 계획을 면밀히 검토하고 조금이라도 내 성에 차지 않으면 얼마든지 계획 수정을 요구한다. 작업에 따라 다르지만, 어떤 작업이든 적어도 서너 번 이상은 수정을 요구하는 것 같다. 반대로 얘기하면, 이 정도로 계획을 다듬지 않으면 내가 원하는 방향으로 구현하지 않을 가능성이 높다. LLM은 나와는 다른 전제를 품을 때가 많아서, 여러 세부 계획에서 나와는 동상이몽을 하고 있을 가능성이 높다. 그런 것들을 사전에 최대한 제거하여 내 의도에 일치시켜야 한다. ## 알아서 피드백 루프를 돌게 LLM 코딩 에이전트로 개발을 할 때 가장 중요하다고 여겨지는 부분은 바로, 스스로 웬만큼 방향을 조정할 수 있도록 여건을 마련하는 일이다. LLM의 각종 구현 실수를 하나하나 내가 지적하는 게 아니라, 각종 자동화된 테스트와 정적 분석을 통해 스스로 깨닫고 구현을 고칠 수 있도록 해주는 것이다. 예를 들어, CSS 버그를 고칠 때를 생각해 보자. LLM이 CSS 코드를 고치게 한 뒤, 내가 웹 브라우저를 확인하는 방식은 너무 번거롭다. 대신, Playwright MCP를 붙여서 스스로 화면을 볼 수 있게 하는 게 낫다. 요는, LLM의 작업 결과가 요구 사항을 충족하는지를 스스로 판단할 수 있게 하여, 요구 사항이 충족될 때까지 작업을 계속하게 만드는 것이다. 비슷한 이유에서, 구현하기에 앞서 테스트 코드를 먼저 작성하도록 지시하는 것이 여러모로 편하다. 테스트 코드를 작성하는 과정까지만 사람이 지켜보면 되고, 그 뒤는 상대적으로 신경을 덜 써도 되기 때문이다. 실은, 나는 LLM 코딩 에이전트를 활용할 때도 가끔은 테스트를 직접 짜기도 한다. 프롬프트로 요구 사항을 정확하게 검증하는 테스트 코드를 짜게 하는 것보다 내가 직접 테스트 코드를 짜는 게 빠르겠다고 느낄 때 그렇다. 이런 작업 흐름을 선호하다 보니, 좀 더 엄밀한 타입 시스템을 갖춘 프로그래밍 언어, 좀 더 엄격한 린트 규칙 등이 LLM 코딩 에이전트를 활용할 때 훨씬 유리하다고 생각하게 되었다. LLM 시대 이전에도 생각은 비슷하긴 했지만 말이다. ## 가끔은 손 코딩 하지만 여전히 LLM에 많은 한계점이 있기 때문에, 나는 아직도 가끔은 손 코딩을 한다. API를 설계할 때도 그렇고, 엄밀한 테스트 코드를 짜고 싶을 때도 그렇다. (LLM은 테스트 코드를 좀 대충 짜는 경향이 있다.) 그리고 무엇보다, 재밌을 것 같은 코딩은 내가 한다! 바이브 코딩에 깊게 심취했다가 코딩의 재미가 사라졌다는 소프트웨어 프로그래머들의 얘기를 종종 듣는다. 내 생각에는, 재미있는 부분은 LLM에게 시키지 않는 게 좋다. 결과의 품질 때문이 아니라, 소프트웨어 프로그래머로서 모티베이션을 유지하기 위해서 그렇다. 재미 없고 지루한 부분, 그러니까 코딩하기 싫어지게 하는 작업에서 최대한 LLM을 활용하는 것이 LLM과 공존하는 좋은 전략이 아닐까 생각한다. 뭐, 적어도 내게는 이 방식이 잘 먹히는 것 같다. * * * 1. 이건 한 가지 팁인데, LLM에게만 필요한 정보를 `<!-- … -->` 주석 안에 적는 방법도 있다. ↩︎ 2. `Shift` + `Tab`을 두 번 누르면 계획 모드에 진입할 수 있다. ↩︎ 3. Claude Code에서 `/model` 명령어를 통해 고를 수 있다. ↩︎ *[LSP]: language server protocol *[RAG]: retrieval-augmented generation *[FEP]: Fediverse Enhancement Proposals
024 0
박준규 @curry.hackers.pub.ap.brid.gy · 19/09/2025
rosettalens.com/s/ko/dijkstra-haske…
rosettalens.com
다익스트라가 말하는 하스켈과 자바 - RosettaLens 번역
2001년 에츠허르 W. 다익스트라가 텍사스대 예산위원회에 보낸 편지를 통해, 신입생 프로그래밍 수업에서 하스켈 같은 함수형 언어를 자바로 대체하려는 움직임을 비판하고, 함수형 프로그래밍과 지연 평가의 교육적 가치, 첫 프로그래밍 언어 선택이 학생들의 사고에 미치는 중대한 영향을 논한다.
032 0
박준규 @curry.hackers.pub.ap.brid.gy · 19/09/2025
rosettalens.com/s/ko/introduction
rosettalens.com
위대한 선을 위한 하스켈 배우기! - RosettaLens 번역
하스켈이 무엇인지(순수 함수형, 지연 평가, 정적 타입과 타입 추론), 하스켈을 배울 때의 마음가짐과 GHC/GHCi로 시작하는 방법을 소개합니다.
110 0
박준규 @curry.hackers.pub.ap.brid.gy · 19/09/2025
rosettalens.com/s/ko/rust
rosettalens.com
Rust에 대한 나의 부정적 견해 - RosettaLens 번역
Rust에 대한 현재의 생각을 정리한 글이다. 안전성과 매크로 같은 장점부터 unsafe/panic, 문법적 설탕, 메모리 모델 집착, 재작성의 오류, 복잡성, 비동기 문제, 범용 언어로서의 사용에 이르기까지 비판적 관점을 제시한다.
022 0
Reposted by 박준규
백화선생 @hybrideconomics.bsky.social · 19/09/2025
그래서 KT는 LTE 주파수 확보 문제를 해소하기 위해 펨토셀을 울며 겨자먹기로 보급하게 됩니다. 미창부와 KT, SKT, 삼전 모두가 엮인 총체적 난국이었죠. 이 과정에서 시간이 지나 펨토셀이 일반화되고 알리에서도 물건을 파는 시대가 왔습니다 -_-; 그러면서 이번 사건이 터진겁니다. 당분간은 정말로 이거 해소할 방안이 없어 보이니, 꾸준히 모니터링 하면서 이상한 소액결제을 꾸준히 모니터링 하는 수 밖에 없어보입니다. 추측인데 eSIM, 듀얼 SIM이 되는 기기가 빈 SIM 공간이 있으면 취약하니, 어떻게든 채우라 하네요.
21113 0
박준규 @curry.hackers.pub.ap.brid.gy · 18/09/2025
rosettalens.com/s/ko/io-monad-consi…
rosettalens.com
IO 모나드는 해롭다 - RosettaLens 번역
‘IO 모나드’라는 표현이 하스켈 학습과 실천에 끼치는 해악을 지적하며, IO는 모나드가 아니라 IO 타입의 IO 액션으로 이해해야 한다고 주장하는 글. 오해를 부르는 답변과 잘못된 교육 관행을 비판하고, 언제 ‘X 모나드’라 부르는 것이 적절한지 예시와 함께 정리한다.
144 0
박준규 @curry.hackers.pub.ap.brid.gy · 18/09/2025
하스켈은 이렇게 씁니다! 참 쉽죠! main = do putStrLn "What is your name?" name <- getLine putStrLn ("Hello, " ++ name) RE: sns.lemondouble.com/notes/acs9jh6em6
sns.lemondouble.com
021 0
박준규 @curry.hackers.pub.ap.brid.gy · 16/09/2025
`Either` 씁시다. 너무 편합니다.
120 0
박준규 @curry.hackers.pub.ap.brid.gy · 16/09/2025
hackers.pub
공허한 참
하스켈 책을 읽는데 공허한 참(Vacuous truth)이라는 말이 나왔다. 함수 `all`에 빈 리스트를 넣으면 왜 참이 나오는지 묻는 질문인데 나는 그냥 이렇게 생각했다.(공허한 참이라는 말을 처음 들어봤다.) ‘구현이 그렇게 되어 있으니까 그렇지...’ 잠시 함수 `all`을 간단히 구현해보면 다음과 같다.[1] all :: (a -> Bool) -> [a] -> Bool all _ [] = True all p (x:xs) = p x && all p xs 리스트의 첫번째 원소부터 참거짓을 검사하고 나머지 원소는 재귀로 검사를 하는데 이때 첫번째 결과와 나머지 결과를 `&&`로 연산한다. 재귀를 끝내려면 입력이 빈 리스트인 경우 결과를 정의해야 하는데 리스트 안의 모든 원소들이 참이 나왔다고 가정했을 때 마지막 빈 리스트의 결과도 참이어야 전체 결과가 참이 될 것이다. 위와 같이 생각하고 별 생각 없이 해설을 읽었는데 다음과 같은 설명이 나왔다 > Alternatively, think of it this way: it _isn't_ true that any of them is _not_ alphanumeric. We call such a statement about all of nothing a "vacuous truth". 이게 무슨 말인가 싶어 ChatGPT에게도 물어보았는데 이런 게 나왔다. P⟹Q P \Longrightarrow Q 이 논리식(?)과 공허한 참에 대한 설명은 내 이해가 부족해서 ChatGPT의 설명 링크로 대체한다. ChatGPT의 설명 중 그나마 이해할 수 있었던 것은 다음 문장이었다. > 공집합의 모든 원소는 짝수이다. ‘공집합에는 원소가 없으니 원소 중에서 짝수가 아닌 경우가 존재하지 않으므로 이 명제는 참이다.’라고 한다. 아무튼 가끔 코드와 수학의 공통점 같은 걸 발견했을 때 재밌고 놀라운데 이번에도 약간 그런 경험을 했다. 아래 그림은 웃자고 장난으로 ChatGPT에게 부탁해서 그려보았다.(설명이 필요한 농담은 실패한 농담이므로 설명하지 않을 것이다. 설명이 없으니까 이 농담은 재밌는 농담이다.) * * * 1. 실제 구현은 이렇다. ↩︎
022 0
박준규 @curry.hackers.pub.ap.brid.gy · 15/09/2025
해커즈 퍼브 게시글에 이미지를 업로드하려면 어떻게 하나요?
000 0
박준규 @curry.hackers.pub.ap.brid.gy · 10/09/2025
hackers.pub
헬: 하스켈 방언 기반의 셸 스크립팅 언어
# 헬: 하스켈 방언 기반의 셸 스크립팅 언어 * 저자: Chris Done * 원문: https://chrisdone.com/posts/hell/ 헬은 제가 개인적인 셸 스크립팅 용도로 만든, 하스켈의 작은 방언 형태의 셸 스크립팅 언어입니다. 2월부터는 Hakyll 대신 헬을 사용해 이 블로그를 생성하고 있습니다.[1] 업데이트: 2024년 10월 3일 기준으로, 저는 업무에서 테라폼과 여러 API와 함께 약 2천 줄 규모의 다양한 대형 스크립트에 헬을 사용하고 있습니다. #!/usr/bin/env hell main = do Text.putStrLn "Please enter your name and hit ENTER:" name <- Text.getLine Text.putStrLn "Thanks, your name is: " Text.putStrLn name 제 2024년 새해 결심은 자동화를 위해 더 많은 셸 스크립트를 작성하는 것입니다. 지금까지는 bash의 단점 때문에 이런 작업을 피해왔습니다. 그리고 그 밖의 문제들도 있습니다. bash, zsh, fish 등은 다음과 같은 문제를 가지고 있습니다. * 이해할 수 없는 난해한 문법입니다. * 인용문 사용(`x=$(ls -1)` 등)으로 인해 실수하기 쉽습니다. * 기본적인 작업조차 서브 프로세스에 지나치게 의존합니다. * 따라서 비교, 산술, 순서 지정 등과 같은 것들은 전혀 원칙적이지 않으며, 함정이 가득합니다.[2][3] 하지만 bash에도 장점이 있습니다. 안정적이고, 단순하며, 모든 환경에서 동일하게 동작합니다. bash 스크립트를 작성하면 수년간 코드를 한 번도 수정하지 않고 계속 실행할 수 있습니다. 지난해 작성한 코드가 내년에도 그대로 동작한다는 점은 대부분의 인기 있는 프로그래밍 언어에서는 해당되지 않습니다. 하스켈을 한번 보세요. 그러므로 제가 실제로 사용하고 싶은 언어를 정의하기 위해, 셸 스크립팅 언어의 구조를 논의해보겠습니다. * 매우 기본적이어야 합니다. * 즉시 실행되어야 합니다.(눈에 보이는 컴파일 과정이 없어야 함) * 모듈 시스템이 없어야 합니다. * 패키지 시스템이 없어야 합니다.[4] * 추상화 기능(클래스, 복잡한 데이터 타입, 다형 함수 등)이 없어야 합니다. * 이전 버전과 호환되지 않게 변경되어서는 안 됩니다.[5] 왜 모듈이나 패키지 시스템이 없어야 할까요? 그런 시스템은 프로그램을 “완성” 상태로 유지하기 어렵게 만듭니다. 항상 다른 통합을 할 수 있고, 추가할 기능이 남아 있기 마련입니다. 저는 헬이 냉정하게 완결된 소프트웨어이길 원합니다. 완성된 소프트웨어에는 아름다움이 있습니다. 위 내용을 바탕으로, 저는 스크립팅 한계를 정의할 수 있습니다. 즉, 모듈 시스템이나 패키지 시스템, 추상화 기능이 필요해지거나, 표준 라이브러리 이상의 기능이 필요해질 때, 그때는 아마 일반 목적의 프로그래밍 언어를 사용하는 것이 적절합니다. 이를 고려하여, 저는 하스켈 방언[6]을 만들기로 선택했습니다. 그 이유는 다음과 같습니다. * 하스켈을 잘 알고 있습니다. * 제가 가장 자주 사용하는 언어입니다. * 비교, 순서 지정 등과 관련해 탄탄한 개념을 가지고 있습니다. * 동시성을 손쉽게 처리할 수 있는 좋은 런타임을 가지고 있습니다. * 가비지 컬렉션이 있습니다. * 바이트와 텍스트를 적절히 구분합니다. * 정적 Linux x86 바이너리로 컴파일할 수 있습니다. * 성능이 좋습니다. * 정적 타입을 지원합니다! 언어를 설계하면서 저는 다음과 같은 결정을 내렸습니다. * 충실한 하스켈 문법 파서를 사용합니다. * 이렇게 하는 것이 더 낫습니다. 직관과 코드를 재사용할 수 있습니다. * 임포트, 모듈, 패키지는 지원하지 않습니다. * 재귀 정의는 지원하지 않지만, `fix`를 사용하면 가능합니다. * 기본적인 타입 클래스만 지원합니다.(`Eq`, `Ord`, `Show`, `Monad`) 이는 예를 들어 `List.lookup`이나 일반적인 비교 연산 등에 필요합니다. * 다형 타입은 지원하지 않습니다. 이것은 일종의 추상화이며, 필요하지 않습니다. * 하스켈에서 이미 사용되는 이름(`List.lookup`, `Monad.forM`, `Async.race` 등)을 그대로 사용합니다. 이를 통해 기존 하스켈에 대한 직관을 재사용할 수 있습니다. 릴리스 페이지에서 정적 링크된 리눅스 바이너리를 다운로드할 수 있습니다. 구현 내부에 대해 알아보려면, 제가 업무에서 헬을 소개할 때 만든 헬 소개 슬라이드를 참고하세요. * * * 1. 이런 문제들에는 이제 진절머리가 납니다. ↩︎ 2. ShellCheck에서 발견되는 방대한 코드 점검 문제 목록을 한번 살펴보세요. ↩︎ 3. 코드 실행에 관한 이 블로그 글을 참고하세요. ↩︎ 4. 이 기준은 믿기 어려울 정도로 Node.js 생태계 위에서 동작하는 zx와 같은 스크립팅 언어는 제외합니다. ↩︎ 5. 참고: 이전 버전과의 비호환성이라는 햄스터 바퀴에서 벗어나기 ↩︎ 6. 다른 대체 셸 스크립팅 언어를 사용하지 않고, Elixir나 Oil도 사용하지 않았습니다. ↩︎
010 0