- 선택자와 규칙으로 대상 집합을 고르고 속성을 적용하는 CSS의 구조는, 집합과 규칙으로 동작하는 Datalog와 형태적으로 닮아 있음
div.awesome같은 선택자 결합은 교집합을 만들고, Datalog에서는 같은 변수를 반복하는 방식으로 비슷한 join이 일어남- 현재 CSS는 계산된 스타일 결과를 다시 선택 조건으로 삼지 못해, 재귀적 전이 질의나 파생 상태의 반복 전파를 직접 표현하기 어려움
- Datalog은 재귀 규칙과 고정점 평가로 새 사실이 더 이상 생기지 않을 때까지 관계를 확장하며, 단조성 덕분에 유한한 범위에서 계산을 끝낼 수 있음
- 실제 CSS는 Container Queries 같은 기능으로 조상 정보를 읽을 수 있지만 피드백 루프와 순환을 막는 방향을 택했고, 대신 CSS 문법을 재귀 질의에 접목할 여지는 남아 있음
CSS와 Datalog의 닮은 구조
- CSS는 대상 집합 선택과 선택된 대상에 대한 규칙 적용이라는 구조를 가짐
- HTML 요소 같은 "Things"가 먼저 존재하고, selector가 공통 속성을 가진 집합을 가리킴
div,#child,.awesome,[data-custom-attribute="foo"]같은 selector로 집합을 기술할 수 있음div.awesome처럼 selector를 결합해 교집합을 만들 수 있음
- CSS 규칙은 selector와 declaration을 묶어, 선택된 요소에
color나font-size같은 속성을 설정함- 다만 이런 속성은 대체로 언어 바깥의 상태를 바꾸며, 그 결과를 다시 selector 조건으로 삼지는 못함
div[color=red]처럼 스타일 결과를 다시 질의하는 형태는 브라우저가 받아들이지 않음
- Datalog도 비슷하게 사실 집합과 규칙 기반 도출로 동작함
parent(alice, bob)같은 원자와 relation이 기본 단위가 됨- 변수
X,Y를 써서 조건에 맞는 항목 집합을 고를 수 있음 - 같은 변수를 반복해 조건을 연결하면 CSS의 selector 결합과 비슷한 join이 일어남
head(X, Y) :- body1(X, Z), body2(Z, Y)구조는 CSS 규칙과 방향만 반대일 뿐 형태가 유사함- CSS의 selector가 Datalog의 body에 가깝고, declaration은 head에 가까움
div.awesome { color: red; }는color(X, red) :- div(X), class(X, awesome).와 대응됨
CSS가 못 하는 재귀적 질의
data-theme="dark"안의 모든 포커스 요소에 반전 스타일을 주되, 중간에data-theme="light"가 나오면 멈추는 조건은 전이적 질의를 필요로 함- 실제 CSS에서는
[data-theme="dark"] :focus와[data-theme="dark"] [data-theme="light"] :focus같은 규칙으로 일부만 처리할 수 있음 - 중첩 단계가 늘어나면 규칙을 계속 추가해야 하며, 재귀적 관계를 직접 표현하기 어려움
- 실제 CSS에서는
- 필요한 조건은 요소가 effectively-dark인지 재귀적으로 판정하는 일임
- 자신이
data-theme="dark"이면 effectively-dark가 됨 - effectively-dark 조상 아래의 자식도, 중간에
data-theme="light"가 없으면 effectively-dark가 됨 - 이 상태를 바탕으로
.effectively-dark :focus에 스타일을 적용해야 함
- 자신이
- 가상의 CSSLog 문법에서는 규칙이
class: +effectively-dark처럼 파생 상태를 추가할 수 있음.effectively-dark > :not([data-theme="light"])가 자식으로 상태를 전파함- 규칙은 목표 상태에 도달할 때까지 재귀적으로 반복돼야 함
- 이런 종류의 재귀 전파는 현재 CSS로는 표현하기 어려움
- 글 말미에서 일부 비슷하게 흉내 내는 방법도 나오지만, 같은 원리의 일반 해법은 아님
Datalog의 재귀와 고정점
- Datalog은 기존 사실에서 새 사실을 도출하는 방식으로 동작하며, 재귀를 기본적으로 다룸
ancestor(X, Y) :- parent(X, Y).ancestor(X, Y) :- parent(X, Z), ancestor(Z, Y).
ancestor규칙은 부모 관계를 바탕으로 조상 관계를 단계적으로 확장함parent(alice, bob)에서ancestor(alice, bob)가 먼저 생김- 이어서
alice -> bob -> carol,alice -> bob -> dave같은 경로도 추가로 도출됨
- 이 계산은 명시적
for루프 없이도 고정점 평가로 끝까지 진행됨- 처음에는 명시된 base fact만 사용함
- 모든 규칙의 body를 현재 사실 집합에 대입해 head를 추가함
- 새 사실이 더 이상 생기지 않을 때 멈춤
- 이 방식이 끝나는 이유는 단조성에 있음
- 사실을 추가만 하고 제거하지 않으므로, 알려진 사실 집합은 계속 커지기만 함
- 유한한 사실 집합에서 시작하면 유도 가능한 사실 수도 유한하게 제한됨
- 반대로 사실을 제거할 수 있으면 이전 결론이 뒤집히며 무한 루프에 빠질 수 있음
Container Queries와 실제 CSS의 경계
- 실제 CSS의 Container Queries는 조상이나 컨테이너의 스타일을 기준으로 규칙을 적용할 수 있음
@container style(--theme: dark) { .card { background: royalblue; color: white; } }같은 형태를 지원함
- 하지만 transitive dark mode 예시는 단순한 조상 조회보다 더 강한 조건을 요구함
- 각 요소가 자기 자신이 effectively-dark인지 알아야 함
- 그 상태가 자손 전체로 전이적으로 전파돼야 함
data-theme="light"경계에서 전파가 멈춰야 함
- Container Queries는 2번째 조건을 처리하지 못함
- 조상의 custom property는 읽을 수 있어도, 다른 규칙이 이미 계산한 파생 상태를 다시 질의하지는 못함
- DOM에 원래 있던 정보는 볼 수 있지만, 재귀 계산 결과를 selector 조건으로 삼지는 못함
- 2015년의 관련 글도 element queries가 같은 문제에 부딪혔다고 짚음
- 질의로 설정한 속성을 다시 질의할 수 있게 되면 루프와 무한 반복 위험이 커짐
- CSS Working Group은 이 문제를 정보 흐름 방향 제한으로 피해 왔음
- 자손이 조상 정보를 질의하는 것은 허용함
- 반대 방향 피드백이나 자기 자신 스타일로의 순환은 막음
- 그래서 고정점 의미론 없이도 계산을 유한하게 유지함
CSS 문법을 재귀 질의 언어로 뒤집는 가능성
- Datalog 의미론을 CSS에 넣기보다, CSS 문법을 Datalog 위에 얹는 방향이 더 현실적인 새 길로 제시됨
- Datalog의
:-, 마침표, 선언 없는 원자 같은 문법은 현대 언어 사용자에게 진입 장벽이 큼 - CSS는 이미 트리 구조를 다루는 selector 문법을 풍부하게 갖고 있음
- Datalog의
- 실제 데이터 중에는 트리 형태가 많다고 짚음
- JSON
- AST
- 파일시스템
- 조직도
- XML
- 이런 영역에서는 부모/자식 관계를 암묵적으로 다루는 CSS풍 문법과 고정점 재귀를 결합하면 유용할 수 있음
- 일반 Datalog은 트리 구조를 관계형 표현으로 옮겨 적어야 해서 번거로움이 큼
- CSS selector 감각을 재귀 질의에 그대로 가져오면 더 많은 프로그래머가 쉽게 접근할 수 있음
- 이런 형태의 도구는 아직 뚜렷하게 보이지 않음
- "CSSLog"라는 이름은 임시적이며, 더 나은 이름의 언어가 나올 수 있음
- 재귀적 트리 질의를 더 익숙한 표기법으로 다룰 여지는 남아 있음
보충 논점과 참고 링크
- Datalog은 1970년대부터 관계형 데이터베이스와 당시의 AI 연구 맥락에서 등장했으며, 이후 여러 형태로 반복해서 재등장함
- 고정점 계산의 단순한 형태는 naive evaluation으로 소개되지만, 이미 알려진 사실을 매번 다시 계산해 비효율적일 수 있음
- 각 단계에서 새로 나온 사실만 활용하는 semi-naive evaluation이 대표적 개선 방향으로 함께 언급됨
- 단조성은 분산 시스템에서도 유익한 성질로 이어짐
- custom property 상속으로 transitive dark mode를 부분적으로 흉내 내는 방법도 있음
[data-theme="dark"] { --effective-theme: dark; }[data-theme="light"] { --effective-theme: light; }@container style(--effective-theme: dark) { :focus { outline-color: white; } }- 이 방법은 이 특정 경우에는 대체로 동작하지만, 진짜 전이 폐쇄를 일반적으로 제공하지는 못함