
Ailrun (UTC-5/-4)
@ailrun@hackers.pub · 2 following · 18 followers
소개 - Who Am I?
- 개발자/연구원
- Haskell Language Server Admin
- PL Theorist
- Logician
- 중증 맥덕
근황 - Recent Interests
- 언어간 상호운용성 (Interoperability) 연구 중
- 의존적 형이론 (dependent type theory) 연구 중
- 효율적인 LaTeX Typesetting 공부 중
- Québec 에서 제일 맛있는 Stout 마시는 중
GitHub Page
- ailrun.github.io/ko
GitHub
- @Ailrun
@bglbgl gwyng
@kodingwarriorJaeyeol Lee
@lionhairdino
제 심상이랑은 좀 다르네요. 저는 H의 중간 막대가 좀 아래로, H가 좌우로 살짝 벌어져있고 P가 좀 더 바짝 붙은 형태를 상상했습니다. Inkscape 켜서 직접 그리기에는 졸리네요 😅
슬슬 해커스펍 svg 로고를 찾을 때가 오고 있습니다.
@lionhairdino 딱 떠오르는 건 HP를 붙여서 맥주잔 모양으로 만드는 거네요
책[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》 ↩︎
@curry박준규 짧을 때는 길이가 chunk size의 배수냐가 중요할 겁니다. 길 때는 평균적인 성능이 나오겠지만요.
Yacc와 같은 파서 제네레이터에 BNF를 넣으면 파서 코드가 자동으로 생성된다. 그런데 HTTP나 ActivityPub 등의 프로토콜 스펙을 입력으로 넣으면 자동으로 코드를 구현해주는 도구 어디 없나?
@curry박준규 구문론(Grammar theory)에 대한 이해도와 규약론(Protocol theory)에 대한 이해도가 차원이 달라서... 구문론 수준의 도구를 만들기는 어렵죠.
CBPV가 핫하다보니 다뤄야 할 것이 가지치기로 막 늘어난다. 어떻게하면 핵심만 다룰 수 있을까...
아이폰 사파리 가로 스크롤 원인
@lionhairdino 지금 보니까 Graphviz 이미지도 그렇네요. 이미지가 모두 그런 걸까요 아니면 Graphviz만 그런 걸까요
아이폰 사파리 가로 스크롤 원인
@lionhairdino 다른 브라우저에서도 몇몇 긴 이름들이 가로로 스크롤을 하게 만들더라고요
@ailrunAilrun (UTC-5/-4) 맞아요. 사실 저는 줄바꿈 문자라고 부르는 것보다 줄끝 문자라고 불러야 하지 않나 싶습니다.
@hongminhee洪 民憙 (Hong Minhee) 말씀대로 "줄끝 문자" 혹은 "줄마침 문자"가 더 직관적인 이름일 수도 있겠습니다.
협업을 할 때 미묘하게 거슬리는 것들
- 텍스트 파일의 마지막 줄 완성 안 하기 Posix 표준 기준으로 각 줄은 새줄 문자(line feed, '\n')로 끝나야 하며, 마지막 줄도 예외는 아님
- 그리고 위의 일이 주기적으로 재발할 때
다음 글은 CBPV에 대해서 써볼까 합니다. CBV(Call-By-Value)나 CBN(Call-By-Name)은 전공자라면 한번쯤은 들어봤을 이름이지만, CBPV는 특정 분야 석박사가 아니면 들어본 적 없을 것 같네요. 하지만 실제 컴파일러 구현(GHC지만...)도 논의되고 있는 만큼 앞으로 유명해지지 않을까 싶어 미리 다뤄보려고 합니다.
어이쿠, CBPV가 무엇의 약자인지 써놓지를 않았네요. Call-By-Push-Value의 약자입니다. "Push"가 들어가는 걸로 CBV랑은 완전히 다른 것이 되는데요, 어찌 다른지는 글에서 다뤄보도록 하겠습니다.
다음 글은 CBPV에 대해서 써볼까 합니다. CBV(Call-By-Value)나 CBN(Call-By-Name)은 전공자라면 한번쯤은 들어봤을 이름이지만, CBPV는 특정 분야 석박사가 아니면 들어본 적 없을 것 같네요. 하지만 실제 컴파일러 구현(GHC지만...)도 논의되고 있는 만큼 앞으로 유명해지지 않을까 싶어 미리 다뤄보려고 합니다.
@lionhairdino SKI도 잘 안쓰이는데 그보다 심한 Jot은 안 쓰이지 않을까요
@ailrunAilrun (UTC-5/-4) 아, 단.무.지!로 잘 해결하는 사람 못이깁니다. ㅋㅋ
@lionhairdino "잘"인지는 모르겠습니다만 다른 것도 크게 도움은 안 되더라구요 ;;
브라우저 탭 라이프
사이드 탭
트리 탭
탭 잠그기
탭 배경색 바꾸기
탭 타이틀 바꾸기 및 고정
다 설치했다니, 이제사 탭 뭉치에서 방황하는 시간이 좀 줄어드나 싶네요.
가상 데스크탑으로 구분, 브라우저 창으로 구분, 그런데도 한 브라우저당 탭을 몇 십개씩 열어 놓습니다. 마음의 여유가 없어지니, 한 주제를 오래 못 보나 싶습니다. 언제 바꾸긴 해야 할 것 같은 탭 라이프인데, 외부 요인이 생겨야 가능할 것 같습니다.
@lionhairdino 저는 그냥 30개고 40개고 켜놓고 탭 검색 기능으로 버팁니다 ㅎㅎ
@ailrunAilrun (UTC-5/-4) 낱말 선택이 정갈합니다! 저는 구어체로 주로 글을 올리다보니 뒤죽 박죽 낱말 선택을 하는데요.
@lionhairdino 어이쿠, 그리 읽힌다니 다행입니다. 반복되는 접속어도 있고 해서 부끄럽습니다만...
@ailrunAilrun (UTC-5/-4) 재밌게 잘 읽었습니다. 걱정하신 것과 다르게 저에게는 쓰신 글이 술술(?) 잘 읽혔어요. 이제 다음 글은 Dependent Haskell인가요!
@curry박준규 Dependent Haskell은 흥미로운 내용을 다루기에는 아직 너무 갈 길이 멀고... 그렇다고 동기만 다루자니 좀 심심하네요. 아마 Agda나 Coq등을 조금 다뤄보지 않을까 싶습니다. 관련 내용으로 Dependent Haskell을 좀 언급할 수는 있겠습니다.
같은 것과 같지 않은 것에 대해서 짤막한 글을 써 보았습니다. 오랜만에 국어로 쓰는 글이라 잘 읽힐지 모르겠네요.
같은 것을 알아내는 방법

Ailrun (UTC-5/-4) @ailrun@hackers.pub
같은 것과 같지 않은 것
국밥 두 그릇의 가격이 얼마인가? KTX의 속력이 몇 km/h인가? 내일 기온은 몇 도인가? 일상에서 묻는 이런 질문은 항상 같음의 개념을 암시적으로 사용하고 있다. 앞의 예시를 보다 명시적으로 바꾼다면 아래와 같이 (다소 어색하게) 말할 수 있다.
- 국밥 두 그릇의 가격은 몇 원과 같은가?
- KTX의 속력은 몇 km/h와 같은가?
- 내일 기온은 몇 도와 같은가?
이런 질문들의 추상화인 이론들은 자연스럽게 언제 무엇과 무엇이 같은지에 대해서 답하는 데에 초점을 맞추게 된다. 예를 들면
- x2+x+1=0x^2 + x + 1 = 0의 실수 해의 갯수는 0과 같다.
- 물 분자 내의 수소-산소 연결 사이의 각도는 104.5도와 같다.
- 합병 정렬의 시간 복잡도는 O(nlogn)O(n\log{n})과 같다.
등이 있다. 이렇게 어떤 두 대상이 같은지에 대해서 이야기를 하다보면 반대로 어떤 두 대상이 같지 않은지에 대해서도 이야기하게 된다. 즉,
- x+4x + 4를 22로 나눈 나머지는 x+1x + 1을 22로 나눈 나머지와 같지 않다.
- 연결 리스트(Linked List)와 배열(Array)은 같지 않다.
- 함수 λ x→x\lambda\ x \to x와 정수 55는 같지 않다.
같은 것과 판정 문제(Decision Problem)
이제 컴퓨터 과학(Computer Science)과 프로그래밍(Programming)에 있어 자연스러운 의문은 "두 대상이 같은지 아닌지와 같은 답을 주는 알고리즘(Algorithm)이 있나?"일 것이다. 다시 말해서 두 대상 aa와 bb를 입력으로 주었을 때
- 알고리즘이 참 값(True\mathtt{True})을 준다면 aa와 bb가 같고
- 알고리즘이 거짓 값(False\mathtt{False})을 준다면 aa와 bb가 같지 않은
알고리즘이 있는지 물어볼 수 있다. 이런 어떤 명제가 참인지 거짓인지 판정하는 알고리즘의 존재 여부에 대한 질문을 "판정 문제"("Decision Problem")라고 하며, 명제 PP에 대한 판정 문제에서 설명하는 알고리즘이 존재한다면 "PP는 판정 가능하다"("PP is decidable")고 한다. 즉, 앞의 질문은 "임의의 aa와 bb에 대해 aa와 bb가 같은지 판정 가능한가?"라는 질문과 같은 의미라고 할 수 있다.
이 질문에 대한 대답은 당연하게도 어떤 대상을 어떻게 비교하는지에 따라 달라진다. 예를 들어 우리가 32 비트(bit) 정수에 대해서만 이야기하고 있다면 "임의의 32 비트 정수 aa와 bb에 대해 aa와 bb가 각 비트별로 같은지 판정 가능한가?"라는 질문에 대한 답은 "그렇다"이다. 반면 우리가 비슷한 질문을 자연수를 받아 자연수를 내놓는 임의의 함수에 대해 던진다면 답은 "아니다"가 된다.[1]
그렇다면 어떤 대상의 어떤 비교에 대해 판정 문제를 물어보아야할까? 프로그래머(Programmer)로서 명백한 대답은 두 프로그램(Program)이 실행 결과에 있어서 같은지 보는 것일 것이다. 그러나 앞서 자연수를 받아 자연수를 내놓는 함수에 대해 말했던 것과 비슷하게 두 프로그램의 실행 결과를 완벽하게 비교하는 알고리즘은 존재하지않는다. 이는 우리가 두 프로그램의 같음을 판정하고 싶다면 그 같음을 비교하는 방법에 제약을 두어야 함을 말한다. 여기서는 다음의 두 제약을 대표로 설명할 것이다.
- 문법적 비교(Syntactic Comparison)
- β\beta 동등성 (β\beta Equivalence)
1. 문법적 비교(Syntactic Comparison)
이 방법은 말 그대로 두 프로그램이 문법 수준에서 같은지를 보는 것이다. 예를 들어 다음의 두 JavaScript 프로그램은 문법적으로 같은 프로그램이다.
// 1번 프로그램
let x = 5;
console.log(x);
// 2번 프로그램
let x = 5;
console.log( x );
공백문자의 사용에서 차이가 있으나, 그 외의 문법 요소는 모두 동일함에 유의하자. 반면 다음의 두 JavaScript 프로그램은 동일한 행동을 하지만 문법적으로는 다른 프로그램이다.
// 1번 프로그램
let x = 5;
console.log(x);
// 2번 프로그램
let x = 3 + 2;
console.log(x);
두 프로그램 모두 x
에 5
라는 값을 할당하고 5
를 콘솔에 출력하나, 첫번째 프로그램은 = 5;
를, 두번째 프로그램은 = 3 + 2
을 사용하여 5
를 할당하고 있기 때문에 문법적으로 다르다.
문법적 비교는 이렇게 문법만 보고서 쉽게 판정할 수 있다는 장점이 있으나, 두번째 예시처럼 쉽게 같은 행동을 함을 이해할 수 있는 프로그램에 대해서도 "같지 않음"이라는 결과를 준다는 단점을 가진다. 혹자는
3 + 2
같은 계산은 그냥 한 다음에 비교하면 안돼? 컴파일러(Compiler)도 상수 전파(Constant Propagation) 최적화라던지로3 + 2
를5
로 바꾸잖아?
라는 생각을 할 수도 있을 것이다. 이 제안을 반영한 방법이 바로 β\beta 동등성이다.
2. β\beta 동등성
바로 앞의 소절에서 단순 계산의 추가에 의해 같음이 같지 않음으로 변하는 것을 보았다. 이런 상황을 피하기 위해서는 같음을 평가할 때 프로그램의 실행을 고려하도록 만들어야 한다. 가장 대표적인, 대부분의 프로그래밍 언어(Programming Language)에 존재하는 프로그램의 실행은 함수 호출이다. 따라서 함수 호출을 고려한 같음의 비교는 f(c)
와 함수 f
의 몸체 b
안에서 인자 x
를 c
로 치환한 것을 같다고 취급해야한다. 예를 들어
let f = (x) => x + 3;
이 있다면, f(5)
와 5 + 3
혹은 8
을 같은 프로그램으로 취급해야한다. 이 비교 방법의 큰 문제는 함수가 종료하는지 알지 못한다는 것이다. 두 프로그램 a
와 b
를 비교하는데, a
가 종료하지 않는 함수 l
을 호출한다면, 이 알고리즘은 "같음"이나 "같지 않음"이라는 결과를 낼 수조차 없다. 즉, 올바른 판정법이 될 수 없다.
더 심각한 문제는 아직 값을 모르는 변수가 있는 "열린 프로그램"("Open Program")에 대해서도 이런 계산을 고려해야한다는 것이다. 다음의 JavaScript 예시를 보자.
let g = (x) => f(x) + 3;
let h = (x) => (x + 3) + 3;
g
와 h
는 같은 프로그램일까? 우리가 g
와 h
가 같은 프로그램이기를 원한다면 f(x)
와 x + 3
을 같은 프로그램으로 보아야한다. 대부분의 프로그램은 함수 안에서 쓰여지기 때문에 프로그램의 비교는 거의 항상 g
와 h
의 몸체와 같은 열린 프로그램들의 비교이다. 따라서 g
와 h
를 다른 프로그램으로 본다면 계산을 실행하여 두 프로그램을 비교하는 의미가 퇴색되고 만다. 그렇기 때문에 우리는 x
와 같이 값이 정해지지 않은 변수가 있을 때에도 f(x)
을 호출하여 비교해야만 한다. 이는 우리가 단순히 모든 함수가 종료하는지 여부를 떠나서, 함수의 몸체에 등장하는 모든 부속 프로그램(Sub-program)이 종료하는지 아닌지를 따져야만 한다는 이야기이다.
이런 강한 제약조건으로 인해 β\beta 동등성을 통해서 프로그램 비교의 판정 문제를 해결 가능한 곳은 매우 제한적이지만, β\beta 동등성이 매우 유용한 한가지 경우가 있다. 바로 의존 형이론(Dependent Type Theory)의 형검사(Type Checking)이다.
의존 형이론과 형의 같음
의존 형이론은 형(Type)에 임의의 프로그램을 포함할 수 있도록 하는 형이론(Type Theory)의 한 종류이다. 예를 들어 명시적인 길이(n
)를 포함한 벡터(Vector) 형을 Vector n Int
과 같이 쓸 수 있다. 이 형은 n
개의 Int
값을 가진 벡터를 표현하는 형이다. 이제 append
라는 두 벡터를 하나로 연결하는 함수를 만든다고 해보자. 대략 다음과 같은 형을 쓸 수 있을 것이다.
append : Vector n a -> Vector m a -> Vector (n + m) a
즉, append
는 길이 n
짜리 a
형의 벡터와 길이 m
짜리 a
형의 벡터를 합쳐서 길이 n + m
짜리 a
형의 벡터를 만드는 함수이다. 이 함수를 사용해서 길이 5
의 벡터를 길이 2
와 길이 3
짜리 벡터 x
, y
로부터 만들고 싶다고 하자.
append x y : Vector (2 + 3) a
안타깝게도 우리는 길이 2 + 3
짜리 벡터를 얻었지, 길이 5
짜리 벡터를 얻진 못했다. 여기서 앞서의 질문이 다시 돌아온다.
아니,
2 + 3
를5
로 계산하면 되잖아?"
그렇다. 이런 의존 형에 β\beta 동등성을 적용하면 우리가 원하는 형을 바로 얻어낼 수 있다. Vector (2 + 3) a
과 Vector 5 a
는 같은 형이기 때문이다. 더욱이, 의존 형의 경우 종료하지 않는 부속 프로그램이 잘못된 형을 줄 수 있기 때문에 많은 경우 종료하지 않는 부속 프로그램을 어차피 포함하지 않는다. 다시 말해, 앞서 말한 제약 조건 즉 모든 부속 프로그램이 종료해야만 한다는 제약조건은 의존 형의 경우 상대적으로 훨씬 덜 심각한 제약조건이 되는 것이다.
이런 의존 형에 있어서의 β\beta 동등성 검사를 "변환 검사"("Conversion Check")라고 하며, 두 형이 β\beta 동등일 경우 이 두 형이 서로 "변환 가능하다"("Convertible")라고 한다. 이 변환 검사는 의존 형이론 구현에 있어서 가장 핵심인 기능 중 하나이며, 가장 잦은 버그를 부르는 기능 중 하나이기도 하다.
마치며
이 글에서는 같음과 같지 않음의 판정 문제에 대해 간략히 설명하고 프로그램의 같음을 판정하는 법에 대해서 단순화하여 다루어보았다. 구체적으로는 문법 기반의 비교와 β\beta 동등성을 통한 비교로 프로그램의 같음을 판정하는 법을 알아보았고, 이 중 β\beta 동등성이 적용되는 가장 중요한 예시인 의존 형이론을 β\beta 동등성을 중점으로 짤막하게 설명하였다. 마지막 문단에서 언급했듯 의존 형이론의 구현에 있어서 β\beta 동등성을 올바르게 구현하는 것은 가장 중요한 작업 중 하나이기에, 최근 연구들은 β\beta 동등성의 구현 자체를 의존 형이론 안에서 함으로서 검증된 β\beta 동등성의 구현을 하기 시작하고 있다. 이 글이 같음과 같지 않음과 판정 문제 그리고 β\beta 동등성에 있어 유용한 설명을 내놓았기를 바라며 이만 줄이도록 하겠다.
두 함수가 같다라고 보는 방법에 따라 다르나, 두 함수가 항상 같은 값을 가진다면 같다고 하자. 이때 함수의 판정 문제는 정지 문제(Halting Problem)와 동일하다. 임의의 튜링 기계(Turing Machine) ff가 입력 nn을 받았을 때 종료하면 g(n)=1g(n) = 1, 아니면 g(n)=0g(n) = 0이라고 하면 이 함수 gg와 상수 함수 c(n)=1c(n) = 1가 같은 함수임을 보이는 것은 ff가 항상 종료한다는 것을 보이는 것과 동등하다. ↩︎
@ailrunAilrun (UTC-5/-4) 저는, 상단의 카테고리 중 "게시글만"을 보고, 다른 서비스와 차별점이 이거구나 싶었습니다. 개발자 선수분들이 "투고"한 것들이, 적당한 속도로 쌓여 가면 좋겠습니다.
@lionhairdino 재활의 시발점으로 요새 하고 있는 일인 Conversion checker에 대한 글이나 좀 써 보아야겠습니다.
@ailrunAilrun (UTC-5/-4) 저는, 상단의 카테고리 중 "게시글만"을 보고, 다른 서비스와 차별점이 이거구나 싶었습니다. 개발자 선수분들이 "투고"한 것들이, 적당한 속도로 쌓여 가면 좋겠습니다.
@lionhairdino 국어 글쓰기 재활을 좀 해 보아야겠네요. 일전의 블로그 투고 연재의 "실패"는 너무 큰 조각을 한 번에 쓰려고 했기 때문 아닌가 싶은데, 이런 매체에서의 글쓰기는 더 세밀한 조각으로 나눌 수 있지 않을까 싶네요.
@ailrunAilrun (UTC-5/-4) 저도 궁금해서 질문을 했었습니다!
@curry박준규 그렇군요. 저는 기본적으로 "조용히 공개"를 항상 사용할 것 같은데, 기본으로 설정하는 방법이 있으면 좋겠네요.
기능이 여럿 있는데, 그 상세를 잘 모르겠는 것이 많군요. 이를테면 "공개"와 "조용히 공개"의 차이점이 제게는 직관적이지 않네요. 무슨 차이인지 아시는 분이 있다면 알려주시면 감사하겠습니다.
@ailrunAilrun (UTC-5/-4) 어서오세요. 반갑고 반갑습니다~
@lionhairdino 안녕하세요. 이런 매체로는 오랜만이네요.