日本의 TypeScript 컨퍼런스인 TSKaigi 2026이 5月 22日(金)–23日(土)에 東京에서 開催된다고 합니다. 함께 가실 韓國 분 계실까요?
一旦 저랑 @2chanhaeng초무 님하고
@kodingwarriorJaeyeol Lee (a.k.a. kodingwarrior)
님이 같이 가실 것 같습니다.
@2chanhaeng@hackers.pub · 91 following · 80 followers
日本의 TypeScript 컨퍼런스인 TSKaigi 2026이 5月 22日(金)–23日(土)에 東京에서 開催된다고 합니다. 함께 가실 韓國 분 계실까요?
一旦 저랑 @2chanhaeng초무 님하고
@kodingwarriorJaeyeol Lee (a.k.a. kodingwarrior)
님이 같이 가실 것 같습니다.
내가 보려고 번역해둔 Functional Programming in Lean 타래
초무 @2chanhaeng@hackers.pub
Note
이 글은 Lean 공식 문서에 소개된 Functional Programming in Lean을 작성자가 읽기 위한 목적으로 Kagi Translate를 통해 1차로 기계 번역 후 보완한 글입니다. 원문과의 차이가 있을 수 있으며 정식 내용은 항상 원문을 통해 확인 바랍니다. 원문이 Creative Commons Attribution 4.0 International License를 기반으로 배포된 것을 존중하여 이 글 또한 CC BY 4.0 하에 배포합니다.
Lean은 의존 타입 이론을 기반으로 하는 대화형 정리 증명기 입니다. 원래 Microsoft Research에서 개발되었으나, 현재는 Lean FRO에서 개발이 진행되고 있습니다. 의존 타입 이론은 프로그램과 증명의 세계를 하나로 묶어주기 때문에 Lean은 프로그래밍 언어이기도 합니다. Lean은 이러한 이중적 특성을 진지하게 받아들이며, 범용 프로그래밍 언어로 사용하기에 적합하도록 설계되었습니다. 심지어 Lean은 Lean 자체로 구현되어 있습니다. 이 책은 Lean으로 프로그램을 작성하는 법에 관한 것입니다.
프로그래밍 언어로서의 Lean은 의존 타입을 가진 엄격한 순수 함수형 언어입니다. Lean으로 프로그래밍하는 법을 배우는 과정의 상당 부분은 이러한 각 속성이 프로그램 작성 방식에 어떤 영향을 미치는지, 그리고 어떻게 함수형 프로그래머처럼 생각할 수 있는지를 익히는 것으로 구성됩니다. 엄격성 은 Lean의 함수 호출이 대부분의 언어와 유사하게 작동하는 것을 의미합니다. 즉, 함수의 본문이 실행되기 전에 인자값이 완전히 계산된다는 의미입니다. 순수성 은 프로그램에서 타입에 명시되지 않는 한, Lean 프로그램이 메모리 위치 수정, 이메일 전송, 파일 삭제와 같은 부수 효과(side effect)를 가질 수 없음을 의미합니다. Lean이 함수형 언어라는 것은 함수가 다른 값들과 마찬가지로 일급 객체이며, 실행 모델이 수학적 표현식의 평가에서 영감을 얻었음을 뜻합니다. Lean의 가장 독특한 특징인 의존 타입은 타입을 언어의 일급 구성 요소로 만들어, 타입이 프로그램을 포함하거나 프로그램이 타입을 계산할 수 있게 합니다.
이 책은 Lean을 배우고 싶지만 반드시 함수형 프로그래밍 언어를 사용해 본 적은 없는 프로그래머들을 대상으로 합니다. Haskell, OCaml, F#과 같은 함수형 언어에 익숙할 필요는 없습니다. 반면, 이 책은 대부분의 프로그래밍 언어에서 공통적으로 쓰이는 루프, 함수, 데이터 구조와 같은 개념에 대한 지식은 갖추고 있다고 가정합니다. 이 책이 함수형 프로그래밍에 관한 좋은 입문서가 될 수는 있지만, 프로그래밍 전반에 관한 입문서로는 적합하지 않습니다.
Lean을 증명 보조기로 사용하는 수학자들도 언젠가는 맞춤형 증명 자동화 도구를 작성해야 할 때가 올 것입니다. 이 책은 그분들을 위한 것이기도 합니다. 이러한 도구들이 정교해질수록 함수형 언어의 프로그램과 닮아가지만, 대부분의 현직 수학자들은 Python이나 Mathematica 같은 언어에 익숙합니다. 이 책은 그 간극을 메워줌으로써 더 많은 수학자가 유지보수 가능하고 이해하기 쉬운 증명 자동화 도구를 작성할 수 있도록 도울 것입니다.
이 책은 처음부터 끝까지 순서대로 읽도록 구성되었습니다. 개념은 하나씩 도입되며, 뒷부분은 앞부분의 내용을 숙지하고 있다고 가정합니다. 때로는 앞서 짧게 다루었던 주제를 나중에 더 깊이 있게 파고들기도 합니다. 책의 일부 섹션에는 연습 문제가 포함되어 있습니다. 해당 섹션의 이해를 공고히 하기 위해 풀어볼 가치가 있습니다. 또한 책을 읽으면서 배운 내용을 창의적이고 새로운 방식으로 활용하며 Lean을 직접 탐구해 보는 것도 유익합니다.
Lean으로 작성된 프로그램을 작성하고 실행하기 전에, 먼저 컴퓨터에 Lean을 설치해야 합니다. Lean 도구 모음은 다음과 같이 구성됩니다:
elan은 rustup이나 ghcup과 유사하게 Lean 컴파일러 툴체인을 관리합니다.lake는 cargo, make, 또는 Gradle과 유사하게 Lean 패키지와 그 의존성을 빌드합니다.lean은 개별 Lean 파일의 타입을 검사하고 컴파일하며, 현재 작성 중인 파일에 대한 정보를 프로그래머 도구에 제공합니다.
보통 lean은 사용자가 직접 실행하기보다는 다른 도구에 의해 호출됩니다.lean과 통신하여 관련 정보를 편리하게 보여줍니다.Lean 설치에 관한 최신 지침은 Lean 매뉴얼을 참조하시기 바랍니다.
Lean에 입력으로 제공되는 코드 예제는 다음과 같은 형식으로 표시됩니다:
def add1 (n : Nat) : Nat := n + 1
#eval add1 7
위의 마지막 줄(#eval로 시작하는 줄)은 Lean에게 답을 계산하도록 지시하는 명령입니다.
Lean의 응답은 다음과 같은 형식으로 표시됩니다:
8
Lean이 반환하는 오류 메시지는 다음과 같은 형식으로 표시됩니다:
Application type mismatch: The argument
"seven"
has type
String
but is expected to have type
Nat
in the application
add1 "seven"
경고는 다음과 같은 형식으로 표시됩니다:
declaration uses 'sorry'
관용적인 Lean 코드는 ASCII에 포함되지 않는 다양한 유니코드 문자를 사용합니다.
예를 들어, 그리스 문자 α, β 및 화살표 →는 모두 이 책의 첫 번째 장에 등장합니다.
이를 통해 Lean 코드는 일반적인 수학적 표기법과 더 유사해질 수 있습니다.
기본 Lean 설정에서 Visual Studio Code와 Emacs 모두 백슬래시(\) 뒤에 이름을 입력하여 이러한 문자를 입력할 수 있습니다.
예를 들어, α를 입력하려면 \alpha를 입력합니다.
Visual Studio Code에서 문자를 입력하는 방법을 알아보려면 해당 문자 위에 마우스를 올려 툴팁을 확인하세요.
Emacs에서는 해당 문자 위에 커서를 두고 C-c C-k를 사용하세요.
David Thrane Christiansen 은 20년 동안 함수형 언어를 사용해 왔으며, 의존 타입을 사용한 지는 10년이 되었습니다. 그는 Daniel P. Friedman과 함께 의존 자료형 이론의 핵심 아이디어를 소개하는 The Little Typer 를 집필했습니다. 코펜하겐 IT 대학교에서 박사 학위를 받았으며, 재학 시절 Idris 언어의 첫 번째 버전에 주요 기여자로 참여했습니다. 학계를 떠난 후에는 미국 오리건주 포틀랜드의 Galois와 덴마크 코펜하겐의 Deon Digital에서 소프트웨어 개발자로 일했으며, Haskell Foundation의 상임 이사를 역임했습니다. 집필 당시 그는 Lean Focused Research Organization 에 소속되어 풀타임으로 Lean 프로젝트에 매진하고 있습니다.
이 무료 온라인 도서는 Microsoft Research의 아낌없는 지원 덕분에 집필 및 무료 배포가 가능했습니다. 집필 과정에서 그들은 Lean 개발팀의 전문 지식을 제공하여 제 질문에 답해주고 Lean을 더 사용하기 쉽게 만들어 주었습니다. 특히 Leonardo de Moura는 이 프로젝트를 시작하고 제가 첫발을 뗄 수 있게 도와주었으며, Chris Lovett은 CI 및 배포 자동화를 구축하고 테스트 독자로서 훌륭한 피드백을 주었습니다. Gabriel Ebner는 기술 검토를 맡았고, Sarah Smith는 행정적인 업무가 원활히 돌아가도록 힘써주었으며, Vanessa Rodriguez는 소스 코드 하이라이트 라이브러리와 특정 버전의 iOS용 Safari 간의 까다로운 상호작용 문제를 진단하는 데 도움을 주었습니다.
이 책을 쓰는 데는 평소 근무 시간 외에도 많은 시간이 소요되었습니다. 제 아내 Ellie Thrane Christiansen은 평소보다 더 많은 가사 분담을 도맡아 주었으며, 아내의 헌신이 없었다면 이 책은 존재할 수 없었을 것입니다. 매주 하루씩 추가로 일하는 것이 가족들에게 쉽지 않은 일이었음에도, 집필 기간 동안 인내심을 갖고 지지해 준 가족들에게 감사의 마음을 전합니다.
Lean을 둘러싼 온라인 커뮤니티는 기술적으로나 정서적으로 이 프로젝트에 열렬한 지지를 보내주었습니다. 특히 Sebastian Ullrich는 에러 메시지 텍스트를 CI에서 확인하고 책에 쉽게 포함할 수 있도록 지원 코드를 작성할 때, 제가 Lean의 메타프로그래밍 시스템을 익히는 데 핵심적인 도움을 주었습니다. 새로운 개정판을 게시한 지 불과 몇 시간 만에 열정적인 독자들이 오류를 찾아내고, 제안을 건네며, 따뜻한 격려를 보내주었습니다. 특히 문체와 기술적인 면에서 많은 제안을 주신 Arien Malec, Asta Halkjær From, Bulhwi Cha, Craig Stuntz, Daniel Fabian, Evgenia Karunus, eyelash, Floris van Doorn, František Silváši, Henrik Böving, Ian Young, Jeremy Salwen, Jireh Loreaux, Kevin Buzzard, Lars Ericson, Liu Yuxi, Mac Malone, Malcolm Langfield, Mario Carneiro, Newell Jensen, Patrick Massot, Paul Chisholm, Pietro Monticone, Tomas Puverle, Yaël Dillies, Zhiyuan Bao, 그리고 Zyad Hassan에게 감사의 인사를 전하고 싶습니다.
졸려서 이만 자러 갑니다 안녕히 주무시고 좋은 꿈 꾸세요 고양이 사진 투척
Redesigned my personal website (https://ji.hyeok.org) using the design from my name card! The name card itself is designed by
@hongminhee洪 民憙 (Hong Minhee).
The physical name card looks like this.
Redesigned my personal website (https://ji.hyeok.org) using the design from my name card! The name card itself is designed by
@hongminhee洪 民憙 (Hong Minhee).
https://dohyeon.dev/building-markdown-editor
음. code-mirror로 마크다운 에디터 한번 만들어볼까
Just shouted out some important things to come like working with
@anewsocial and
@bsky.brid.gyBridgy Fed for Bluesky and with @swf on encryption
Now we have
@hongminhee洪 民憙 (Hong Minhee)
presenting @fedifyFedify: ActivityPub server framework!
@fedifyFedify: ActivityPub server framework abstracts away complexity in building for the fediverse
개발자들이 환장하는 개못생긴 프로그래밍 마스코트 실존
Drinking coffee around Stalingrad kiez Brussels with
@hongminhee洪 民憙 (Hong Minhee)
and @2chanhaeng초무!
Wow so while I was explaining how I got into the fediverse to
@hongminhee洪 民憙 (Hong Minhee)
and @2chanhaeng초무 the tv in this cafe started talking about
@peertube on #France24
최고의 말랑이 최고의 말랑이
세기의 대결중인 우리 멍구 강아지 보여줄께
어째서 다들 노동자가 아니라 사용자에게 이입하는 걸까…?
페디버스에 이런 웹게임적 요소를 넣으면 어떨까...? 이미 몇 개 있을 것 같기도 한데
https://bsky.app/profile/yamyo.bsky.social/post/3mcz5srwsnc22
오 아예 모음집이 있네
요즘 CPU는 회로가 너무 밀도가 높아져서 양자역학적 요소까지 고려해야 한다는게 신기했슴
예전에는 CPU 버그가 드물었지만, 최근에는 복잡성의 증가로 많이 흔해졌다는 이야기로 시작하는 CPU 버그에 대한 좋은 마스토돈 포스팅. 버그가 어떤 식으로 발생하고 여기에 어떤 식으로 준비/대처하는지에 대해 자세히 이야기를 해준다. 습관적으로 마스토돈에 들어갔다가 상당히 흥미롭고 깊이있는 이야기를 발견해서 기뻤다. 주소는 여기
예전에 lwn.net 에서 본 What every programmer should know about memory가 생각났다. Static RAM 과 Dynamic RAM 의 속도차이는 왜 어떻게 발생하는 것일까에 대해 아주 오랫동안 궁금해하던 것을, 회로도를 통해서 직관적으로 이해할 수 있게 해줘서 굉장히 (...인생에서 가장 기뻤던 순간 중 손에 꼽을 정도였다.) 기뻤던 기억이 아직도 난다.
로우레벨 컴퓨팅에 대한 이야기는 내 정신 저 밑바닥에 있는 "아니 그러니까 왜냐고?!" 의 욕망을 크게 해결해주는 경향이 있어서 좋아한다.
그리고 마스토돈의 기본 웹 클라이언트는 브라우저들이 번역을 정말 잘 못한다-_-; 웹페이지의 기본 언어가 브라우저 언어로 맞추어져 보여지고, 그래서 번역할 필요가 없다고 브라우저가 판단해서 그런 것 같다고 이해하고 있는데 이 문제 어떻게 해결할 방법이 없을까 흠 ' -' ...
침 흘리는 고양이 보여줄게
페디버스에 이런 웹게임적 요소를 넣으면 어떨까...? 이미 몇 개 있을 것 같기도 한데
https://bsky.app/profile/yamyo.bsky.social/post/3mcz5srwsnc22
ioriについて
ActivityPubを部分的にサポートするioriは、自分のためのナレッジ管理サービスとして開発した。
現代では様々なブログサービスやナレッジ共有サービスが存在するが、どれもいつか滅びてしまうリスクを抱えている。
それは仕方がないことだが、自分が得た知識や情報がある日突然アクセスできなくなるのは避けたい。
そこで、ioriでは自分で情報のフローをコントロールできることを重視し、ActivityPubを通じて自由な形式でナレッジが共有できるように設計した。
特定のサービスのレコメンド機能に依存せず、読者が自分の好きなサービスから情報を取得できることを目指している。
I'll be presenting @fedifyFedify: ActivityPub server framework at
@fosdem 2026! My talk Fedify: Building ActivityPub servers without the pain was accepted for the Social Web Devroom. See you in Brussels on January 31–February 1!
경찰에서 연락 받았습니다. 실종 8시간만에 신병확보 했다고 합니다.
무슨 일이 있었는지는 알 수 없습니다만, 현재 안전 확보된 상태라고 합니다.
걱정해주신 분들 정말 감사드립니다.
최근 그냥 코딩 에이전트에게 코딩을 넘어서 삶의 귀찮은 자동화나 일들을 대신 해주는 느낌으로도 쓸 수 있지 않을까 싶어서, 자는 동안 남은 토큰 한도로 epub 파일로 된 소설을 번역시켰더니 꽤 그럴싸한 수준의 번역이 나와서 놀랐다. 그래서 이건 좀 잘 만들어두면 굉장히 편하겠다 싶어서 pdf 파일과 epub 파일을 번역하는걸 Agent Skills로 만들어보았다.
혼나는 빠루 네컷만화 무력하게 고영을 놓치는 엄무의 저 손이 너무 웃김
내가 일반 리눅스 쓴 경험으로 이런 이야기도 썼는데! 아! 망각하는 인간에게는 고통이 반복될 뿐이다! 허나 우리 모두는 망각하지 않는가?! 우리는 모두 고통 받는다!
#발광
RE: https://bsky.app/profile/did:plc:de27rm6eyuf5ez6gmvjdmilq/post/3mcor36wxxk2x
뱀 아가들 자랑하기
졸려서 이만 자러 갑니다 안녕히 주무시고 좋은 꿈 꾸세요 고양이 사진 투척
보송보송 동글동글 고로로롱
인강들을때 고양이 맨날 뒤에서 이케 막 드르렁 뻐드렁 퍼질러 자고있슨...-
차별금지법안이 제안됨과 동시에 격렬한 반대 메시지가 쌓이고 있네요.
부디 열화와 같은 성원으로 찬성해주시기 부탁드립니다
https://pal.assembly.go.kr/napal/lgsltpa/lgsltpaOpn/list.do?menuNo=&lgsltPaId=PRC_O2N5O1M1M1V9T1T1S0S9R5R9Z1A1Y3&searchConClosed=0&refererDiv=S
놀아달라고 하고 싶은데 바닥이 너무 따끈한 상황.
[속보] 李대통령 "재일동포, 계엄 때 민주주의 함께 지켜…존경과 감사"
송고2026-01-14 12:36
www.yna.co.kr/view/AKR2026...
[속보] 李대통령 "재일동포, 계엄 때 민주주의 함께 ...
공익글: VS Code에서 작업하던 파일이 유실됐다면 가장 먼저 Timeline부터 확인합시다. 바로 오늘도 이 방법으로 삭제된 코드를 복구한 지인분이 계시며...
폴란드 대통령 사진 담당자가 영국 총리실 쥐잡이 보좌관 래리한테 걸려넘어졌다고. 뒤에 있는 사람이 폴란드 대통령...
My team is hiring! https://careers.theori.io/ko/o/198152
초무 shared the below article:
洪 民憙 (Hong Minhee) @hongminhee@hackers.pub
Consider Git's -C option:
git -C /path/to/repo checkout <TAB>
When you hit Tab, Git completes branch names from /path/to/repo, not your
current directory. The completion is context-aware—it depends on the value of
another option.
Most CLI parsers can't do this. They treat each option in isolation, so
completion for --branch has no way of knowing the --repo value. You end up
with two unpleasant choices: either show completions for all possible
branches across all repositories (useless), or give up on completion entirely
for these options.
Optique 0.10.0 introduces a dependency system that solves this problem while preserving full type safety.
or() Optique already handles certain kinds of dependent options via the or()
combinator:
import { flag, object, option, or, string } from "@optique/core";
const outputOptions = or(
object({
json: flag("--json"),
pretty: flag("--pretty"),
}),
object({
csv: flag("--csv"),
delimiter: option("--delimiter", string()),
}),
);
TypeScript knows that if json is true, you'll have a pretty field, and if
csv is true, you'll have a delimiter field. The parser enforces this at
runtime, and shell completion will suggest --pretty only when --json is
present.
This works well when the valid combinations are known at definition time. But it can't handle cases where valid values depend on runtime input—like branch names that vary by repository.
Common scenarios include:
--environment affects which services are available--connection affects which tables can be completed--project affects which resources are shownIn each case, you can't know the valid values until you know what the user
typed for the dependency option. Optique 0.10.0 introduces dependency() and
derive() to handle exactly this.
The core idea is simple: mark one option as a dependency source, then create derived parsers that use its value.
import {
choice,
dependency,
message,
object,
option,
string,
} from "@optique/core";
function getRefsFromRepo(repoPath: string): string[] {
// In real code, this would read from the Git repository
return ["main", "develop", "feature/login"];
}
// Mark as a dependency source
const repoParser = dependency(string());
// Create a derived parser
const refParser = repoParser.derive({
metavar: "REF",
factory: (repoPath) => {
const refs = getRefsFromRepo(repoPath);
return choice(refs);
},
defaultValue: () => ".",
});
const parser = object({
repo: option("--repo", repoParser, {
description: message`Path to the repository`,
}),
ref: option("--ref", refParser, {
description: message`Git reference`,
}),
});
The factory function is where the dependency gets resolved. It receives the
actual value the user provided for --repo and returns a parser that validates
against refs from that specific repository.
Under the hood, Optique uses a three-phase parsing strategy:
This means both validation and completion work correctly—if the user has
already typed --repo /some/path, the --ref completion will show refs from
that path.
@optique/git The @optique/git package provides async value parsers that read from Git
repositories. Combined with the dependency system, you can build CLIs with
repository-aware completion:
import {
command,
dependency,
message,
object,
option,
string,
} from "@optique/core";
import { gitBranch } from "@optique/git";
const repoParser = dependency(string());
const branchParser = repoParser.deriveAsync({
metavar: "BRANCH",
factory: (repoPath) => gitBranch({ dir: repoPath }),
defaultValue: () => ".",
});
const checkout = command(
"checkout",
object({
repo: option("--repo", repoParser, {
description: message`Path to the repository`,
}),
branch: option("--branch", branchParser, {
description: message`Branch to checkout`,
}),
}),
);
Now when you type my-cli checkout --repo /path/to/project --branch <TAB>, the
completion will show branches from /path/to/project. The defaultValue of
"." means that if --repo isn't specified, it falls back to the current
directory.
Sometimes a parser needs values from multiple options. The deriveFrom()
function handles this:
import {
choice,
dependency,
deriveFrom,
message,
object,
option,
} from "@optique/core";
function getAvailableServices(env: string, region: string): string[] {
return [`${env}-api-${region}`, `${env}-web-${region}`];
}
const envParser = dependency(choice(["dev", "staging", "prod"] as const));
const regionParser = dependency(choice(["us-east", "eu-west"] as const));
const serviceParser = deriveFrom({
dependencies: [envParser, regionParser] as const,
metavar: "SERVICE",
factory: (env, region) => {
const services = getAvailableServices(env, region);
return choice(services);
},
defaultValues: () => ["dev", "us-east"] as const,
});
const parser = object({
env: option("--env", envParser, {
description: message`Deployment environment`,
}),
region: option("--region", regionParser, {
description: message`Cloud region`,
}),
service: option("--service", serviceParser, {
description: message`Service to deploy`,
}),
});
The factory receives values in the same order as the dependency array. If
some dependencies aren't provided, Optique uses the defaultValues.
Real-world dependency resolution often involves I/O—reading from Git repositories, querying APIs, accessing databases. Optique provides async variants for these cases:
import { dependency, string } from "@optique/core";
import { gitBranch } from "@optique/git";
const repoParser = dependency(string());
const branchParser = repoParser.deriveAsync({
metavar: "BRANCH",
factory: (repoPath) => gitBranch({ dir: repoPath }),
defaultValue: () => ".",
});
The @optique/git package uses isomorphic-git under the hood, so
gitBranch(), gitTag(), and gitRef() all work in both Node.js and Deno.
There's also deriveSync() for when you need to be explicit about synchronous
behavior, and deriveFromAsync() for multiple async dependencies.
The dependency system lets you build CLIs where options are aware of each other—not just for validation, but for shell completion too. You get type safety throughout: TypeScript knows the relationship between your dependency sources and derived parsers, and invalid combinations are caught at compile time.
This is particularly useful for tools that interact with external systems where the set of valid values isn't known until runtime. Git repositories, cloud providers, databases, container registries—anywhere the completion choices depend on context the user has already provided.
This feature will be available in Optique 0.10.0. To try the pre-release:
deno add jsr:@optique/core@0.10.0-dev.311
Or with npm:
npm install @optique/core@0.10.0-dev.311
See the documentation for more details.
LLM에서 마크다운이 널리 쓰이게 되면서 안 보고 싶어도 볼 수 밖에 없게 된 흔한 꼬라지로 그림에서 보는 것처럼 마크다운 강조 표시(**)가 그대로 노출되어 버리는 광경이 있다. 이 문제는 CommonMark의 고질적인 문제로, 한 10년 전쯤에 보고한 적도 있는데 지금까지 어떤 해결책도 제시되지 않은 채로 방치되어 있다.
문제의 상세는 이러하다. CommonMark는 마크다운을 표준화하는 과정에서 파싱의 복잡도를 제한하기 위해 연속된 구분자(delimiter run)라는 개념을 넣었는데, 연속된 구분자는 어느 방향에 있느냐에 따라서 왼편(left-flanking)과 오른편(right-flanking)이라는 속성을 가질 수 있다(왼편이자 오른편일 수도 있고, 둘 다 아닐 수도 있다). 이 규칙에 따르면 **는 왼편의 연속된 구분자로부터 시작해서 오른편의 연속된 구분자로 끝나야만 한다. 여기서 중요한 건 왼편인지 오른편인지를 판단하는 데 외부 맥락이 전혀 안 들어가고 주변의 몇 글자만 보고 바로 결정된다는 것인데, 이를테면 왼편의 연속된 구분자는 **<보통 글자> 꼴이거나 <공백>**<기호> 또는 <기호>**<기호> 꼴이어야 한다. ("보통 글자"란 공백이나 기호가 아닌 글자를 가리킨다.) 첫번째 꼴은 아무래도 **마크다운**은 같이 낱말 안에 끼어 들어가 있는 연속된 구분자를 허용하기 위한 것이고, 두번째/세번째 꼴은 이 **"마크다운"** 형식은 같이 기호 앞에 붙어 있는 연속된 구분자를 제한적으로 허용하기 위한 것이라 해석할 수 있겠다. 오른편도 방향만 다르고 똑같은 규칙을 가지는데, 이 규칙으로 **마크다운(Markdown)**은을 해석해 보면 뒷쪽 **의 앞에는 기호가 들어 있으므로 뒤에는 공백이나 기호가 나와야 하지만 보통 글자가 나왔으므로 오른편이 아니라고 해석되어 강조의 끝으로 처리되지 않는 것이다.
CommonMark 명세에서도 설명되어 있지만, 이 규칙의 원 의도는 **이런 **식으로** 중첩되어** 강조된 문법을 허용하기 위한 것이다. 강조를 한답시고 **이런 ** 식으로 공백을 강조 문법 안쪽에 끼워 넣는 일이 일반적으로는 없으므로, 이런 상황에서 공백에 인접한 강조 문법은 항상 특정 방향에만 올 수 있다고 선언하는 것으로 모호함을 해소하는 것이다. 허나 CJK 환경에서는 공백이 아예 없거나 공백이 있어도 한국어처럼 낱말 안에서 기호를 쓰는 경우가 드물지 않기 때문에, 이런 식으로 어느 연속된 구분자가 왼편인지 오른편인지 추론하는 데 한계가 있다는 것이다. 단순히 <보통 문자>**<기호>도 왼편으로 해석하는 식으로 해서 **마크다운(Markdown)**은 같은 걸 허용한다 하더라도, このような**[状況](...)**は 이런 상황은 어쩔 것인가? 내가 느끼기에는 중첩되어 강조된 문법의 효용은 제한적인 반면 이로 인해 생기는 CJK 환경에서의 불편함은 명확하다. 그리고 LLM은 CommonMark의 설계 의도 따위는 고려하지 않고 실제 사람들이 사용할 법한 식으로 마크다운을 쓰기 때문에, 사람들이 막연하게 가지고만 있던 이런 불편함이 그대로 표면화되어 버린 것이고 말이다.
지금 이시간 가내 털공주
길가다 본 벨벳질감 고양이