엔드포인트 솔루션이나 네트워크 장비를 운영하다 보면 그 솔루션 본연의 역할을 지고지순(?) 하게 지키기보다는 뭔가 민원을 해결하는 예외 처리에 리소스를 투입할 때가 많은데 그럴 때마다 뭔가 법을 어긴 것 같고 마음이 안 좋다.

bgl gwyng
@bgl@hackers.pub · 81 following · 91 followers
슈티를 함께 만들 팀을 만들고 있습니다. 관심 있으신 분, 또는 잘 모르겠지만 이야기를 나눠보고 싶은 분도 bgl@gwyng.com으로 편하게 연락주세요.
GitHub
- @bglgwyng
shootee
- www.shootee.io
10만년 만에 오픈소스 기여해봄. 러스트로 UI 개발해본건 처음인데 언어보단 UI가 더 까다로웠다 https://github.com/faiface/par-lang/pull/42
@joonnotnotJoon 오 어떤 경로로 Par에 도달하게 되셨나요? 세션 타입을 공부중이신가요?
러스트가 어렵다는 이야기에는 러스트의 특징을 이질적으로 느끼는 경우가 많아서도 있겠지만…어렵다는 말의 재생산이 어렵다는 이미지를 더 굳히는 것 같기도 합니다. 일종의 악순환이라고도 생각하는 게…어렵다는 이미지를 가지고 보면 개념 익히는 것도 어렵고 컴파일 에러를 해결하는 과정도 좀 더 고되게 느낄 수 있거든요. 러스트에 대한 이미지를 어렵다고 생각할수록, 갖고있는 배경지식이 러스트와 이질적일수록 어렵다고 느끼는 사람들이 많은듯합니다
뒤늦은 서울숲하스켈 조교 후기: 왠지 모르겠는데, 다들 운동을 열심히 하시는지 몸이 굉장히 좋으셨다. 건강한 신체에 건강한 정신이 깃든다를 실천하고 계신 분들이었다.
...는 농담이고(근데 사실입니다), 커리큘럼이 내가 상상하던 방향이랑은 꽤 달라서 흥미로웠다.
마지막 회차에 하스켈로 웹서버를 띄우는 것을 목표로 진행중이었는데, 이를 위해 Monad Transformer(Monad는 진즉에 해치우고), Tagless Final, Lens를 모두 소개한 상태였다. 근데 저 개념들이 '왜 하스켈에선 이거 안 돼요? 왤케 불편해요?' 같은 질문을 회피하지 않으려면 꼭 가르쳐야 하는 부분들이긴 하다. 가령, 'Monad만 배우면 이제 하스켈에서 명령형 코딩 할수 있다'라는 이야기가 이론상은 맞는데, Monad Transformer나 Algebraic Effect 같은거 안쓰면 웹사이트등 실제로 쓸모있는 프로그램을 사실상 짤수가 없다. 그래서 가르치긴 해야한다.
문제는 저걸 다 가르치려면 상당히 빡셀테니, 나는 만약에 내가 하스켈 부트캠프를 한다면 일단은 저런걸 회피하고 하스켈의 멋진 부분에 집중하는 커리큘럼을 짜야겠다고 그동안 생각했었다. 근데 또 이건 어찌보면 기만이기도 하다. 그런데 서울숲하스켈에서는 어찌저찌 다들 따라오도록 구성을 잘하신것 같다 하스켈을 이질적인(긍정적으로든 부정적으로든) 프로그래밍 언어로 소개하는게 아니라, 언젠가 본인의 작업에 활용할 언어의 후보로 올리게끔 하려면 저런 내용들을 다 다뤘어야 할것이다.
암튼 그동안 수고많으셨습니다. @eunmin은민
@lionhairdino
@bglbgl gwyng 너무 큰 비약인지는 모르겠으나 보면서
https://hazel.org/build/dev/ 이 떠오르네요. 편집기가 code를 맘대로 바꾼다는 측면에서 말이지요.
@ailrunAilrun (UTC-5/-4)
@lionhairdino 아 이거 찾는중이었습니다ㅋㅋ hegel로 아무리 검색해도 안나왔는데 hazel이었네요.
들여쓰기와 공백에 민감?한 게 다른 건가요?
하스켈은 들여쓰기가 슈거 문법 같은 거라 들여쓰기 없이 괄호와 세미콜론으로 작성할 수 있는데, 익숙해서 그런가 들여쓰기가 더 가독성이 높은 느낌도 듭니다. @bglbgl gwyng
@lionhairdino 들여쓰기를 제외한 토큰을 구분하는 용도로 쓰이는 공백을 그냥 공백이라고 했네요.
들여쓰기가 물론 가독성은 좋지요. 근데 편집할때 불편을 주는 문제가 있고, 에디터가 좀 힘을써주면 양쪽의 장점을 다 취할수있다는 얘기였습니다.
저도 비슷한 생각인데, Haskell이나 Rust는 코너 케이스를 다루지 않고는 컴파일도 못 하게 금지하는 경우들이 꽤 많고 (그래서 좋은 언어지요), 빠르게 해피 패스만을 검증하고 싶을 때는 Python 같은 널널한 언어(복잡하고 규모가 큰 소프트웨어를 만들 때는 나쁜 언어가 되지요)가 쉽게 느껴질 수 있다고 생각합니다. 즉, Haskell이나 Rust가 어렵다고 말할 때의 어려움은 개념적 이해의 난도라기 보다는 시행착오의 커브의 경사를 얘기하는 것 같아요.
비슷한 측면에서 저는 Python의 들여쓰기를 강제하는 문법이 프로그래밍 초심자에게 좋은 습관을 처음부터 정착시키는 데에는 일조할 수 있겠지만, 결코 쉽지는 않다고 생각합니다.
RE: https://hackers.pub/@bgl/01967f97-67ab-7a98-a6e5-16cb3ef31856
@hongminhee洪 民憙 (Hong Minhee) 약간 딴 얘긴데, 저는 들여쓰기가 그냥 안좋은 문법요소 같습니다. 코드의 복붙을 unreliable하게 만들어버려서요. 반대로 space sensitive한 문법은 괜찮다고 생각합니다. 복붙시 문제가 생겨도 스페이스 한번 치면 해결되니까요. 들여쓰기 대신에 {} 쌍을 쓰게 만들되, 에디터에서 보여줄때 어떻게 알아서 예쁘게 보여주는게 낫다고 생각해요.
develop :: Maybe Coffee -> IO Code
develop Nothing = pure Garbage
develop (Just fuel) = do
code <- think fuel >>= implement
filter (writtenIn Haskell) code
그때 하스켈학교 디스코드에서 코드 백일장 열어서 나온 것들을 조합해서 만들었다.
RE: https://hackers.pub/@bgl/01967fa1-dee1-76ca-93bc-1778c0dc9a75
서울숲하스켈보고 예전에 카카오 하스켈부트캠프했던게 생각났다. 그때 굿즈도 만들었다. hoxy 갖고싶은분 계시면 마플에 새로 주문넣어서 보내드립니다.
Hackers' Pub 저장소에 보내주신 @perlmint 님과
@morealLee Dogeon 님의 CSS 및 PWA 관련 패치들이 모두 적용되어 배포까지 완료되었습니다.
- https://github.com/hackers-pub/hackerspub/pull/44
- https://github.com/hackers-pub/hackerspub/pull/45
- https://github.com/hackers-pub/hackerspub/pull/46
- https://github.com/hackers-pub/hackerspub/pull/47
여러분의 많은 기여 감사합니다. 🙏
참고로 현재 hackers.pub에 배포된 게 어떤 버전인지 알고 싶다면 https://hackers.pub/nodeinfo/2.1에 들어가셔서 software.version
을 보시면 됩니다. 버전의 마지막 부분인 빌드 넘버가 Git 커밋 해시입니다.
러스트가 어렵다는 이야기가 숙고없이 재생산 되는거 같긴 합니다. 제가 러스트를 별로 안써봐서 실제로 얼마나 어려운진 모르겠습니다.
그런데 말씀하신 모나드, 트레잇, 오너십 등의 개념들과 클래스는 좀 차이가 있다고 생각합니다. 그러니까 자바에서 클래스 때문에 어떤 코드를 못짜게 되진 않잖아요? 자바를 하면서 클래스를 제대로 쓰지않고도 뭔가 만들순 있습니다. 반면 전자의 개념들은 잘못된 코드를 짜는걸 막고, 초보자 입장에서 뭔가 하고싶은게 있는데 그게 금지되는 상황에서 어렵다는 느낌을 (필요이상으로 크게) 받을수 있다고 생각합니다.
서울 하스켈숲 워크숍 완주했습니다...! @eunmin 님의 친절한 설명과 세심한 준비에 감사합니다 🙇
@nyeongAn Nyeong (安寧) 축하합니다! 앞으로도 함께 즐하스켈해요.
- Zig 라이브러리 짜기
- 이력서 쓰기
한달간 써본결과 Cursor >> >Windsurf 란 결론을 내렸다. 근데 Cursor는 프로젝트를 켜지를 못한다는(...왜???) 사소한 문제가 있어서 못쓰고 있다.
Rust로 작성한 JPEG XL 디코더, jxl-oxide의 버전 0.12.0을 릴리스했습니다. https://github.com/tirr-c/jxl-oxide/releases/tag/0.12.0
CMYK 프로파일 등 복잡한 ICC 프로파일을 지원하기 위해 기존에 사용하던 Little CMS 2 (lcms2) 에 더해, Rust로 작성된 색 관리 시스템인 moxcms 지원을 추가한 것이 주요 변경사항입니다. CLI 툴의 기본 CMS는 아직 lcms2이지만 --cms moxcms
옵션으로 moxcms를 사용할 수 있습니다.
근데 백엔드 구현하기 GraphQL이 REST보다 쉽지 않나?? (강성GQL교도)
@bglbgl gwyng rebase는 CLI에서 하는게 훨씬 편하더라구요......
@kodingwarriorJaeyeol Lee 얼마전에 Gitkraken 1년치 화끈하게 결제했는데 그 이후로 CLI 실력이 점점 늘고있습니다;;
Hackers' Pub에 GraphQL API를 추가하고 있습니다. https://hackers.pub/graphql가 GraphQL 엔드포인트입니다. 아직 인증 기능도 없고 노출된 노드도 얼마 없습니다만, 차차 추가해 나갈 예정입니다. 다음은 예시 쿼리입니다:
{
actorByHandle(handle: "@hongminhee@hackers.pub") {
uuid
iri
type
handle
instance {
host
software
softwareVersion
}
name
bio
avatarUrl
url
}
}
Protected Route에 대해 좀 생각을 더 해봤는데, 난 처음엔 React Native Navigation에서만 쓰는 해괴한 방식인줄 알았다. 근데 좀더 생각해봐도 별로긴하다. 라우터란게 결국 전체 상태중에 path만 별도로 관리해주는 건데,
app :: State -> View
--- router 도입
app :: (Path, State) -> View
--- Protected Route
app :: State -> Path -> View
여기서 대충 말해서 Path -> View
가 라우터라 할수 있겠다. Protected Route는 State로부터 라우팅 룰을 동적으로 바꿔버리는 방식이다.
근데 애초에, 라우팅 룰을 정적인 정보로 만들고(즉 ->
가 아님) 활용할게 아니라면 라우터를 쓸 이유가 뭘까? 그냥 Path
를 일반적인 다른 상태와 똑같이 취급해도 상관없을 것이다. 근데 react-router도 그렇고 애초에 라우팅 룰을 정적인 정보로 알수있는 설계로 되어있지가 않다. Tanstack Router는 예외다.
근데 이러면 그냥 XState 쓰면 되지 않나? 상태/전이에 대한 정적인 정보를 가지고 있고, 또 React Native에서의 라우팅에는 화면 전환이 단순한 Path 변경이 아닌 어떻게 화면을 전환 시킬지(애니메이션 등)도 기술해야하는데, 이 역시 XState 이벤트로 넣으면 된다.
Gitkraken을 쓰는데 interactive rebase에서 커밋 내용까지 수정하는걸 GUI 내에서 하는 방법을 모르겠다ㅠㅠ
@bglbgl gwyng 현재는 좀 더 명확하도록 수정했습니다.
bgl gwyng replied to the below article:
논리적이 되는 두 가지 방법 - 논리와 저수준(Low-level) 자료 표현(Data representation) (2 편 중 1 편)

Ailrun (UTC-5/-4) @ailrun@hackers.pub
이 글은 어떤 문장이 "논리적"이라고 할 수 있는지에 대한 심도 있는 탐구를 시작합니다. 일상적인 오용을 지적하며, 진정으로 논리적인 주장은 증명 가능성과 체계의 무모순성이라는 두 가지 핵심 조건을 충족해야 한다고 주장합니다. 특히, "좋은 가정 아래" 논리성을 증명하는 두 가지 방법, 즉 함수형 언어와 유사한 구조를 가진 자연 연역과, 약간의 "부정행위"를 통해 무모순성을 쉽게 보일 수 있는 논건 대수를 소개합니다. 글에서는 명제와 판단의 개념을 명확히 정의하고, 자연 연역을 통해 논리적 증명을 구축하는 방법을 상세히 설명합니다. 특히, 자연 연역과 함수형 언어 간의 놀라운 유사성, 즉 커리-하워드 대응을 통해 논리적 사고와 프로그래밍 언어 이해 사이의 연결고리를 제시합니다. 또한, 자연 연역의 한계를 극복하고 무모순성을 보다 쉽게 증명할 수 있는 논건 대수를 소개하며, 자연 연역과의 구조적 차이점을 강조합니다. 이 글은 논리적 사고의 깊이를 더하고, 프로그래밍 언어와 논리 간의 관계에 대한 흥미로운 통찰을 제공합니다. 특히, 커리-하워드 대응을 통해 논리와 프로그래밍이 어떻게 연결되는지 이해하고 싶은 독자에게 유익할 것입니다.
Read more →@ailrunAilrun (UTC-5/-4) 좋은 글 감사합니다!
저런 식의 증명 대신 L에서 x가 등장하는 자리에 M을 치환해넣고, y가 등장하는 자리에 N을 치환해넣은 증명 또한 가능하기 때문이다. 이 직관을 구체화한 것이 바로 다음의 cut 제거 정리이다.
요부분이 좀 헷갈리는데요, 글에서 소개된 Sequent Calculus에서는 치환에 대한 설명이 없는 것으로 보입니다. 함수형 언어에서의 치환에 해당하는 Sequent Calculus에서의 규칙이 추가로 필요한데 생략하신 걸까요, 아니면 글의 내용만으로 설명가능한데 제가 뭔가 놓친걸까요?
@jhhuhJi-Haeng Huh 이때 vscode-with-extensions로 설치되는 확장들은 그냥 파일 복사방식으로 설치되지않나요? 저는 vscode haskell을 설치하면 Nix로 HLS도 함께 설치되는 방식을 상상했습니다.
bgl gwyng shared the below article:
논리적이 되는 두 가지 방법 - 논리와 저수준(Low-level) 자료 표현(Data representation) (2 편 중 1 편)

Ailrun (UTC-5/-4) @ailrun@hackers.pub
이 글은 어떤 문장이 "논리적"이라고 할 수 있는지에 대한 심도 있는 탐구를 시작합니다. 일상적인 오용을 지적하며, 진정으로 논리적인 주장은 증명 가능성과 체계의 무모순성이라는 두 가지 핵심 조건을 충족해야 한다고 주장합니다. 특히, "좋은 가정 아래" 논리성을 증명하는 두 가지 방법, 즉 함수형 언어와 유사한 구조를 가진 자연 연역과, 약간의 "부정행위"를 통해 무모순성을 쉽게 보일 수 있는 논건 대수를 소개합니다. 글에서는 명제와 판단의 개념을 명확히 정의하고, 자연 연역을 통해 논리적 증명을 구축하는 방법을 상세히 설명합니다. 특히, 자연 연역과 함수형 언어 간의 놀라운 유사성, 즉 커리-하워드 대응을 통해 논리적 사고와 프로그래밍 언어 이해 사이의 연결고리를 제시합니다. 또한, 자연 연역의 한계를 극복하고 무모순성을 보다 쉽게 증명할 수 있는 논건 대수를 소개하며, 자연 연역과의 구조적 차이점을 강조합니다. 이 글은 논리적 사고의 깊이를 더하고, 프로그래밍 언어와 논리 간의 관계에 대한 흥미로운 통찰을 제공합니다. 특히, 커리-하워드 대응을 통해 논리와 프로그래밍이 어떻게 연결되는지 이해하고 싶은 독자에게 유익할 것입니다.
Read more →@bglbgl gwyng vscode 쓰시죠? 예전 기억을 더듬어보면
nix-ld
만 잘 설정해서 필요하면 extension들을 ad-hoc하게 깔아서 쓰는덴 큰 문제가 없었습니다. (제 경험상 vscode-fhs는 비추합니다) 저는 그러다 한번씩 pkgs/applications/editors/vscode/extensions/update_installed_exts.sh
스크립트를 이용해서 깔려있는 extension들을 nix-expression으로 뽑아서 vscode-with-extensions
로 다시 빌드하곤 했습니다.
@jhhuhJi-Haeng Huh 이때 vscode-with-extensions로 설치되는 확장들은 그냥 파일 복사방식으로 설치되지않나요? 저는 vscode haskell을 설치하면 Nix로 HLS도 함께 설치되는 방식을 상상했습니다.
에디터의 플러그인도 Nix로 관리하고 싶다. 에디터 쪽에서 지원을 해야하는데, 누군가 총대매고 해줄법도 한데...
@bglbgl gwyng vscode 쓰시죠? 예전 기억을 더듬어보면
nix-ld
만 잘 설정해서 필요하면 extension들을 ad-hoc하게 깔아서 쓰는덴 큰 문제가 없었습니다. (제 경험상 vscode-fhs는 비추합니다) 저는 그러다 한번씩 pkgs/applications/editors/vscode/extensions/update_installed_exts.sh
스크립트를 이용해서 깔려있는 extension들을 nix-expression으로 뽑아서 vscode-with-extensions
로 다시 빌드하곤 했습니다.
Void Linux 한국 미러가 없어서 수제로 구워왔습니다 많관부
https://mirror.kyoku.dev
하루를 매우 낮은 능률로 허비한다음에 Bartosz 아저씨 강의보는걸로 죄책감을 덜어낸다.
[단편] 나는 나를 사랑해
에디터의 플러그인도 Nix로 관리하고 싶다. 에디터 쪽에서 지원을 해야하는데, 누군가 총대매고 해줄법도 한데...
@bglbgl gwyng Zed 쓰고 싶은데 Deno 확장이 약간 만들다 만 느낌이라 못 쓰고 있네요…
@hongminhee洪 民憙 (Hong Minhee) 저는 HLS가 안 켜졌어서 일단 잠깐 치워놨습니다. 막히는 부분이 비슷하네요ㅋㅋ
바쁜일 좀 정리되면 Zed를 트라이해봐야겠다. 깔아놓고 쓰질 않고있네.
I'm exploring a new idea called FediOTP (codename): an authentication system that uses #ActivityPub DMs to deliver one-time passwords, allowing any #fediverse account to authenticate with web services. Unlike current solutions that rely on specific APIs (#Mastodon, #Misskey), this would work with any ActivityPub-compatible server, increasing interoperability across the fediverse. Would love to hear your thoughts on potential challenges or use cases for this approach.
Lynx 쓰고싶다... https://lynxjs.org/api/lynx-api/lynx/lynx-get-text-info.html 이런거 처음부터 제공하는거부터 너무 느낌이 좋아.
많은 파일을 대상으로 텍스트를 대량 치환해야 할 일이 꽤 있는데, 파일 탐색과 치환을 인터랙티브하게 수행할 수 있는 툴이 없어서 ised(interactive sed)를 만들었다. 월요일부터 개밥먹기 할 예정. https://github.com/parksb/ised
@hongminhee洪 民憙 (Hong Minhee)
@xiniha 오 저도 pothos씁니다. pothos에 Prisma plugin, Relay plugin 발라서 날먹한 부분이 많습니다.
@parksbSimon Park
안녕하세요 오픈 소스에 관심있는 개발자입니다!
자정되면서 맞지 않는 부분은 어떤게 있을까요? 🤔
yarn-plugin-catalogs가 pnpm catalog 기능을 yarn에도 확장 시킨것 같은데 이런 부분에서 안맞는게 있는걸까요?
#Yarn #pnpm #javascript
@zust 플러그인은 오히려 Yarn이 잘 만든 소프트웨어라는 생각을 하게 된 계기예요 ㅎㅎ 저는 '공격적인 수정' 중 하나가 PnP라고 생각하는데요, 최근에 겪은 문제로는...
(1) Node v22.12에서 require(esm)이 가능해졌음에도 PnP는 이를 제대로 지원하지 못했습니다. (https://github.com/yarnpkg/berry/issues/6646)
(2) Go로 재작성하고 있는 타입스크립트도 PnP와의 호환이 문제가 됩니다. (https://github.com/microsoft/typescript-go/issues/460)
Yarn만의 문제는 아니고, 호환성과 혁신성을 트레이드오프할 때 일어날 수 있는 일인 것 같습니다.
그... 예전엔 ChatGPT한테 반말로 질문해도 존댓말로 답변하지 않았나요? 요즘은 반말하면 반말로 대답하네요. 그새 머리가 커진 느낌.
# Ask Hackers Pub : 이번 주말에 뭐 하시나요?
이번 주말에 뭘 하려고 계획 중인지 편하게 얘기해 보아요.
읽을 책, 가볼 곳, 해볼 것.. 어떤 것이든 좋습니다.
도움 요청이나 피드백 요청도 좋습니다.
물론! 아무것도 하지 않고 쉬는 것도 훌륭합니다.
* 지난 주말에 계획하셨던 일의 회고도 한 번 남겨보면 좋을 것 같아요.
- semantic-lang-gen에 hypertypes 적용
- join 가능한 reactive 테이블 joinable-table PoC
- bunja 도입 PoC
- 헬스장 가기
https://github.com/faiface/par-lang
Session Type 기반의 언어라고 한다.
bgl gwyng shared the below article:
간단한 rpmbuild의 사용과 private rpm package 배포
Perlmint @perlmint@hackers.pub
마지못해 패키지를 만들어야 할 것 같은 사람을 위한 설명입니다. 제대로된 패키지를 만들고 싶은 경우에는 부족한 점이 많습니다.
대부분의 경우에는 프로그램을 직접 소스에서 빌드하는 일도 적고, 그걸 시스템 전역에 설치하는 일도 흔치는 않을 것입니다. 좋은 패키지매니저와 관리가 잘되는 패키지 저장소들을 두고 아주 가끔은 직접 빌드를 할 일이 생기고, 흔치 않게 시스템 전역에 설치할 일이 생길 수 있습니다. 어지간한 프로그램들은 요즈음의 장비에서는 별 불만 없이 빌드 할 만한 시간이 소요되나, 컴파일러처럼 한번 빌드했으면 다시는 하고 싶지 않은 프로그램을 다시 설치해야하는 경우도 있을 수 있습니다. 하필 이런 프로그램들은 결과물도 덩치가 매우 큽니다. 이럴 때는 최대한 간단하고 필요한 항목만 패키지에 넣어서 만들어두고 다시 활용하면 좋을 것이기에 이런 경우를 위한 rpmbuild에 대한 나름 최소한의 사용 방법을 정리해봅니다.
rpmbuild
rpmbuild는 rpm-build
패키지로 설치가 가능하며, 나름 단순하게 rpm으로 패키징을 할 수 있는 유틸리티입니다. spec파일에 패키지 정보, 빌드 명령, 설치 명령, 패키지가 포함해야 할 파일 목록을 작성해서 rpmbuild에 입력으로 넣어주면 빌드부터 시작해서 rpm패키지를 만들어줍니다. native 프로그램의 경우 디버그 심볼을 알아서 분리해서 별도의 패키지로 만들어주고, 필요한 의존성도 추정해서 명시해줍니다. 또한, 필요한 경우 하나의 spec 명세로 연관된 서브 패키지도(ex. 실행파일 패키지인 curl과 라이브러리 패키지 libcurl, 라이브러리를 사용하기 위한 개발 패키지 libcurl-devel) 같이 만들 수 있습니다.
작업 환경
rpmbuild는 기본으로 ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS,BUILDROOT}
의 경로에서 동작하며 각 경로의 용도는 다음과 같습니다.
SOURCES
에는 압축된 소스코드가 위치합니다.SPECS
에는 패키지 정의인 spec파일을 둡니다.BUILD
밑에서 빌드 작업이 진행됩니다.RPMS
에 바이너리 rpm결과물이 생성됩니다.SRPMS
에는 소스 rpm결과물이 생성됩니다.BUILDROOT
는 패키징 하기 위해 빌드 결과물을 모으는 경로입니다.
spec파일
spec파일은 패키지를 어떻게 빌드하고 어떤 항목들이 패키지에 포함될지, 패키지의 이름, 설명 및 의존성 등의 메타데이터, 패키지 설치, 삭제시의 스크립트를 정의할 수 있습니다. 보통 시작 부분에는 메타데이터 정의로 시작하며, 다음과 같은 기본적인 형태를 취합니다. 나름 단순하게 만든 python을 위한 spec을 예시로 들어보겠습니다.
Summary: Python %{version}
Name: python-alternative
Version: %{version}
Release: 1%{?dist}
Obsoletes: %{name} <= %{version}
Provides: %{name} = %{version}
URL: https://www.python.org
Requires: libffi openssl
AutoReq: no
License: PSFL
Source: https://www.python.org/ftp/python/%{version}/Python-%{version}.tgz
BuildRequires: libffi-devel openssl-devel
BuiltRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
%define major_version %(echo "%{version}" | sed -E 's/^([0-9]+)\\..+/\1/' | tr -d)
%define minor_version %(echo "%{version}" | sed -E 's/^[0-9]+\\.([0-9]+)\\..+/\1/' | tr -d)
%description
Python
%package devel
Summary: python development files
Requires: %{name} = %{version}-%{release}
%description devel
Python development package
%prep
%setup -q -n Python-%{version}
%build
./configure --prefix=%{_prefix}
%install
%{__make} altinstall DESTDIR=%{buildroot}
%{__ln_s} -f %{_bindir}/python%{major_version}.%{minor_version} %{buildroot}/%{_bindir}/python%{major_version}
%clean
%{__rm} -rf %{buildroot}
%files
%{_bindir}/python*
%exclude %{_bindir}/idle*
%{_bindir}/pip*
%{_bindir}/pydoc*
%exclude %{_bindir}/2to3*
%{_libdir}/libpython*
%{_prefix}/lib/libpython*
%{_prefix}/lib/python*
%{_mandir}/man1/python*
%files devel
%{_includedir}/python*
%{_prefix}/lib/pkgconfig/python*
%
로 매크로를 사용할 수 있으며, %package
, %description
, %files
같은 매크로는 인자를 주어서 서브 패키지를 정의하는데도 쓸 수 있습니다.
앞선 예제처럼 devel
이라고작성하면 메인 패키지이름 뒤에 붙여서 python-alternative-devel
가 되며, curl
- libcurl
과 같은 경우에는 메인의 이름은 curl
이고, 딸린 패키지를 정의할 때는 %package -n libcurl
과 같이 -n
옵션을 추가해서 지정할 수 있습니다. 몇몇 매크로는 단계를 정의하는 것과 같은 동작을 하며 다음과 같습니다.
%package
유사성을 보면 spec파일의 맨 첫부분은 메인 패키지의 %package
에 해당하는 것이 아닌가 싶습니다. <Key>: <Value>
의 형태로 메타정보를 작성합니다. 대부분은 Key를 보면 무슨 값인지 추측 할 만합니다.
나중에 설명할 %files
에서 나열한 파일을 rpmbuild가 분석하여 자동으로 패키지가 필요로 하는 의존성을 추정해서 추가 해 줍니다. python 스크립트, perl 스크립트, native 실행파일 등을 분석해서 알아서 추가해주는 것 같은데, 경우에 따라서는 틀린 의존성을 추가해주기도 합니다. 이 때는 AutoReq: no
를 설정하여 자동 의존성 추가를 막을 수 있습니다. 이 python-alternative 패키지는 /usr/local/bin/python%{version}
을 설치하는데 아마도 같이 포함되는 python 스크립트에 의해서 /bin/python
을 의존성으로 추정하여 요구합니다. 패키지 스스로가 제공하는 의존성은 미리 설치 되어있기를 요구하지 않게 동작하는 것 같으니 보통은 문제가 없습니다만, 이 경우에는 스스로 제공을 하지 않기 때문에 python을 설치하기 위해서 python이 필요한 경우가 발생하므로 AutoReq
를 껐습니다.
%prep
준비단계로 소스코드의 압축을 해제하고 필요한경우 패치를 적용합니다.
%setup
매크로를 이 안에서 보통 사용하며, %setup
은 Source
에 명시된 파일명의 압축 파일을 SOURCES
밑에서 찾아서 압축을 풉니다. 그리고 동일한 이름의 디렉토리로 이동을 합니다. 앞선 예제에서는 SOURCES/Python-%{version}.tgz
의 압축을 풀고 Python-%{version}
으로 이동을 합니다.
패치가 필요한 경우 보통 이 뒤에 패치를 적용하는 명령들을 추가 합니다.
%build
설정, 컴파일 등을 수행하는 단계입니다. 이곳에서 자주 하는 매크로로 %configure
, %make_build
등이 있습니다. %configure
는 configure를 prefix 및 기타 몇가지 일반적으로 쓰이는 옵션을 추가하여 실행해주며, %make_build
는 make
와 비슷하게 모든 타겟을 빌드 합니다. 예제에서는 둘다 안쓰고 있고, 심지어 실제 빌드는 안하는데 어쨌든 이후의 %install
까지 지나고나서 빌드 결과물만 맞는 위치에 만들어지면 대충 패키지를 만드는데는 별 문제는 없는 것 같습니다.
%install
여기서 빌드 결과물을 설치하는 명령을 작성합니다. 일반적으로 %make_install
을 사용하여 make install DESTDIR=%{buildroot}
와 비슷한 명령을 수행하여 %{buildroot}
밑에 빌드 결과물이 prefix를 유지하여 설치되게 합니다. 예제의
%{__ln_s} -f %{_bindir}/python%{major_version}.%{minor_version} %{buildroot}/%{_bindir}/python%{major_version}
을 보면 추정 할 수 있듯이, 패키지에 포함시킬 파일들을 %{buildroot}밑에 생성을 하면 되며, 추가적인 심볼릭 링크는 패키지를 빌드하는 시점에는 존재하지 않지만, 패키지를 설치하게되면 존재하게 될 %{_bindir}/python%{major_version}.%{minor_version}
를 향하는 것을 %{buildroot}
밑인 %{buildroot}/%{_bindir}/python%{major_version}
에 만듭니다.
%files
패키지에 포함될 파일 목록을 작성합니다. glob 양식으로 파일 목록을 작성할 수 있습니다. %{buildroot}
밑에 생성 되었지만 어느 %files
에도 포함되지 않은 파일이 있는 경우에는 빌드를 실패합니다. 그러므로 %exclude
를 사용해서 명시적으로 제외해줘야 합니다.
기타 매크로
rpmbuild에서는 기본으로 다양한 매크로를 제공하고 있습니다. --define "_libdir %{_prefix}/lib64"
와 같은 옵션을 실행시에 주어서 실행시점에 매크로를 덮어 쓸 수도 있고, 앞선 spec파일 내의 %define major_version
와 같이 다른 매크로와 셸 명령을 활용하여 매크로를 정의 할 수도 있습니다.
원하는 동작을 안하는 것 같은 경우에는 --show-rc
옵션을 사용하여 매크로가 어떻게 정의되어있는지 확인해 볼 수 있습니다.
빌드
rpmbuild의 매뉴얼을 보면 자세하게 나와있지만 가장 단순하게는
rpmbuild -bb <specfile>
로 바이너리 패키지를 빌드할 수 있습다. 이 때, 압축된 소스코드는 미리 SOURCES밑에 두어야 합니다.
private rpm package 배포
직접 비공개 패키지 저장소 프로그램을 실행하여 제공하는 방법도 있겠지만, 최대한 간단하게 할 수 있는 방법으로, rpm관련 패키지 설치 명령이 입력으로 http등의 URL도 받는 것을 활용하여 적당한 장비에서 http로 서빙을 해주면 됩니다.
Deno가 Node.js보다 인기 있는 놀라운 연합우주…
RE: https://hollo.social/@hongminhee/019661bd-601a-7df4-aa40-688011ec43ac
요즘 Server-side rendering인데 차트를 streaming으로 incremental update 할수있는 좋은 방법을 찾고 있다. Solid Start의 Server Signal이란게 원하는거랑 가까워 보이긴한데...
거꾸로 상태 모나드로 강화 학습 하기 (1/2) 2편을 마저 써야하는데, 먼저 하스켈 세미나 시간에 전체 내용으로 동료들한테 발표를 했다. 근데 발표하면서 글에 해결해야하는 문제점이라고 써놓은게 되게 얼렁뚱땅이란걸 깨달았다. 실제로 코드를 짠지가 시간이 좀 되어서 정확히 뭐가 문제였는지 좀 까먹어서 실제로 쓰기가 힘들어서 중간에 좀 놨었다ㅋㅋ 암튼 발표를 해보는게 내용 점검에 도움이 많이 된단걸 알았다.
거꾸로 상태 모나드로 강화 학습 하기 (1/2)
기계 학습을 전혀 모르고 살면 안 되겠다 싶어, 얼마전부터 하스켈로 공부하기 시작했다. Hasktorch라고 하스켈용 Torch 바인딩을 사용한다. 이전에 도전했을땐 MNIST까지 하고 다음에 뭘 해야할지 모르겠어서 그만뒀는데, 이번엔 강화 학습으로 이어나가 보기로 했다. 강화 학습은 주어진 환경에서 보상을 최대화하는 에이전트를 학습시키는 것으로, 데이터가 필요없다는게 장점이다. 대신 환경을 만들어야 하는데, 간단한 게임도 환경이 될 수 있다. 게임 만들기는 데이터 모으기와 달리 즐거운 일이니 마다할 필요가 없다. 첫번째로 도전으로 스네이크 게임을 골랐는데, 다들 한번쯤은 해봤을 것이다. 뱀을 조종해 먹이를 최대한 많이 먹으면 되는데, 뱀의 머리가 벽이나 아니면 자기 몸통에 부딪히면 죽는다. 여차여차 학습시켜서 이정도까지 하는데에는 성공했다. 강화 학습 지식이 일천해서 게임을 클리어하는 수준까지는 못 만들겠다. 이 글은 학습을 잘 시키는 방법이 아니라, 강화학습 코드를 어떻게 잘 짜느냐에 대한 것이다.<일단 강화 학습이란걸 형식화 해보자. 앞서 언급한 환경과 에이전트란 단어를 어떻게 정의할수 있을까? 먼저 에이전트의 정의는 이렇다.type Agent = Observation -> Action<관측 Observation 에 따라 행동 Action 을 선택한다. 나는 눈앞에 콜라가 보이면 마신다. 환경은 에이전트를 실행하는 무언가이다. 일상적인 표현으로 쓰자면, 에이전트를 둘러싼 무언가이다.runAgent :: Agent -> [Action]<이 runAgent 함수가 환경의 역할을 수행한다. Agent를 인자로 받아 죽을 때까지 선택한 행동들을 반환한다. 그런데 이건 너무 외연적인 정의고, 환경과 에이전트가 보통 만족할만한 조건을 나열해보자면 이렇다.환경은 상태를 가지고 있고 시간이 지남에 따라 변경된다상태는 에이전트가 무엇을 관측할지를 결정한다에이전트의 행동은 다음 상태에 영향을 끼친다<이 조건들을 바탕으로 환경을 좀더 구체적으로 정의하면 다음과 같다.class Environment e where type State e type Observation e type Action e update :: (State e, Action e) -> State e -- 조건 1, 3 observe :: State e -> Observation e -- 조건 2<스네이크 게임에서 상태는 뱀의 모양과 먹이의 위치, 관측은 상태와 같고(플레이어는 전체 게임 화면을 볼 수 있다), 행동은 좌회전과 우회전이 된다. 그런데 뱀이 아무렇게나 좌회전 우회전 한다고 박수 쳐줄순 없고, 우리는 얘한테 바라는 게 있다. 죽지않고 더 많은 먹이를 먹어야 한다. 이를 위해 뱀이 제때 방향을 바꿔서 먹이를 지나치지 않고 먹으면 잘 했다고 보상을 주자. 그러면 뱀은 보상을 더 많이 받을 방법을 학습한다.type RewardFunction = (Observation, Action) -> Float<보상 함수는 관측와 행동에 따라 보상을 결정한다. 가령 뱀이 먹이 하나를 냠냠하면 보상을 1 주면 된다. 지나쳐버리면 0점이고, 죽었을 때는 -1점을 줄 수도 있다. 당근과 채찍이라 생각하고 정의하면 된다. 또, 보상은 더해서 누적이 되어야 하므로 대충 Float으로 고른다. 학습을 시키려면 보상을 계산해야하고, 그러기 위해 관측과 행동을 짝지어야한다.trainAgent :: Agent -> [(Observation, Action)]<runAgent와 달리 Observation도 포함되어 있다. 이때 [(Observation, Action)]을 에피소드 Episode 라고 한다. 에피소드로부터 보상을 구하자.rewards = fmap (uncurry rewardFn) (trainAgent agent)rewardFn :: RewardFunctionagent :: Agent<...해치웠나? 에피소드가 어떻게 기록될지를 살펴보자.0123456789행동→↓→↓←↑→↓←↓보상🍎🍎🍎💀<위의 rewards의 값이 이런 의미일거라고 보인다. 실제로 Float 값을 표시해보자.0123456789행동→↓→↓←↑→↓←↓보상0010011000<혹시 이상한 걸 찾으셨나요? 이런식으로 보상하는게 틀린 건 아니다. 다만 에이전트가 장기적인 계획을 세우도록 학습시키지 못한다. 먹이를 먹는 순간에만 보상을 받기 때문에, 멀리 떨어진 먹이를 향해 다가가게끔 유도할 수가 없다. 이를 해결하려면 보상을 뒤에서부터 누적시켜야 한다.0123456789행동→↓→↓←↑→↓←↓보상3332221000rewards = scanr (+) 0 (fmap (uncurry rewardFn) (trainAgent agent))<코딩 테스트용 코드를 짜야할것 같은 느낌이 들었는데, 다행히 scanr 함수 덕분에 쉽게 해결했다. 좀더 개선해볼까. 지금의 보상 체계에서 에이전트는 먼 미래를 고려하며 선택하는 것을 배울 수 있다. 그런데, 사실 뱀이 맨 처음에 좌회전을 하던 우회전을 하던, 죽기 전까지 먹이를 총 몇개 먹는지에 엄청난 영향을 끼칠거 같진 않다. 어떤 시점에서의 선택의 영향은 시간이 지날 수록 점점 희미해진다. 이 점을 반영해서 뱀이 잘못된 편견을 갖지 않도록 도와주자. 미래의 보상을 누적하되, 감쇠율 0.9를 적용하는 것이다.0123456789행동→↓→↓←↑→↓←↓보상1.932.152.391.541.711.901.000.000.000.00rewards = scanr (\x y -> x + 0.9 * y) 0 (fmap (uncurry rewardFn) (trainAgent agent))<이제 나머지는 GPU한테 맡기면 된다. 조금만 기다리면 위의 동영상에서처럼 움직이는 뱀을 볼 수 있다.<여기서 GPU를 100장 더 사서 뱀 대신에 좀더 우리 삶에 도움되는 에이전트를 학습시킨다면, 그걸 10년전에 했으면, 난 지금 부자가 되어있을지도 모르겠다. 하지만 기회는 이미 떠났고, 난 지금 돈은 없지만 대신 시간은 많다. 그 시간을, 지금의 그럭저럭 볼만한 코드를 쥐꼬리만큼 개선하는데 낭비해보려 한다. 지금 코드에서 뭐가 마음에 안드냐면,<보상이 사실 서로 다른 두 개를 가리킨다 먹이를 먹자마자 즉각적으로 주는 보상과, 그것을 누적한 보상, 이렇게 두 개가 있다. 실제로 학습에 사용하는 것은 후자이다. 그런데 막상 보상 함수의 정의는 전자에 대한 것이다. 이는 보상 함수를 계산할 때 미래에 어떤 일이 일어나는지 알수가 없어서 그렇다. 정의를 두 개로 나눈 이유가 의미를 명쾌하게 하기 위해서가 아니라, 그냥 한번에 계산을 할수 없기 때문이다.<환경의 정의 위에서 살펴본 runAgent의 정의가 일견 우아해보일 수 있다. 문제는 그 정의는 환경과 에이전트가 모두 순수 함수일 것을 강요한다는 것이다. Environment의 정의도 마찬가지다. 환경과 에이전트는 각자의 부수효과 Side effect 를 가질 수 있어야 한다. 가령 온라인 게임을 환경으로 삼는다면, 환경은 네트워킹을 할 수 있어야 한다. 또, 에이전트는 매번 똑같은 선택이 아닌 확률적 선택을 하고 싶을텐데, 이는 순수 함수로써는 불가능하다.<그러면 부수효과를 허용하면 되는거 아냐?<맞다. 나처럼 시간이 많다면 직접 해보는 것도 나쁘지 않다. 하지만 정의가 점점 지저분해지는 것을 보게될 것이고... 시간을 아껴주기 위해 정답을 알려주겠다. 환경은 에이전트가 실행될 수 있는 모나드여야 한다. 딱 거기까지여야 한다. 환경과 에이전트 사이에 그 이상의 관계는 부적절하다.<글이 생각했던 것보다 길어져버려서, 오늘은 여기까지 해야겠다. 이어지는 글에서 거꾸로 상태 모나드가 이걸 어떻게 해결하는지 소개한다. 사실 아까 환경이니 에이전트니 어쩌고 할때부터, 진작에 강화 학습을 마스터한 학생들이 이미 다 아는 내용에 지겨워 졸기 시작하는게 보였다. 다음 수업은 재밌을테니, 대신 그때까지 모나드를 배워오세요.
hackers.pub · Hackers' Pub
Link author: bgl gwyng@bgl@hackers.pub
@basix 님이 알려주신 DaisyDisk를 깔고 UI 때깔부터 심상치않길래 바로 10달러 결제하고 디스크 정리를 시작했다.
각종 캐시들을30분간 열심히 지워서100GB+를 확보했는데, 근 한달동안 가장 보람차고 충만한 30분이었다ㅠㅠ
RE: https://hackers.pub/@basix/019665ed-f91c-7649-9ffd-0f460c440132
@bglbgl gwyng 이거 DaisyDisk로 잡으면 macOS 시스템에서 이상하게 리저빙해둔 것까지 잘 잡히더라고요. 전 타임머신이 오작동해서 디스크 반쪽짜리로 살다가 DaisyDisk로 감지해서 diskutil로 열심히 제거했습니다...
@basix 오 감사합니다. 잃어버린 내 용량 되찾으러 떠납니다.
@bglbgl gwyng 제가 항상 스토리지로 고생해서 이번에는 그냥 통 크게 2TB로 질렀습니다. Haskell, Rust 프로젝트 같은 것들은 의존성 조금만 생겨도 빌드 디렉터리가 스토리지 많이 차지하더라고요.
@hongminhee洪 民憙 (Hong Minhee) 고민해봐야겠습니다. 이게 실질 가용 용량이 < 200GB다 보니까 뭔가 (애먼) 애플한테 사기당한 기분이 듭니다ㅋㅋㅋ 아니 분명 400만원주고 500GB 모델을 샀는데!!
왜 맥북으로 개발을 하면 상시로 저장공간이 모자란 걸까요? 500GB 쓰는데 그렇습니다. OmniDisk를 가끔 돌려보는데 한 300GB정도가 어딘가 숨어있어요...
한 3~4일 관심 없음, 관심 없음 열심히 눌렀더니, 그 후로 개발 얘기만 70~80프로 뜨는 것 같아요.
@lionhairdino 아항 알고리즘을 적극적으로 길들여야하는군요