하스켈북의 저자에게 다음 책이 언제 나오냐고 물었더니 아이 넷 육아를 하느라 바빠서 계획이 없다고 한다. 아이가 넷이라니!(아이가 셋인 사람이 하는 소리) https://x.com/theodorvaryag/status/1933303607362715941

박준규
@curry@hackers.pub · 180 following · 103 followers
darcs hub
- hub.darcs.net/vincent
Hackage
- hackage.haskell.org/user/JoonkyuPark
- 중국어를 전공했습니다.
- 전역하자마자(금요일 전역, 월요일 출근) 강원도에서 제약 영업을 1년 4개월 정도 했습니다.
- 컴퓨터 학원을 1년 정도 다니면서 정보보안 스터디를 했습니다.
- 이때 Python, C, Java순으로 프로그래밍을 처음 접했습니다.
- 정보보안 관제 회사에서 고객사에 파견 나가 악성코드 대응 업무를 4년 정도 했습니다.
- 이때 개발자가 되고 싶어서 C++ 코리아 스터디에 몇 번 참석했는데 여기서 운명의 ‘모나드’라는 단어를 처음 접하고 하스켈 공부를 시작했습니다.
- 기업에서 정보보안 담당자 7년 차로 일하고 있습니다.
- 코딩은 취미로 하거나 혼자만 하는 소소한 업무 자동화에 활용하고 있습니다.
- 풀타임 개발자와 오픈소스 메인테이너의 삶을 동경하고 있습니다.
- 주로 Windows에서 Putty로(80x24, Consolas) 리눅스 서버에 붙어서 Vim을 사용합니다. Visual Studio Code를 잠깐 써봤는데 다시 Putty로 돌아왔습니다.
- 집에서는 몇 년 전 중고로 산 MacBook Air M1을 사용하고 있습니다.
- 회사에서는 몇 년 전 중고로 산 HHKB를 씁니다.
- 30대 후반, 기혼, 자녀는 셋(둘째와 셋째는 쌍둥이) 있습니다. 경기도에 살고 있습니다.
해커즈 퍼브에 사람이 많아져서 더 이상 일일히 모든 사람을 내 홀로(Hollo) 계정에서 팔로우 할 수 없게 됐다. 마침 레일웨이 구독 비용도 생각보다 많이 나와서 구독을 해지했다. 최소 비용 5 달러는 이미 결제했기 때문에 6월 22일까지는 계정을 유지할 수 있을 것 같은데 그 전에 백업을 잊지 않고 해야겠다.(글이 많진 않지만 그냥 없애기는 아쉽다.) 서비스 종료된 인스턴스의 핸들을 멘션하면 무슨 일이 벌어질까? 발신자 인스턴스에만 글이 남게 되는 걸까?
해커즈 퍼브 저장소를 딥위키에 넣어 봤다! https://deepwiki.com/hackers-pub/hackerspub
한동안 Hollo를 이용했는데 하스켈 코드 쓰러 잠깐 마음의 고향 해커즈 퍼브에 왔습니다.
welcome :: IO ()
welcome = do
putStrLn "Welcome to Hackers' Pub"
welcome
레일웨이가 터졌나! https://railway.app/
‘그냥 tryAny
쓰면 예외는 다 잡을 수 있는 거 아닌가? 왜 ResourceT
를 써야 하지?’라고 생각했는데 찾아보니 tryAny
로는 비동기 예외를 잡을 수 없다고 한다.
writeGreetingSafeAttempt :: IO ()
writeGreetingSafeAttempt = do
dir <- getDataDir
h <- IO.openFile (dir </> "greeting.txt") WriteMode
_ <- tryAny do
IO.hPutStrLn h "hello"
IO.hPutStrLn h "world"
IO.hClose h
오늘 자기 전에 Hollo 설치하자!
액티비티퍼브에 Place
가 있어서 사람들이 위치 공유도 하고 장소에 ‘좋아요’도 누르듯이 Book
도 있으면 재밌겠다. 사람들이 좋아하는 책에 ‘좋아요’도 누르고 서로 연결도 시켜주고!
이런 용어집도 있었구나! 이거 설마 예전에 봤던 neo4j 책에서도 이 용어집을 참고했으려나!
액티비티퍼브에서 Hashtag
뿐만 아니라 Mention
도 tag
구나!
@hongminhee洪 民憙 (Hong Minhee) Hollo를 사용하려고 합니다. 오라클 클라우드의 S3 호환 오브젝트 스토리지도 Hollo가 지원하나요?
저는 소셜 서비스의 핵심이 ‘좋아요(Like
)’라고 생각합니다. 저는 제가 페이스북에서 처음으로 ‘좋아요’를 많이 받아서 너무 신났던 감정을 잊을 수 없습니다. 그리고 저는 지금도 ‘좋아요’의 노예⋯(사람들은 그런 너를 관종이라고 부른단다.)
액티비티 퍼브를 구현한 지도앱 같은 게 있나요?
어디 RFC 문서 의존 관계 정리된 거 없나요?
엔드포인트 솔루션이나 네트워크 장비를 운영하다 보면 그 솔루션 본연의 역할을 지고지순(?) 하게 지키기보다는 뭔가 민원을 해결하는 예외 처리에 리소스를 투입할 때가 많은데 그럴 때마다 뭔가 법을 어긴 것 같고 마음이 안 좋다.
유심을 교체했다.
책 읽다가 생소한 게 나와서 적어 본다.
패키지 attoparsec
에 있는 함수 parseOnly
는 청크 한 개만 다룰 수 있고 나머지 청크는 버린다. 이러면 스트림 데이터를 읽을 수가 없다. 이때 모듈 Data.Attoparsec.ByteString.Run
에 있는 다음 도구를 사용하면 된다.
parseAndRestore :: Monad m =>
RestorableInput m ByteString -> Parser a -> m (Either ParseError a)
data RestorbleInput m i = RestorableInput
(m i) -- ^ Get the next chunk of input, or an empty string if there is no more
(i -> m ()) -- ^ Push a nonempty chunk of input back to the input stream
newRestorableIO
:: IO i -- ^ Get the next chunk of input, or an empty string if there is no more
-> IO (RestorableInput IO i)
RestorableInput IO ByteString
은 ByteString
청크 스트림을 표현한다. 이게 일종의 가변 상태를 가지고 있다. 이 가변 상태가 있기 때문에 앞에서 읽었지만 소모되지 않은 청크를 버리지 않고 입력에 다시 넣는 방식을 쓰는 것 같다.
이런 방식을 Incremental parsing이라고 하는 것 같다.
와, 이번 장에서는 ExceptT
모나드 트랜스포머를 알려준다! 크리스와 줄리 선생님 필력만 믿습니다.
https://hackage-content.haskell.org/package/transformers-0.6.2.0/docs/Control-Monad-Trans-Except.html
하스켈 패키지 서버 에러 나서 라이브러리 문서를 못보니까 코딩을 못하네!
모든 파싱 라이브러리가 스트리밍을 지원하는 건 아니구나. Attoparsec은 스트리밍 데이터를 파싱할 수 있다고 한다. https://hackage.haskell.org/package/attoparsec
책 제목에 포함되어 있는 단어이자 패키지 이름인 pipes
가 도대체 뭐하는 건지 감이 안 잡혔는데 드디어 해당 챕터에 돌입했다. 대충 보니 스트리밍 데이터를 처리하는 패키지인 것 같다. 라이브러리 제작자가 가브리엘라 님이라니!
‘스트리밍’이라는 단어를 많이 들어보고 많이 써왔는데 정확한 의미는 뭔지 몰랐다. 스트리밍은 큰 데이터를 잘라서 전달하는 것을 의미하는 것 같다.
ChatGPT도 루비 태그를 지원하면 좋겠다!
책에 나온 예제를 따라 하는데 결과가 책과 달라 이상했다. 코드를 한참 보다가 결국 원인을 찾았다. if 분기의 결과를 반대로 적었던 것이다.
repeatUntil
:: Monad m
=> m chunk
-> (chunk -> Bool)
-> (chunk -> m ())
-> m ()
repeatUntil getChunk isEnd f =
repeatUntilNothing getChunkMaybe f
where
getChunkMaybe = do
chunk <- getChunk
if isEnd chunk
then return (Just chunk)
else return Nothing
청크가 없으면 Nothing
을 리턴해야 하는데 반대로 적어버린 것이다. 덕분에 시간이 모자라서 남은 연습 문제 하나는 내일로 미뤄야겠다.
오늘 읽을 챕터 제목은 ‘스트리밍(Streaming)’이다. 서두에 이런 말이 나온다.
No one ever steps in the same stream twice, for it is not the same stream and they are not the same person.
ChatGPT에게 저게 유명한 말인지 물었더니 다음과 같이 대답했다.
https://chatgpt.com/share/6800e63e-07b4-800c-b32b-fef1723ef5c5
“판타 레이”가 여기서 나온 말이구나. 나는 이 말을 웹툰 “덴마”에서 처음 봤다. 무슨 뜻인지도 모르고 봤네.
책 한 챕터 다 읽었다. stm
패키지에서 제공하는 기능이 많지만 여기서는 TVar
만 쓸 줄 알면 돼서 분량이 많지 않네. 다음과 같은 형식으로 웹사이트 방문자 카운터를 구현할 수 있다.
countingServer :: IO ()
countingServer = do
hitCounter <- atomically (newTVar @Natural 0)
serve @IO HostAny "8000" \(s, _) -> do
count <- atomically (increment hitCounter)
sendResponse s (textOk (countHelloText count))
increment :: TVar Natural -> STM Natural
increment hitCounter = do
oldCount <- readTVar hitCounter
let newCount = oldCount + 1
writeTVar hitCounter newCount
return newCount
ChatGPT에 문제가 생긴 것 같다. 접속이 안 되네.
GHCup에서 GHC의 추천 버전이 9.6.7로 변경되었다. 내 로컬에서 버전 올려도 문제 없으려나!
✗ ghc 9.6.7 recommended,base-4.18.3.0
https://github.com/haskell/ghcup-metadata/pull/304
한편 현재 최신 버전은 9.12.2이다.
✗ ghc 9.12.2 latest,base-4.21.0.0 2025-03-12
자기 전에 그래도 한 챕터를 다 읽고 연습 문제까지 풀었다. 다음 챕터부터는 그 유명한 STM을 다룬다!
Vim에서 첫 행으로 갈 때 매번 1G
했는데 그냥 gg
하면 된다니!
하스켈 코드 포매터 stylish-haskell
을 잘 쓰고 있습니다. Vim에서 :%!stylish-haskell
이라고 입력하는 방식으로 사용하는데요, 코드에 문제가 있을 경우 코드 전체가 지워지고 다음과 같은 문자열로 대체됩니다.
<string>:1:18: error: [GHC-58481] parse error on input `!'
Vim에서 작업 중이므로 단순히 u
를 눌러서 취소하면 되긴 합니다만 혹시 다른 방법이 있을까요?
curl --http1.1 --dump-header - http://localhost:8000
--http1.1
처럼 버전을 정할 수 있다.--dump-header <filename>
으로 헤더를 파일로 저장할 수 있는데 파일 이름 대신-
를 적으면 헤더를 화면에 출력한다.
책[1]에서 문자열을 다룰 때 StrictText
와 Builder
의 성능을 비교하는 예제에서 Builder
의 성능이 더 좋다고 설명한다. 그런데 내 PC에서 같은 코드를 실행했는데 결과가 반대로 나왔다. 이상해서 문자열 길이를 늘렸더니 책에서 말한대로 나왔다.
ghci> concatSpeedTest 50000
0.004451s
0.04721s
ghci> concatSpeedTest 500000
0.062405s
0.023449s
ghci> concatSpeedTest 5000000
0.511402s
0.205632s
Chris Martin, Julie Moronuki, 《Sockets and Pipes》 ↩︎
Yacc와 같은 파서 제네레이터에 BNF를 넣으면 파서 코드가 자동으로 생성된다. 그런데 HTTP나 ActivityPub 등의 프로토콜 스펙을 입력으로 넣으면 자동으로 코드를 구현해주는 도구 어디 없나?
오, Quasi-quotation 신기하다. 문자열 안에 아스키 글자를 넣으면 컴파일 에러가 나네!
library/Book.hs:221:18: error:
• Must be only ASCII characters.
• In the quasi-quotation: [A.string|GET ♫ HTTP/1.1|]
|
221 | line [A.string|GET ♫ HTTP/1.1|] <>
| ^^^^^^^^^^^^^^^^
HTTP 요청에서 이름과 값의 쌍을 필드(fields)라고 부르는구나!
HTTP를 다룬 RFC 중 2022년에 9110과 9112가 나오면서 1999년에 나온 2616과 2014년에 나온 7230은 쓸모없어졌다고 한다.
책을 읽고 있는데 고퍼(Gopher)라는 고대의 프로토콜이 나왔다. 책에서 다음과 같이 아직 살아 있을 수 있는 고퍼 서버 목록을 소개한다.
고퍼 서버에 \r\n
을 날리면 응답이 오는데 위 세 개 서버에 요청을 날려보니 모두 응답이 온다.
예를 들어 세 번째 고퍼 서버의 응답은 다음과 같다.(스압 주의)
i fake (NULL) 0
i fake (NULL) 0
i __ __ _ _____ _ _ _ fake (NULL) 0
i| \/ | ___| |_ __ _| ___(_) | |_ ___ _ __ fake (NULL) 0
i| |\/| |/ _ \ __/ _` | |_ | | | __/ _ \ '__| fake (NULL) 0
i| | | | __/ || (_| | _| | | | || __/ | fake (NULL) 0
i|_| |_|\___|\__\__,_|_| |_|_|\__\___|_| fake (NULL) 0
i fake (NULL) 0
1MetaFilter /MetaFilter gopher.metafilter.com 70
isharing and discussing neat stuff on the web fake (NULL) 0
1Ask MetaFilter /Ask MetaFilter gopher.metafilter.com 70
iasking questions and getting answers fake (NULL) 0
1FanFare /FanFare gopher.metafilter.com 70
ipop culture discussion -- TV, movies, podcast, books fake (NULL) 0
1Projects /Projects gopher.metafilter.com 70
icreative work by MetaFilter community members fake (NULL) 0
1Music /Music gopher.metafilter.com 70
ioriginal musical and audio recordings by MeFites fake (NULL) 0
1Jobs /Jobs gopher.metafilter.com 70
iemployment opportunities and member availabilities fake (NULL) 0
1IRL /IRL gopher.metafilter.com 70
iorganizing meetups and community events in real life fake (NULL) 0
1MetaTalk /MetaTalk gopher.metafilter.com 70
iwhere the commuity talks about MetaFilter itself fake (NULL) 0
1FAQ /FAQ gopher.metafilter.com 70
ifrequently asked questions fake (NULL) 0
디스코드 서버 ‘하스켈 학교’에 오랜만에 뉴비가 오셔서 질문을 해주셨는데 내가 너무 신나서 횡설수설 하는 중⋯
ChatGPT에게 뭐만 물어보면 “좋은 질문이에요!”라고 말하는데 은근히 기분이 좋다.
여러분 darcs pull
한 다음에 바로 darcs revert
하지 마세요! 패치 로그는 남아 있는데 코드만 사라집니다!(너 말고 아무도 darcs 안 쓰니까 괜찮아⋯)