최근 수정 시각 : 2024-10-14 14:19:11

Scheme



파일:나무위키+유도.png  
은(는) 여기로 연결됩니다.
대수기하학의 스킴에 대한 내용은 스킴(대수기하학) 문서
번 문단을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
참고하십시오.
파일:하위 문서 아이콘.svg   하위 문서: Scheme/튜토리얼
,
,
,
,
,
#!wiki style="display: inline; display: none;"
, }}}
프로그래밍 사이트 선정 프로그래밍 언어 순위 목록
{{{#!wiki style="margin: 0 -10px -5px; word-break: keep-all"
{{{#!wiki style="display: inline-table; min-width: 25%; min-height: 2em;"
{{{#!folding [ IEEE Spectrum 2024 ]
{{{#!wiki style="margin: -5px 0"
<rowcolor=#fff> 스펙트럼 부문 상위 10개 프로그래밍 언어 직업 부문 상위 10개 프로그래밍 언어
1 Python 1 SQL
2 Java 2 Python
3 JavaScript 3 Java
4 C++ 4 TypeScript
5 TypeScript 5 SAS
6 SQL 6 JavaScript
7 C# 7 C#
8 Go 8 HTML
9 C 9 Shell
10 HTML 10 C++
}}}
}}}
}}}
[ Stack Overflow 2024 ]
[ TIOBE 2024 ]
||<tablewidth=100%><width=9999><-4><bgcolor=deepskyblue><tablebgcolor=#fff,#222> 2024년 8월 기준 검색어 점유율 상위 20개 프로그래밍 언어 ||
1 Python 11 MATLAB
2 C++ 12 Delphi / Object Pascal
3 C 13 PHP
4 Java 14 Rust
5 C# 15 Ruby
6 JavaScript 16 Swift
7 SQL 17 Assembly language
8 Visual Basic 18 Kotlin
9 Go 19 R
10 Fortran 20 Scratch
{{{#!wiki style="margin: 0 -10px -5px; min-height: calc(1.5em + 5px);"
{{{#!folding [ 21위 ~ 50위 펼치기 · 접기 ]
{{{#!wiki style="margin: -5px -1px -11px"
21 COBOL 36 Scala
22 Classic Visual Basic 37 Transact-SQL
23 LISP 38 PL/SQL
24 Prolog 39 ABAP
25 Perl 40 Solidity
26 (Visual) FoxPro 41 GAMS
27 SAS 42 PowerShell
28 Haskell 43 TypeScript
29 Dart 44 Logo
30 Ada 45 Wolfram
31 D 46 Awk
32 Julia 47 RPG
33 Objective-C 48 ML
34 VBScript 49 Bash
35 Lua 50 Elixir
}}}}}}}}} ||
[ PYPL 2024 ]

}}} ||
프로그래밍 언어 목록 · 분류 · 문법

(display "Hello, world!\n")
Scheme
스킴
파일:Lambda_lc.svg
개발자 제럴드 서스먼
가이 스틸 주니어
최초 공개 1975년 ([age(1975-12-22)]년 전)
최신 표준 R7RS (2021년 2월 13일 발표)
파일:홈페이지 아이콘.svg
1. 개요2. 역사3. 특징4. 종류5. 실습6. 교재7. 예제8. 여담9. 관련 문서10. 외부 링크

[clearfix]

1. 개요

스킴(Scheme)은 Lisp 계열의 언어 중 커먼 리스프(Common Lisp)와 함께 가장 유명하고 대표적인 프로그래밍 언어이다.

2. 역사

표준 연도
R0RS 1975년
R1RS 1978년
R2RS 1985년
R3RS 1986년
R4RS 1991년
R5RS 1998년
R6RS 2007년
R7RS 2013년
그 시초는 칼 휴잇(Carl Hewitt)의 액터(Actor) 모델을 이해하기 위한 시도에서 비롯되었다. 제럴드 서스먼(Gerald Sussman)과 가이 스틸 주니어(Guy Steele Jr)가 Maclisp에 기반한 작은 리스프 인터프리터를 만들면서 액터 모델을 구현하고 메시징 기능을 추가한 것이 바로 Scheme의 시초이다. 1975년 당시 이름은 Schemer였는데, 이는 Plannar, Conniver등 리스프에서 파생된 다른 언어의 이름이 ~er로 끝나서 이를 따라 명명했다고 한다. 이후 개발자들이 파일이름과 확장자가 각각 6글자로 제한되는 ITS 운영체제를 사용하면서 현재의 이름인 Scheme으로 이름이 변경되었다.

당시에는 프로그램에서 함수를 불러올 때 호출하고 끝나서 돌아오는 과정에서 시간이 오래 걸려서 많은 수의 함수를 쓰는 것은 비효율적이라는 것이 상식이었다.[1] 서스먼과 스틸이 하던 연구는 이 "상식"을 뒤집는 함수 호출 구조에 대한 것으로, 함수를 호출하고 값을 반환하는 대신 주어진 환경에서 메모리 주소를 점프하며 함수만 갈아끼우는 획기적인 방식을 구현하기 위한 프로토타입 언어였다. 둘은 처음에 (이후에 나온) 커먼 리스프와 같은 규모의 언어를 생각하고 있었지만 정작 나온 것은 극도의 미니멀리즘에 기반한 언어였다.

여기서 스킴의 행보가 여러 방향으로 찢어지게 되는데, 대표적으로 극단적으로 간소하면서도 이해하기 쉬운 언어를 교육용으로 사용하려는 방향은 서스먼의 명저 SICP(Structure and Interpretation of Computer Programs)에서 드러난다. 이 책은 MIT 컴퓨터 과학 전공의 동명의 입문 과목 교과서로 유명해졌고, 한동안 미국 전역에서 이를 따라 SICP와 스킴으로 입문을 가르치는 학교가 꽤 있었다.[2] 반면에 한편에서는 이 우아한 언어를 실제로 업무에 사용해보려는 노력으로, R5RS 표준의 제정 이후 SRFI(Scheme Request for Implementation, 스킴 구현 요청)을 통해 프로그래머들이 필요한 기능들을 요청하고 직접 구현하는 움직임이 활발해졌다. 간단한 구조 때문에 성능을 생각 않는다면 취미삼아 만들어볼만한 사이즈의 언어였기에 각종 컴파일러와 인터프리터가 우후죽순처럼 생겨났고, 각각의 부분은 호환되는 부분이 많지만 세세한 데에서 호환되지 않는 경우가 꽤 많았다.

이러한 분열의 와중에서 스킴에 가장 큰 타격을 준 것은 R6RS 표준의 제정이었다. 원래는 양쪽의 입맛을 모두 맞추기 위해 각종 기능을 넣은 새 표준이었지만, R5RS와 호환되지 않는데다 R5RS 표준에 비해 몇 배나 큰 기본 베이스에 교육자들은 사용을 거부했고, 기존에 사실상의 표준으로 사용되던 SRFI 라이브러리와 전혀 호환되지 않는 기능에 프로그래머들도 사용을 거부했다. 이 때문에 교육용 R5RS, 업무용 R5RS+SRFI, 업무용 R6RS로 스킴 커뮤니티는 완전히 분열해버렸다.

결국 스킴 제정 위원회는 R7RS 표준을 교육용 기본 언어와 이와 호환되는 업무용 방대한 언어로 나누어 두 표준을 따로 제정하기로 했다. 얼마 전에 새로 제정된 R7RS-small 표준은 R6RS를 완전히 흑역사로 묻어버리고 R5RS기반으로 만들어졌으며, 이후에 제정될 R7RS-large는 커먼 리스프보다 큰 규모의 언어를 목표로 삼는다는데... 글쎄, 위원회 언어는 듀크 누켐 포에버보다 나오기 힘들다. 어차피 SRFI 상당수를 공식으로 지정하는 것이 중점이 될 것이라 하니 SRFI가 잘 지원되는 스킴을 찾아보자.

3. 특징

다른 리스프 계열[3]보다 함수형 프로그래밍을 적극 장려하는 언어로, 언어 표준에 순수 함수형에서 벗어나는 함수 이름 뒤에는 꼭 "!"를 붙이는 것을 관례로 명시할 정도이다.[4] 그러나 그보다 더 핵심적인 특징은 최소한의 기본 명령만 기계어로 정의가 되어있고, 언어 표준에 명시된 다른 명령들은 기본 명령에 속한 람다(lambda)함수를 이용해 정의가 되어있다는 것이다. 이 때문에 스킴은 다른 프로그래밍 언어에 비해 매우 사이즈가 작으면서도, 기본만 사용해 필요에 따라 무엇이든 정의해 사용할 수 있는 독특한 디자인을 가지고 있다. 커먼 리스프를 109가지 기능이 붙어있는 다목적 공구에 비유하자면, 스킴은 딱 최소한의 10개 기능만 붙어있는 맥가이버 칼에 비유할 수 있다. 같은 뿌리에서 나온 다른 사이즈의 언어라는 점에서 C++와 C의 관계와도 비슷할지도 모르겠지만, C++의 기능을 C에서 구현하려면 오만가지 삽질을 해야하는데 비해 스킴은 커먼 리스프의 기능을 구현 가능한, Greenspun's Tenth Rule에서 가장 안전한 언어라 할 수 있다.
복잡한 C나 포트란 프로그램은 커먼 리스프에 이미 기본적으로 있는 기능을 직접 만들어 써야 할 가능성이 높은데, 큰 기능을 여러 개 구현하려다 보니 대충 끼워 맞추고 상세도 불분명해 버그가 넘쳐나고 느릴 수밖에 없다.
Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp.
Greenspun's Tenth Rule
위에 말했듯이 스킴은 워낙 표준에 따라 다양한 구현이 있고, 그마저도 표준은 그저 대충 거의 다 지키면 되는 것. 수준의 인식이 있어서 각각의 구현이 조금씩 다른 언어나 방언이라고 할 수 있다. 관계가 조금 먼 Racket을 제외하면, 스킴을 스킴으로 묶는 특성은 다음과 같다.
  • Lambda

    • 스킴의 뿌리인 람다식. (lambda (x) ...)로 스킴에 존재하는 거의 모든 것을 나타낼 수 있다. 람다 계산식에서의 그 람다 맞다.
  • Tail-call elimination(TCE)

    • 위에서 나왔듯 "함수간에 값을 주고받는 형식"이 아닌 "값을 환경으로 두고 함수를 점프해서 갈아끼우는 방식"을 일반화한 것으로, 함수 A가 B를 부를 때 B를 부르는 장소가 Tail[5]인 경우 그냥 B의 주소로 점프해버리는 방식이다. 하는 법을 알면 재귀적 함수를 부를 때 절대 스택 오버플로우가 나지 않으며 함수 호출 속도도 빠른 스킴의 전매특허 특징으로, 언어 상세에 "tail-call elimination을 하지 않으면 스킴이 아니다"라고 명시되어 있을 정도다. 이후에 많은 언어(주로 함수형)에서 채용했다.[6]
  • Continuation

    • TCE과 함께 연구의 부산물로, TCE가 함수에 대한 설명이라면 Continuation은 환경에 대한 설명이다. 함수가 서로를 호출하고 자리를 넘겨줄 때 주변 환경을 같이 연속적(continuous)으로 넘겨준다는 의미로, 핵심 call/cc[7]라는 명령어는 "현재 환경을 기억했다가 새 값을 넘겨받으면 이 자리로 즉시 돌아와서 값을 전달해라"라는 뜻으로 쓰인다. 실 용례로는 간단하게는 return, break 등의 대용품, 복잡하게는 멀티태스킹 관리(!) 등이 있다. 이해하기 복잡하면 C의 setjmp/longjmp의 업그레이드 버전이라 생각하면 된다.
  • S-expression

    • 리스프라면 빼놓을 수 없는 문법 구조로, atom(심볼, 문자, 문자열, 숫자 등)과 괄호 리스트 딱 두 개로만 이루어져있어 코드 (car a)와 데이터 '(car a)의 구조가 똑같고 바꿔치기도 가능하다. 이를 Homoiconicity라고 한다.
  • Macro

    • 바로 그 바꿔치기를 가능하게 하는 일등공신으로, 기본 매크로는 커먼 리스프의 매크로보다 덜 강력하지만 오류가 적고 간편한 문법을 사용하며[8], 대부분의 스킴은 자체적으로 커먼 리스프 수준의 매크로도 함께 지원한다.

4. 종류

워낙 종류가 많아서 고르기 힘들지만 가장 특징적이고 메이저한 스킴은 다음과 같다.
  • Chicken

    • 가장 커뮤니티가 활성화된 R5RS 스킴으로, Freenode IRC의 #Chicken에는 언제나 많은 사람이 상주해있고 즉각 질문에 대한 답이 올라온다. C를 통해 컴파일하며 가장 많은 수의 SRFI와 라이브러리를 지원한다. 특징은 구현방식으로, 새 함수로 넘어갈때 리턴 없이 함수를 계속 부르다 스택이 꽉 차면 GC를 부르고 처음에 시작한 함수로 longjmp를 해버린다. 여기 나열된 스킴 중 유일하게 R7RS 지원 의지를 표현한 스킴. 교육이 아닌 실제 업계에서 사용될 것을 염두에 두고 만들어진 컴파일러/인터프리터이다.
  • Gambit

    • 가장 속도가 빠른 편인 R5RS 스킴. 다만 SRFI와 라이브러리 지원은 상대적으로 좋은 편은 아니다. LambdaNative라는 프레임워크를 사용하면 스킴으로 iOS와 안드로이드 개발이 가능하다! C를 통해 컴파일한다.
  • Guile

    • GNU 공식 스크립트 언어(R5RS/R6RS 호환). 특징은 GNU와 리눅스 계열의 시스템(혹은 거기서 파생된) 라이브러리가 많다. 2020년 1월 16일 릴리즈한 3.0부터 JIT를 지원한다! 성능은 2.2 버전 대비 평균 2배 향상되었으며 일부 벤치마크 항목은 최대 32배까지 올라간다고 한다.[9]
  • Stalin

    • 약 빤 네이밍에 걸맞게 약 빤 성능. C를 통해 컴파일하는데, 수치계산에 한해 사람이 짠 C보다 빠른 수준을 넘어서 포트란 수준이다. 단 R4RS인데다 컴파일하는데 조금 큰 파일 하나에 반나절 걸린다.
  • Chez Scheme

    • 인디애나 대학의 한 교수가 만든 컴파일러로, Common Lisp 쪽의 LispWorks 에 대응하는 고가의 상용 컴파일러였으나 2016년 4월 27일부로 9.4 버젼을 GitHub 에 오픈소스로 공개했다.[10] 이전까지는 Petite Chez Scheme 이라는 인터프리터의 바이너리만 무료로 공개했다. LispWorks 와 같이 여러가지 지원도 좋은편이며 (물론 이 지원을 받으려면 유료로 구매해야 한다.) 정상적인 Scheme 컴파일러중에서는 대적할자가 없을정도로 퍼포먼스가 압도적이다. 아래 나오는 Racket 이 PLT-scheme 이던 시절 test-suite 을 돌렸을때 10분이 넘게 걸리던 프로그램이 chez scheme 로 컴파일하니 컴파일 속도도 훨씬 빨랐으며, 실행속도에서도 같은 test-suite 을 30초컷 했다는 유저 경험담도 있을정도. 물론, PLT-scheme 가 퍼포먼스로 유명한 컴파일러는 아니었지만 타 오픈소스 Scheme 컴파일러와 비교하여 퍼포먼스가 딱히 크게 떨어지는 수준은 아니었다는 것을 감안한다면 대충 상상이 될 것이다.
  • Ikarus

    • R6RS 를 지원하는 오픈소스 scheme 컴파일러로, 특이하게도 위 chez scheme 를 만든 교수에게서 박사과정을 했던 사람이 만든 컴파일러이다.지도교수 돈줄을 끊는 학생의 모범적인 예시 퍼포먼스는 상당한 수준이지만, 개인사정으로 인해 홈페이지도 접속불가이고 현재는 업데이트가 되고있지 않는 상황.
  • Racket

    • 스킴이기를 그만 둔 스킴. 딱히 어느 표준에 맞추지 않고 매크로와 확장성을 이용해 어떤 언어든[11] 모방 가능하다. 원래는 plt-scheme이라 불렸지만 지속적으로 스킴의 틀에서 벗어나다 결국 이름을 바꾸었다.(더 자세한 내용은 Racket 항목을 참고하자.)

5. 실습

  • 설치

    • macOS에서 터미널에 아래와 같이 입력하면 스킴을 설치할 수 있다.
      {{{brew install mit-scheme
}}}
  • 시작

    • 설치 후 터미널에 mit-scheme이라고 입력하면 스킴 인터프리터(?)가 실행된다.
      {{{% mit-scheme
MIT/GNU Scheme running under OS X
Type `^C' (control-C) followed by `H' to obtain information about interrupts.

Copyright (C) 2022 Massachusetts Institute of Technology
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Image saved on Saturday January 7, 2023 at 1:28:01 AM
Release 12.1 || SF || CREF || LIAR/svm1

1 ]=>
}}}
  • 예제 코드 저장

    • 아래와 같은 소스 코드를 foo.scm이라는 이름으로 저장하자.[12]
      {{{(define in-S?
      (lambda (n)

        (if (zero? n) #t

          (if (>= (- n 3) 0)

            (in-S? (- n 3))
            #f))))
}}}
  • 저장된 코드 불러오기

    • 인터프리터에서 함수 load를 이용하면 저장된 코드를 불러올 수 있다.
      {{{1 ]=> (load "foo.scm")

;Loading "foo.scm"... done
;Value: in-s?

1 ]=>
}}}

6. 교재

  • The Little Schemer

7. 예제

다음은 스킴으로 팩토리얼 프로시저를 구현한 코드이다.
(define (fact n)
  (if (< n 2) 1 (* n (fact (- n 1)))))

8. 여담

  • 브랜든 아이크 넷스케이프 시절 브라우저 내에 동적 웹페이지를 구현하기 위해 스크립트 언어로 쓰려 한 게 바로 이 언어였다. 그러나 넷스케이프에서 일반인들도 웹페이지 개발을 쉽게 할 수 있도록 하자며 C, Java와 비슷한 문법의 언어로 만들어야 한다고 요구해서 꼬장 부려서 어쩔 수 없이 Scheme을 쓰려는 걸 포기하고 JavaScript라는 새로운 언어를 만들게 된다. 만약 본래 계획대로 Scheme을 썼다면 웹은 어떻게 발전했을지, 웹프로그래밍 입문자가 많았을지, 현재 JavaScript가 안고 있는 여러 문제점들은 생기지 않았는지, 그에 따른 제이쿼리 리액트, TypeScript 같은 것들이 생겨날 필요가 없었는지 모를 일이다.
  • R7RS와 같이 스킴 표준을 표시하는 표현은 Revisedn Report on the Algorithmic Language Scheme의 약자이다.
  • 스킴을 만든 제럴드 서스먼은 자기가 만든 언어를 홍보하려고 SICP를 썼다.
  • 스킴에서 주로 사용하는 확장자는 다음과 같다.[13]
    • .scm("Scheme")
    • .sps("Scheme program source")
    • .sls("Scheme library source")
    • .sld("Scheme library definition")

9. 관련 문서

10. 외부 링크


[1] 그 때문에 GCC같은 C 컴파일러는 작은 함수를 여러개 호출하는 부분을 파악해 하나의 큰 함수로 합쳐버리고, 이를 inlining이라고 한다. [2] 국내에도 카이스트, 고려대, 동아대 등 몇몇 학교가 교육했다. 2020년대에는 MIT도 Python으로 갈아탔다. [3] Clojure 제외 [4] 예를 들어 (set! a 1)은 이미 만들어진 이름 a에다가 새로 1을 배정한다. [5] B를 부른 후에 할 것이 return밖에 없는 경우 [6] GCC에서 -O2를 하면 C에서도 제약이 많지만 TCE가 된다! [7] call-with-current-continuation [8] "..." 점 3개로 뭐든지 패턴매칭이 가능하다! [9] GNU Guile 3.0.0 released [10] https://github.com/cisco/ChezScheme [11] 알골 에뮬레이터가 내장되어있다! [12] Essentials of Programming Languages라는 프로그래밍 언어론 교재에 나오는 코드이다. [13] https://stackoverflow.com/a/56853906

분류