프로그래밍 언어

덤프버전 :

[ 펼치기 · 접기 ]
기반 학문
수학 (해석학 · 이산수학 · 수리논리학 · 선형대수학 · 미적분학 · 미분방정식 · 대수학 (환론 · 범주론) · 정수론) · 이론 컴퓨터 과학 · 암호학 · 전자공학 · 언어학 (형태론 · 통사론 · 의미론 · 화용론 · 음운론) · 인지과학
SoC · CPU · GPU(그래픽 카드 · GPGPU) · ROM · RAM · SSD · HDD · 참조: 틀:컴퓨터 부품
기술
기계어 · 어셈블리어 · C(C++ · C\#) · Java · Python · BIOS · 절차적 프로그래밍 · 객체 지향 프로그래밍(디자인 패턴) · 해킹 · ROT13 · OTP · IoT · 와이파이 · GPS · 임베디드 · 인공신경망 · OpenGL · EXIF · 마이크로아키텍처 · ACPI · UEFI · NERF · gRPC · 리버스 엔지니어링 · HCI · UI · UX · 대역폭 · DBMS · NoSQL · 해시(SHA · 브루트 포스 · 레인보우 테이블 · salt · 암호화폐) · RSA 암호화
연구및 기타 문서
논리 회로(보수기 · 가산기 · 논리 연산 · 불 대수 · 플립플롭) · 정보이론 · 임베디드 시스템 · 디자인 패턴 · 데이터베이스 · 프로그래밍 언어{컴파일러(어셈블러 · JIT) · 인터프리터 · 유형 이론} · 메타데이터 · 기계학습 · 빅데이터 · 폰노이만 구조 · 양자컴퓨터 · 행위자 모델 · 인코딩(유니코드 · MBCS) · 네트워크 · 컴퓨터 보안 · OCR · 슈퍼컴퓨터 · 튜링 머신 · FPGA · 딥러닝 · 컴퓨터 구조론 · 컴퓨터 비전 · 컴퓨터 그래픽스 · 인공지능 · 시간 복잡도(최적화) · 소프트웨어 개발 방법론 · 정보처리이론 · 재귀 이론 · 자연 언어 처리(기계 번역 · 음성인식)
}}}




프로그래밍 사이트 선정 프로그래밍 언어 순위 목록

⠀[ IEEE Spectrum 2021 ]⠀
{{{#!wiki style="display: inline-block; margin: 0 0 -5px; min-width: 25%"
⠀[ Stack Overflow 2022 ]⠀
{{{#!wiki style="display: inline-block; margin: 0 0 -5px; min-width: 25%">
⠀[ TIOBE 2023 ]⠀

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

1. 개요
2. 등장 배경과 역사
3. 구조
4. 분류
4.1. 수준
4.2. 해석 방식에 따른 분류
4.3. 메모리 관리 방식에 따른 분류
4.4. 정적 타입, 동적 타입
4.5.1. 명령형 언어
4.5.1.1. 절차적 언어
4.5.1.2. 객체 지향 언어
4.5.2. 선언형 언어
4.5.2.1. 함수형 언어
4.5.2.2. 논리 프로그래밍
4.5.2.3. 반응형 프로그래밍
4.6. 특수 목적 언어
5. 회화 언어와의 관계
6. 개발툴
8. 오해
9. 기타
10. 둘러보기



1. 개요[편집]


programming language

기계(컴퓨터)에게 명령이나 연산을 시킬 목적으로 설계되어 기계와 의사소통을 할 수 있게 해주는 언어를 뜻한다. 그 결과, 사람이 원하는 작업을 컴퓨터가 수행할 수 있도록 프로그래밍 언어로 일련의 과정을 작성하여 일을 시킨다. 쉽게 말하면 컴퓨터를 이용하기 위한 언어이다. 프로그램을 작성하는데 기본이 되고, 이 프로그램은 논리 연산의 집합이기 때문에 수리 언어의 일종으로 보는 시각도 있다.

컴퓨터보다 먼저 등장하였으며 본격적인 연구는 1930년대 즈음부터 수학자들에 의해 기계적으로 계산 가능한 함수에 대한 연구가 진행된 데에서 비롯되었다. 그 결과 기계가 이해할 수 있는 언어가 탄생했으며, 바로 이 기계가 계산 가능하고 이해 가능한 언어를 실행하는 기계로 언어보다 나중에 발명된 것이 바로 현대적 의미의 컴퓨터이다.

이 언어들은 사람이 이해하기 쉽게 만든 언어로 실행전 컴파일러를 통해 기계어로 컴파일된 후 실행된다.[1]

2. 등장 배경과 역사[편집]


역사를 잘 살펴보면 프로그래밍 비스무리한 것을 만들고 했던 기인들이 종종 등장하지만, 프로그래밍 언어라는 본격적인 개념은 역시 수학에서 등장하였다. 쿠르트 괴델불완전성 정리를 증명하는 과정중에 알고리즘을 추상화시킨 원시 재귀 함수(primitive recursive function) 개념을 만들고, 이를 이용하여 증명에 성공하였는데, 수학적으로 본다면 이것이 최초의 프로그래밍 언어라 볼 수 있다.(굳이 따지자면 함수형 언어라 할 수 있다.) 그리고, 몇년 후에 컴퓨터의 아버지라 불리는 튜링은 불완전성 정리를 보고 "어 이거 내 방식대로 증명해볼 수 있겠는데?"라고 생각하며 연구를 하는데, 여기서 이 원시재귀함수와 동치인 튜링 머신을 발표하고 이 튜링머신을 이용하여 불완전성 정리를 다시 한 번 증명해보인다. 이는 "어떤 체계를 통해서 이 체계의 모순성을 증명할 수 있는 방법은 없다"와 동치이다. 정지 문제 참조.

그 이후 계산가능성이론(Computability Theory)이라는 수학의 분야가 생기면서 기존의 원시재귀함수를 확장한[2] Lambda calculus라거나 Unlimited register machine(URM), While-programming[3], SKI[4] 등등 알고리즘을 표현하기 위한 여러가지 체계들이 등장한다.

참고로, Lambda calculus를 고안한 알론조 처치의 경우 혹시나 이것을 이용하면 수학의 완전성을 증명할 수 있지 않을까 해서 시도하지만, 실패한다. 문제는, 상기된 알고리즘 체계들은 모든 알고리즘을 다 표현할 수 있는 체계인가라는 의문인데, 알고리즘이라는것이 수학적으로 정의된 개념이 아니기때문에 그것을 증명하는것은 불가능하였다. 하지만 저 모든 알고리즘 체계들이 표현방식은 다르더라도 수학적으로 볼때는 다 동일하였기 때문에 알론조 처치는 저 알고리즘 체계들이 '모든 알고리즘의 집합'과 동치라고 그냥 간주할 것을 제안하며, 이것이 그 유명한 처치의 명제(Church's Thesis)[5]이다.

이렇게 수학 쪽에서 프로그래밍 언어에 대한 개념적인 기초가 닦아지는 동안, 역시 수학자인 폰 노이만은 그것들을 이용하여 실제 컴퓨터를 만들기 위한 폰 노이만 구조를 만든다. 그렇게 전자쪽과 결합을 하면서 현대 컴퓨터의 원형이라고 할 수 있는 물건이 만들어졌다. 이러한 전자적인 계산장치는 전기신호를 통해서 제어하였는데, 전기신호를 표현할 수 있는 방법은 신호가 들어왔다(1), 신호가 들어오지 않았다(0) 정도에 불과하였다. 따라서 제어신호는 0과 1만으로 표현하는 라이프니츠[6]식 2진법을 사용할 수 밖에 없었고, 특정한 패턴의 전기신호는 어떠한 동작을 의미한다는 식으로 사람들이 정하고 전자계산장치는 그 신호가 입력되면 정해진 동작을 하는 형태였다.하지만 실제 컴퓨터의 동작에서는 장치가 꺼져서 전기신호가 없는 상태인지 아니면 0을 나타내는 상태인지 구별하기 위하여 0은 기준 레벨(일반적으로 볼트 단위로 표시한다) 이하의 신호일 경우 0으로 본다. 마찬가지로 1 역시 기준 레벨 이상의 신호일 경우 1로 본다.

따라서 뭔가 동작을 시키기 위해서는 이러한 제어신호를 사람이 직접 작성해야 됐는데, 초창기에는 그 0과 1의 제어신호를 사람이 직접 작성하는 형태의 기계어가 사용되었다. 이 기계어는 항목에서 볼 수 있듯이 이쪽 분야에서 가장 원시적인 언어로 기계는 바로 이해할 수 있는데 사람은 도저히 이해하기도 어렵고 알아보기도 힘든 물건이라 결국 사람이 읽기 편하도록 기계어와 특정 기호를 1:1로 대응시키는 어셈블리어가 등장하였다. 과거 기계어를 쓰는 시절보다는 보기가 좀 편해졌지만 여전히 해독하기가 난해하였고, 컴퓨터의 보급과 함께 프로그램의 수요가 늘어나는데 어셈블리어의 생산성은 매우 낮기 때문에, 조금 더 프로그램을 짜기 쉬운 언어들이 등장하기 시작하였다.

초창기에 프로그래밍 언어들은 컴퓨터 성능의 한계로 인해 많은 제약이 따라붙었고, 어셈블리어적인 성격이 어느정도 남아있었다. 그럼에도 어셈블리어에 비하면 읽기가 편했고 이해하기도 훨씬 수월했다. 그리고 컴퓨터의 보급과 성능 발달에 맞물려 그동안 걸려있던 제약조건들도 하나씩 사라지고 보다 사람이 읽기도 쉽고, 이해하기도 쉽고, 작성하기도 쉬운 프로그래밍 언어들이 속속 등장하였다.

다만 어차피 컴퓨터가 이해할 수 있는 언어는 기계어 뿐이기 때문에 사람이 하기에 편해졌다는 것 뿐이지 실제 그 뒤에서 이루어지는 작업은 훨씬 더 복잡해지고 있다.

정리하자면 기계에게 편한 언어는 속도가 빠르지만 사람이 도저히 알아볼 수 있는 물건이 아니라서 생산성이 떨어진다. 반면 사람에게 편한 언어는 속도는 느리지만 이해하기 쉽고 생산성이 높다는 상관관계가 성립한다. 회화 언어로 치자면 중역의 문제로 볼 수 있다.

단, 여기에 함정이 있는데 기계어로 짠 발적화 코드와 자바로 짠 최적화 코드의 수행속도를 비교한다면 당연히 자바 쪽이 빠르다. 그러니까 기계어를 사용한다 해도 그걸 다루는 프로그래머가 기계의 특성을 훤히 꿰고 있지 않으면 다른 고급언어를 사용한 결과물보다 느려질 수도 있다는 얘기다. 그래서 몇몇 개발관련 서적에서는 최적화나 성능 튜닝한답시고 기계어어셈블리어를 남용하지 말라고 조언한다. 실제로도, 지금은 컴파일러와 어셈블러가 무척 발달되어 있어서 사람이 어설프게 짠 코드보다 기계가 변환한 코드 쪽이 더 낫다. 물론 기계가 알고리즘 자체를 개선해주지는 못하므로 컴파일러한테 자신이 의도한 바를 명확하게 전달하는 게 더 중요하다. 예를 들어 "이 배열에 있는 값은 계산 끝날 때까지 중간에 바뀔 일은 절대 없으며 숫자 말고 엉뚱한 건 전혀 없다고 보장하고, 하고 싶은 일은 이걸 다 더하는 것이다." 라고 컴파일러에게 명확하게 자신의 의도를 전달하면 컴파일러는 각종 하드웨어 가속과 코드 병렬화 등을 수행해 프로그래머가 의도한 일을 자신이 할 수 있는 가장 빠른 방법으로 구현해낸다. 일반 루프문을 사용한 배열 숫자 덧셈은 컴파일러가 숫자가 중간에 바뀔지 안바뀔지 판단을 못 하는 경우(전역변수의 배열을 더한다거나)가 생길 수 있는데 애초에 이런 코드를 만들지 않는 능력이 더 중요하다.

자바 언어를 많이 사용하게 된 이유는 다름아니라 C언어의 호환성 때문이라고 한다. C언어의 단점이 유닉스에서 C언어로 만든 코드가, 윈도우 운영체제에서는 작동을 안했다고 한다. 더 큰 문제는 유닉스도 버전이 다양해서 유닉스 운영체제끼리 호환이 안된다는 거다. 이런 호환성 문제를 해결한 언어가 자바다보니, 자바 언어를 많이 사용하게 됐다고 한다.[7]

여담으로 난해한 프로그래밍 언어보다는 어셈블리어가 훨씬 읽기 쉽다...


3. 구조[편집]


프로그래밍 언어의 표기는 구문론(syntax)과 의미론(semantics)의 두 가지 파트로 이루어진다. 구문이란 것은 언어의 외형적인 표기 방법을 일컬으며, 의미론은 구문이 내포하고 있는 의미, 즉 그 코드가 수행하는 작업을 뜻한다. 구문이 문법에 비유된다면 의미론은 글에 담긴 정보라고 볼 수 있다.

더욱 작은 단위로는 문자열(string), 문장(sentence), 어휘항목(lexeme) 등이 있으며 어휘항목의 종류를 통틀어 토큰(token)이라 한다. 어휘항목에 속한 요소에는 식별자(identifier)[8], 리터럴(literal), 연산자(operator), 예약어(reserved keyword)[9] 등이 있다.

대부분의 일반적인 프로그래밍 언어는 이런 토큰들이 영어(정확히는 미국식 영어)로 되어 있다. 물론, 창조씨앗 같은 예외도 있긴 하다.


4. 분류[편집]



4.1. 수준[편집]


추상화가 거의 되지 않아서 컴퓨터가 이해하기 쉬운 언어를 저급언어, 고도로 추상화해 사람이 이해하기 쉽지만 컴퓨터가 이해하려면 복잡한 번역이 필요한 언어를 고급언어라 한다.

  • 저급언어
    • 기계어, 어셈블리어[10]
  • 고급언어
    • 위의 두 언어를 제외한 모든 언어

4.2. 해석 방식에 따른 분류[편집]


크게 AOT 컴파일 언어, JIT 컴파일 언어, 그리고 인터프리터 언어로 분류할 수 있다. 이 분류는 정확히 말하면 언어의 분류가 아니라 언어 구현체의 분류로, 언어 명세가 어느 한 쪽을 완전히 배제한 형태가 아니거나 다른 특별한 이유가 있지 않는 이상 대부분의 언어는 이론상 어떤 식으로든 구현 가능하다. 더불어, 산업 현장에서 널리 쓰이는 언어는 기본적으로 컴파일되어 실행된다고 보면 된다. 아래의 구분은 언어의 대표 구현체를 따른 것이고, 결코 언어 자체의 분류가 아님에 유의할 것. 예를 들면, C언어라고 항상 AOT 컴파일되는 것이 아니며[11], Java라고 항상 JIT 컴파일 되는 것도 아니며[12], OCaml[13]과 Erlang[14]등의 언어는 아예 기본 구현체에 둘 다 포함시켜서 배포한다.

  • AOT(Ahead Of Time) 컴파일 언어 : C언어, C++, Rust, 파스칼, Haskell 등이 포함된다. 소스 코드를 미리 기계어로 번역[15]해서 실행한다. 미리 컴파일하더라도 가비지 컬렉션의 유무에 따라 실행속도가 차이가 나기도 한다. 일반적으로 C계열의 언어들의 런타임 퍼포먼스가 좋기 때문에 AOT 컴파일된 언어들이 근본적으로 다른 구현방식에 비해 빠르다는 착각을 하는 경우가 많으나, AOT 컴파일되었다고 런타임 퍼포먼스가 좋을 것이라고 지레짐작 해서는 안된다.[16]

  • JIT 컴파일 언어 : 대부분의 언어가 여기에 속한다. Java 및 JVM 언어[17], JavaScript 및 WebAssembly[18], C\# 및 CLR 언어[19] 등의 언어들은 기본 구현체가 JIT 컴파일러이다. 이 언어들은 대부분 바이트코드를 생성하는 1차 컴파일러, 그리고 1차 컴파일러가 생성한 바이트코드를 실행하는 가상머신(VM)으로 이루어져 있다. 이 가상머신 안에 바이트코드에 대한 인터프리터, 바이트코드를 기계어로 컴파일하는 JIT 컴파일러와 GC를 비롯한 런타임이 포함된다. JVM과 CLR, CPython의 경우에는 1차 컴파일러가 생성한 바이트코드 파일들을 배포에 활용하지만, 모든 언어가 그런 것은 아니다. 또한, JIT 구현체는 동일한 언어의 AOT 구현체에 비해 유연성이 높은 경우가 많다. 예를 들면 Java에서는 JIT 컴파일할 시에는 가능한 동적인 클래스로딩이 SubstrateVM을 통한 AOT 컴파일 시에는 불가능하고, Reflection[20]에 어느정도 제약이 가해진다.

  • 인터프리터 언어 : 소스 코드를 한 줄 한 줄 읽어 그때그때마다 번역해서 수행한다. BASIC이나 bash 같은 언어가 이런 형태이다. 소스 코드를 한 줄씩 읽어서 수행하기 때문에 대체로 AOT 컴파일 언어나 JIT 컴파일 언어에 비해 성능이 떨어지는데 2010년대 들어서 인터프리터엔진에도 JIT기법이 도입되었다. Python[21], Ruby[22], PHP[23] 등이 있다. 장점은 구현이 단순하고 구현체의 절대적인 크기가 작다는 것이다. 그 밖에 bash, awk과 같이 구현체가 매우 가볍고 간단할 필요성이 있는 경우, 또는 구현체를 충분히 빠르게 만들도록 노력을 들이지 못한 수많은 마이너 언어들이 인터프리터를 통해 작동한다.

4.3. 메모리 관리 방식에 따른 분류[편집]


크게 비관리 언어와 관리 언어로 분류할 수 있다.

  • 비관리 언어(Unmanaged Language): 동적 할당된 메모리의 해제가 자동으로 이루어지지 않으며, (주로 포인터를 이용한) 메모리 직접 접근이 가능한 언어이다. Bare Metal Language라고도 하며, 대표적으로는 C, C++ 등이 있다. 수동 기계제어가 가능한 만큼 프로그래머의 최적화 실력에 따라 높은 퍼포먼스를 낼 수 있지만, 반대로 생각하면 그렇기 때문에 메모리 관리를 제대로 하지 않을 시 프로그램이 망가질 가능성이 커서 관리 언어에 비하면 생산성은 떨어진다.

  • 관리 언어(Managed Language): 동적 할당된 메모리의 해제가 가비지 컬렉터(Garbage Collector)에 의해 자동으로 이루어지며, 메모리 접근에 대한 부분을 추상화시켜 직접적인 기계제어를 기본적으로 차단한다. 대표적으로는 Java, C\#, Python, Go 등이 있다. 메모리 관리를 언어 차원에서 책임져 주기 때문에 상대적으로 생산성은 높지만, 메모리에 직접 액세스할 수 없으므로 비관리 언어에 비해 성능 면에서 손실이 있다.



4.4. 정적 타입, 동적 타입[편집]


  • 정적 타입 언어 : 자료형(Type)이 고정돼 있는 언어. 간단히 얘기하자면 정수형으로 정의한 1은 계속 정수형 1로 남아있다. 이걸 실수형 1.0으로 바꾸려면 명시적인 형 변환(Type casting)을 해줘야 한다. 묵시적 형 변환이 이루어지는 경우도 있지만 제한적이다. C, C++, Java, C\# 등이 여기에 해당된다.

  • 동적 타입 언어 : 타입이 실행 시간에 결정되는 언어. 실행하기 전까지는 특정 식의 타입을 알 수 없다. 자료형이 그것을 처리할 함수(또는 메서드)에 따라 그때그때 바뀌는 언어. 예를 들어 정수형 1을 정의했어도 그걸 처리할 함수가 문자열을 받아들이게 설계돼있다면 자동으로 정수형 1을 문자 1로 바꿔준다. Python, JavaScript, Ruby 등이 여기에 해당된다.

동적 타입과 정적 타입의 차이는 타입이 컴파일 타임에 결정되느냐 실행시간에 결정되느냐 뿐이다. 파이썬도 개별 값들은 내부적으로 고정된 타입을 가지며 암시적으로 마구 변환되지 않는다. 다만 명시적인 조건 없이도 공통의 인터페이스를 구현하는 것들을 넣어주면 동작하게 되어 있다. 이런 개념은 "덕-타이핑"이라고 하고 동적 타입만의 특성은 아니며 정적 타입 언어에서도 구현이 가능하다. 단지 정적 타입 언어들이 추구하는 방향이 덕 타이핑과 맞지 않는 경우가 많기 때문에 잘 안쓸 뿐이다.

동적 타입 언어 중에서 자바스크립트는 암시적으로 자료형을 마구 변환해주지만 기능보다는 설계결함으로 간주된다. 정적 타입 언어 중에서는 C가 이런 무분별한 암시적 형변환으로 악명높다. 알아서 변환해준다는게 좋게들릴지 몰라도 막상 써보면 괴로워지는 경우가 더 많다.

정적 타입 언어가 별 거 아닌 것처럼 느껴질 수도 있지만 실은 프로그래머들을 짜증나게 하는 주범이 바로 형 변환(Type casting)이기 때문에 동적 타입 언어는 이런 점에서 매우 강점을 가진다. 특히 객체 지향 언어에서는 동적 타입 및 그것의 일반화버전이라 할 수 있는 덕 타이핑(Duck typing)이 프로그래머에게 수많은 혜택을 준다. 예를 들어 오리라는 타입과 닭이라는 타입이 있고 둘 다 날아오르는 기능이 있다면 정적 타입 언어에서는 상위 인터페이스를 추출하는 등의 부가 작업이 필요한데 덕 타이핑을 지원하는 언어에서는 그냥 넣어버리면 알아서 난다. 물론 단점도 있는데 고래 같이 못 나는 타입을 집어넣으면 실행시간 오류(런타임 에러)를 뱉어버린다는 것. 정적 타입 언어는 이런 문제가 없다...고 알려져 있지만 거짓말이고 정적 타입 언어도 닭은 닭인데 통닭 같이 못 나는 타입을 집어넣는 바람에(기술적으로는 해당 메서드가 구현이 안된 객체) 런타임 에러가 나올 수 있다.

그러나 이건 현실을 전부 고려하지 않은 반쪽짜리 시각이고, 요즘은 정적 타입 언어가 동적 타입 언어보다 훨씬 생산적이고 오류가 날 가능성을 줄여준다는 점이 정설로 굳어지고 있다. 당장 동적 타입 언어로 유명한 파이썬과 자바스크립트[24]가 정적 타입으로 옮겨가려는 움직임을 보이는 것으로 알 수 있지 않을까? 위에 있는 예시는 거의 모두 현실과는 동떨어진 예제와 설명이다. 위에서 든 상위 인터페이스 추출이나 메서드 미구현 문제는 정적 타입의 문제가 아니라 객체지향의 문제로, 정적 타입과 하등 상관없고 오히려 동적 타입으로 객체지향 개발을 하려고 할 때 더 흔히 일어나는 문제들이다. 강하고 안전한 정적 타입 시스템을 지원하는 언어는 대부분의 일상적 프로그래밍 오류를 미연에 방지해준다. 가령 가장 기본적인 동작인 함수 호출의 사례만 봐도 이러한 측면을 쉽게 이해할 수 있다. 동적 타입 언어는 그 어떤 쓰레기 값을 인자로 사용해서 호출해도, 심지어 아예 인자의 갯수마저 틀려버려도 그 호출 코드를 런타임에 아무 의심없이 실행해버리고 예상치 못한 동작을 하며 디버깅에 애를 먹이기 일쑤다. 하지만 정적 타입 언어는 잘못된 호출을 하고 있다고 컴파일 시에 오류를 친절히 알려준다[25]. 하스켈을 비롯한 강-정적타입 언어 사용자들이 "컴파일이 된다면 버그는 없다"고 하는 말이 빈 말이 아닌 것이다.

단순 사용 편의성 측면을 봐도, 최신 IDE 및 개발 도구들이 제공하는 코드 자동 완성이나 리팩토링, 정의 이동 등의 기능들은 컴파일 타임에 타입을 제대로 추론할 수 없으면 거의 동작하지 못한다. 똑같은 프로젝트[26]를 진행해도 이런 고생산성 기능들을 제대로 활용하느냐 못하느냐에 따라 개발 기간이나 유지보수성이 천지 차이로 벌어진다.

참고로 하스켈(Haskell)은 정적 타입 언어다. 그런데도 하스켈 코드에는 형 변환 연산자가 없다. 이게 어찌된 일이냐 하면 하스켈 언어는 정적 타입 언어이지만 강력한 타입 추론 기능을 내장하고 있어 형변환을 언어 차원에서 자동으로 해 준다. 동적 타입 언어 역시 자동 형변환을 제공하는데 무슨 차이가 있냐면 그 형변환을 런타임에 하느냐 컴파일 타임에 하느냐의 차이다. 즉 버그가 발생하는 시점을 런타임(사용할 때)에서 컴파일 타임(만들 때)으로 끌어당긴다. 타입 추론 엔진은 딱히 하스켈 같은 선언형, 함수형 언어에만 도입할 수 있는 건 아니므로 신형 설계가 적용된 언어라면 명령형, 객체 지향 언어라도 타입 추론을 할 수 있다. 단지 정적 타입 언어로 유명한 C나 자바에 타입 추론 엔진이 없을 뿐이고, C#이나 Kotlin[27] 등의 고생산성 정적 타입 언어들은 얼마든지 타입 추론을 지원하고 있다. 여기서 타입 추론은 묵시적 형 변환과 동의어가 아니다. 겉으로 보이는 건 묵시적 형 변환하고 똑같지만.

하스켈은 형 변환을 하는 게 아니라 형 변환도 타입 선언도 둘 다 필요 없도록 추론을 통해 한 번에 맞는 타입을 부여해준다. 이를 묵시적 형 변환처럼 쓰려고 하는 건 큰 착각인데, 같은 타입으로 볼 수 없는 두 위치에서 같은 변수를 사용하면 묵시적 형 변환이 되는 게 아니라 컴파일 오류를 내뱉는다. 하스켈이나 ML에 쓰인 타입 추론 엔진은 Hindley-Milner 시스템이라 불리는데, 명령형 언어 중에서는 Rust가 이를 적용했었다(현재는 아님).

4.5. 프로그래밍 패러다임[편집]


파일:나무위키상세내용.png   자세한 내용은 프로그래밍 패러다임 문서를 참고하십시오.


4.5.1. 명령형 언어[편집]


명령형 언어(Imperative Language)는 프로그램이 어떻게 작업을 수행할지를 표현하는 프로그래밍 언어이다. C, Java, C++, Fortran 등의 언어가 여기에 속한다. 명령형 언어는 다시 절차적 언어와 객체 지향 언어로 나뉜다.

4.5.1.1. 절차적 언어[편집]

Procedural Language. 프로그램을 프로시저(Procedure)의 단위로 나누어 문제를 해결하는 방식으로 작성하는 프로그래밍 언어. 대표적인 언어는 C와 Pascal. 종종 객체지향 언어의 상대적 개념으로 절차지향 언어란 말을 쓰기도 하는데, 이는 객체지향 언어가 등장한 이후에 나타난 것으로 보인다. 이름의 절차는 '명령이 순차적으로 실행되어야 함'을 지칭하는 절차가 아니라, 'Procedure 단위로 특정 명령을 절차화해야 함'을 말한다. 즉 어셈블리어 등의 순차 제어 개념에서 벗어나 명령을 함수 단위로 묶어 관리함을 말하는 용어다.

애초에 C++나 Java 등의 객체지향 언어가 널리 알려지기 전에는 C 나 Pascal을 절차지향 언어라고 부르지는 않았다. 그보다는 구조적 프로그래밍 언어란 말이 C나 Pascal을 가리키는 말이었다. C++, Java 같은 객체지향 언어는 객체개념 구현 자체가 프로그램언어 개발의 지향점 중 하나였지만, C 나 Pascal 등의 언어는 절차지향을 목표로 개발된 언어가 아니다. 따라서, 객체지향을 구현하지 않은 기존 언어를 가리키는 말로 절차지향 언어보다는 절차적 언어가 적합한 용어일 것이다.


4.5.1.2. 객체 지향 언어[편집]

객체 지향 언어는 프로그래밍을 함에 있어서 데이터와 그 데이터를 처리할 메소드를 한데 묶어 객체를 만들고 객체들을 조립하는 것을 목표로 한 언어들을 말한다. 대표적으로 Java와 C++가 여기에 속한다. 객체 지향 언어의 특징은 추상화, 캡슐화, 상속성, 다형성이 있다. 추상화는 외부 인터페이스만 제공하고 객체 내부를 숨겨서 어떻게 일을 하는지 몰라도 결과를 내보낸다.[28] 캡슐화는 객체 내부에 필요한 데이터등을 묶어서 한번에 관리 할 수 있게 해준다.[29] 상속은 모객체를 상속받아 추가 기능을 더 붙이거나 약간의 수정을 가한 객체를 만들 수 있다.[30] (서브타입) 다형성은 메소드 이름은 같더라도 매개변수의 유무, 매개변수의 개수, 매개변수의 자료형, 반환하는 값의 자료형에 따라 다른 메소드가 실행될 수 있다는 것이다. 즉, 메소드명이 같아도 실제 행위는 타입에 따라 다를 수 있다는 것이며, 상향 형변환(upcasting) 및 동적 바인딩이라는 개념과 관련이 있는 특성이다.
파일:나무위키상세내용.png   자세한 내용은 객체 지향 프로그래밍 문서를 참고하십시오.


4.5.2. 선언형 언어[편집]


선언형 언어(Declarative language)는 명령형 언어와 대비되는 개념으로, 함수형 언어와 논리 프로그래밍(Logic programming)등이 여기에 속한다. 현재 학계를 떠나 슬슬 업계 전반으로 확산되고 있다. 특히 신기술의 도입이 빠른 웹 앱 계열에서 선언형 스타일의 프로그램이 선호되고 있는데 JavaScript 코드의 코딩 스타일이 점점 선언형으로 변화하고 있다. 선언형으로 기술하는 가장 유명한 라이브러리로는 제이쿼리(jQuery)가 있으며 앵귤러JS(AngularJS)는 선언형 언어의 가장 최신 트렌드인 반응형 프로그래밍 개념을 도입하고 있다. (2-Way binding이라는 이름으로 소개하고 있다.)

순수 선언형 언어의 특징으로는 참조 투명성(referential transparency)[31]가 꼽힌다.

또한 선언형 언어에는 '지연 평가(Lazy evaluation)' 이라는 강력한 특징이 있다. 계산을 필요한 그 순간이 올 때까지 미룬다는 개념인데, 이 개념은 선언형 언어에만 있다. 단 선언형 언어 전부가 지연 평가를 지원하는 건 아니다. 이 지연 평가 개념의 강력함은 무한을 다룰 때 나타난다. 예를 들어 입력 데이터로 자연수 전체의 집합을 정의해서 대입하는 게 가능하다! 명령형 언어에서는 무한을 대입하면 말 그대로 무한히 계산을 하기 때문에 프로그램이 무한 루프를 돌며 멈춰 버리지만 지연 평가를 지원하는 언어에서는 만약 계산식의 마지막이 "...해당 리스트의 첫 5개를 출력." 하는 식으로 끝났다면 그 '첫 5개'를 찾아내기 위한 계산만을 수행하고 끝낸다. 예를 들어 "피보나치 수열의 10번째 항부터 30개 항을 출력하라"는 알고리즘을 구현할 때 명령형 언어라면 피보나치 수열을 생성하는 알고리즘 자체에 루프를 멈추는 코드를 삽입해야 하지만 지연 평가가 지원되는 선언형 언어의 경우 피보나치 수열 알고리즘은 "제네레이터"로 만들어 무한수열을 출력하게 하고 필터로 원하는 위치의 리스트를 뽑아주면 된다. 물론 당연히 "피보나치 수열의 마지막 다섯 개를 출력." 하는 식으로 짰다면 지연 평가고 나발이고 이쪽도 무한루프 돌며 프로그램이 멈춰 버린다. 피보나치 수열은 무한수열이기 때문에 "마지막"이 없기 때문.

선언형 언어가 아니어도 지연 평가를 구현하는 방법이 있긴 하다. 더티 플래그(dirty flag) 기법이 대표적. 단지 언어에서 직접 지원하지 않을 뿐이다.

4.5.2.1. 함수형 언어[편집]

파일:external/img.viralpatel.net/xkcd-functional.png

왜 함수형 프로그래밍이 좋은데? 도대체 어느 점이 마음에 드는거야?

꼬리재귀.

명령형 언어가 튜링머신에 기반하고 있다면, 함수형 언어는 람다 칼큘러스에 기반하고 있는 언어에 대한 총칭이다. 현업에서 많이 쓰이는 명령형 언어와는 대조적으로 몇가지 특징이 있다.

  • 순수 함수형 언어는 변수가 없다.
순수 함수형 언어에는 변수와 변수를 바꾸는 대입 연산자(C 언어를 예로 들면
=
)가 없다. 명령형 언어에서
a=3
이 a 에 3 을 대입하라는 명령인 반면, 순수 함수형 언어에서는
a=3
을 수학에서
let a be 3.
같이 a 를 3 으로 '정의'하는 것으로 본다. 즉 한번 a를 뭐라고 정의했으면 그 정의는 유효 범위 내에서 값이 바뀌지 않는다! 그 유효 범위 안쪽에서 a를 다시 재정의 할 수도 있지만 내부 유효범위 한해서 'a'의 정의가 바뀌는거지 대입되는게 아니다. 안쪽과 바깥쪽이 서로 다른 의미의 'a'가 되는 것.

이게 불러오는 가장 큰 차이가 명령의 '순서'가 의미없다는 점이다. 명령형 언어에서는 맨 윗줄에
a=3
이 있더라도 저 아래에 등장하는 a 가 여전히 3임을 보장할 수가 없다. 그렇기 때문에 a 값을 다른 값으로 업데이트 하기 '전'과 '후'의 결과 자체가 완전히 달라지고 순서가 매우 중요하지만, 순수 함수형 언어에서는 첫줄에
a=3
이 있으면 scope(유효범위) 전체에서 a 는 그대로 3 이다.[32] 그렇다면 굳이 a 를 사용하기 '전'에 미리 정의할 필요가 없고, scope 내의 아무곳에나 정의가 되어있기만 하면 그걸 그냥 갖다 쓰는 방식으로도 아무런 문제가 없다.

이런 특징에서 나오는 장점으로 표현식의 의미가 명료해진다는 것이 있다. 또, 제어 흐름을 생각하지 않고 프로그래밍 할 수 있다는 장점이 있다. 디버깅을 할 때도, 명령형 언어에서 버그를 잡을 때는 변수들의 전후 변화를 생각하면서 머리를 싸맬 때, 함수형 언어는 scope만 잘 확인하면 쉽게 디버깅 할 수 있다. 절차형 언어와는 달리 눈에 핏발을 세우고 변수가 어떻게 변화하나 추적할 필요가 없다며 함수형 언어 팬들은 자랑하고는 한다.

  • destructive update
두번째로 동반되는 명령형 언어와의 차이점이 destructive update 인데, 예를 들어 일반적인 명령형 언어에서
a=a+1
는 a라는 변수에 a를 1만큼 증가시킨 값을 대입하라는 의미를 가지며 이 명령을 시행하는 시점에서 변수 a의 값이 바뀌게 된다.

하지만 순수 함수형 언어인 하스켈의 경우
a=a+1
는 a라는 변수를 정의하는 것이 아니라 a라는 함수를 정의한다. 출력하게 하면
a=a+1=(a+1)+1=((a+1)+1)+1=(((a+1)+1)+1)+1=...
이런 식으로 무한 루프에 빠져서 영원히 a의 값을 출력하지 못하다. 이것은 lazy evaluation 때문이 아닌, 함수형 언어의 특징이다. 타 언어에 익숙하다면 저 a가 변수처럼 보일 수 있으나 함수를 정의하는 문법이다.
f(x,y)
를 이변수함수로,
f(x)
를 일변수함수로 보듯이, a 역시
f(void)
같은 파라미터가 0 개인 함수이다. 함수형 언어에서의
a=3
은 그냥 C 언어에서의
int a(void) { return 3; }
으로 보면 된다. 사실 이런 특징이 일반 프로그래머 기준으로 좀 괴악하기때문에 대중적인 함수형 언어들중에는 순수성을 포기하고 명령형 언어적인 부분을 포함하여 destructive update 를 허용하는 경우도 있다. 이미 a의 다른 정의가 있었다면 컴파일러가 중복 정의가 있다며 에러를 뱉어낸다. 하스켈같은 경우, 아예
a=a
를 ⊥(논리학에서의 falsum) 로 정의한다.[33]

일반 프로그래머의 상식으로는 도저히 이해가 안 될 결정인데 일부러 난해한 프로그래밍 언어라도 만들 생각으로 만들었을까? 그게 아니고 함수형 언어는 수학의 '함수'를 프로그래밍 언어 설계에 적극적으로 반영한 것이다. 수학의 함수는 정의상 입력이 같으면 출력도 같다. 그러니까
f(x)=x+1
인 함수를 정의했다면
f(1)=2
다. 다른 값은 절대 나오지 않는다. 함수형 언어의 함수도 마찬가지로
function foo(1)
의 수행 결과가 2였다면 언제 어느때든
foo(1)
은 2만 나온다. 하지만 함수형 언어가 아닌 언어에서는
foo(1)
이 3도 나올 수 있고 4도 나올 수 있다. 그러니까 C언어로 치면 이런 함수에 해당한다.

int inc(int a) {
    static int c=0;
    c=c+a;
    return c;
}


이 함수에 1을 넣어 여러 번 호출하면 1, 2, 3, 4, 5, ...가 나온다. 순수 함수형 언어는 이게 안된다는 얘기다.

이 특징으로 얻는 이득으로 함수형 언어는 메모이제이션[34]이 가능하다. 그 함수를 호출한 파라메터(
f(x)
에서 x)을 알고 있고 그것을 수행한 결과를 안다면 다음에 호출할 때는 그냥 메모해둔 결과값을 돌려주면 된다. 만약 피보나치 수열재귀함수 구현에 메모이제이션을 적용하면 극단적으로 속도가 빨라지는데 아예 <math>O(2^n)</math>가 <math>O(n)</math>으로 바뀌어 버리는 마법같은 일이 벌어진다.[35] 물론 절차형 프로그램도 외부 상태에 전혀 의존하지 않는 순수 함수를 구현하면 캐싱이 가능하긴 한데 그게 언어 차원에서 보장이 되느냐 프로그래머가 의도해야 하느냐의 차이가 있다.

두번째로 입력이 같으면 출력이 같다는 게 언어 차원에서 보장되기 때문에 손쉽게 병렬화가 가능하다. 최근에 함수형 언어가 다시 각광받는 이유중에 하나로, 멀티코어 프로세싱이 요구되고 있는 현 상황에서 떠오르는 강력한 장점으로 꼽힌다. 멀티스레드 프로그래밍에서 버그 발생 원인은 스레드간 공유되는 메모리를 변경하는 것인데, 함수형 언어는 메모리가 불변이기 때문에 언어 차원에서 멀티스레드의 안정성이 보장되기 때문이다. 단 성능상으로는 이점이 없다.[36]

그리고 함수를 first-class datatype[37]으로 분류하기에 함수를 그냥 보통 변수 다루듯 할 수 있다. 즉 함수를 다른 함수에 인수로 바로 넘겨줄 수도 있고, 함수를 만드는 함수(함수를 반환값으로 가지는 함수)를 정의할 수도 있으며 생산성이 매우 뛰어나다. 코드가 매우 간결해지며[38] 버그가 잘 생기지 않는 견고한 코드가 나오는 경향이 있다.

자료구조 상 destructive update[39] 가 허용되지 않기 때문에 효율적인 자료구조와 알고리즘도 명령형 언어에 비해 상당히 달라지게 된다. 일반적으로 저런 destructive update를 사용하는 자료구조를 ephemeral data structure 라 하며, 순수 함수형 언어에서 사용되는 자료구조를 persistent data structure[40] 라 한다.

참고로 객체지향 언어와 함수형 언어는 서로 배타적인 개념이 아니라 얼마든지 섞어 쓸 수 있다. 최근 함수형 패러다임이 유명세를 타면서 C++이나 파이썬 등 명령형 언어들에서 앞다투어 함수형 언어의 기능을 탑재하고 있다. 또, F#, Scala, OCaml 같이 OOP와 함수형 프로그래밍을 짬뽕해놓은 멀티패러다임 언어들도 많다. 사실 JavaScript도 함수형 패러다임을 포함한다. 생산자-소비자 패턴과 같이, 객체 지향 언어에서 함수형 언어의 특징을 살린 패턴도 있다.[41] 그러니까 굳이 함수형 언어를 안 배우더라도 함수형 패러다임과 알고리즘 정도는 배워두면 어느정도 도움이 된다.

함수형 언어의 시초는 아주 옛날에 개발된 LISP부터 시작해서 그 방언 스킴(Scheme)등 이 있었고, 그 Scheme의 방언이며 자바 가상머신에서 실행되는 클로저(Clojure), 전화교환기용 언어에서 출발한 Erlang, 타입 검증용 언어에서 시작된 ML, 극단적인 언어 디자인으로 유명한 Haskell 등이 있다.

다만 함수형 프로그래밍이 완전한 메이저 패러다임이 될 수 있는지에 대해서는 회의적인 의견이 많다. 기본적으로 추상화 단계가 지나치게 높아져서 절대 다수의 개발자들에겐 코드 리딩이 어렵고, 기존의 자료구조를 상당히 들어엎어야 한다는 부담이 있다. 또한 함수형 프로그래밍을 추구하는 Clojure, Haskell 등은 대중성과는 한참 거리가 멀기 때문에 시장이 확대되기에도 무리가 있다.[42] 한 예로 C++만 해도 위에서 함수형 언어의 기능을 탑재하는 중이라고 했지만 여전히 업계에서는 잘 사용되지 않고 있으며, 함수형 언어의 장점인 병렬/분산 프로그래밍 또한 실제로는 기존 명령형 언어들의 기능을 가지고도 충분히 구현 가능한 경우가 대부분이다. 참고

4.5.2.2. 논리 프로그래밍[편집]

명령형 언어가 튜링머신에, 함수형 언어가 람다 칼큘러스에 기반하고 있다면, 논리 프로그래밍은 수리논리학의 First order logic(1차언어)를 모델로 사용하는 프로그래밍 언어의 총칭이다.

사실, 이바닥의 알파와 오메가인 Prolog 이 1차언어에 기반하고 있기때문에 이런식의 정의가 많이 쓰이지만, 실제론 Higher order logic, F-logic, linear logic 등 여러가지 다른 논리를 사용하는 언어도 있고, 이것들도 대부분 Prolog 에 기반하고 있는 경우가 많아서 다 논리 프로그래밍 언어라 한다.

Clause의 집합이 곧 프로그램이 되며, Clause는 이쪽에서 가장 유명한 Prolog 언어를 예로 들면, Head :- Body 형식으로 정의된다.

이것은 If Body, then Head 즉, To solve Head, solve Body 식으로 해석할 수 있다.

사실 일반 프로그래머 눈에 위의 함수형 언어보다 더더욱 괴악해보이게 마련인데, 그래도 튜링 컴플리트이며, C++ 등과 같은 General purpose 언어다.

함수형 언어에서 프로그램을 함수들의 집합으로 보고 있다면, 논리 프로그래밍에서는 프로그램을 공리들의 집합으로 보고 있다고 이해하면 된다.

명령형 언어와는 거리가 멀지만, 함수형 언어와는 의외로 가까운편이고 실제 코드도 꽤 비슷한 양상을 띈다.

실제 코딩시의 함수형 언어와 가장 큰 차이라면 아무래도 함수형 언어가 '함수'를 사용할때, 논리 프로그래밍 언어에서는 '관계'(Relation)[43]를 사용한다는 부분이 가장 큰 차이일것이다.

관계를 사용함으로서 연역되는 차이는, 함수의 경우 하나의 인풋에 하나의 출력값만을 보장하게끔 정의가 되어있지만, 관계는 이런 제약이 존재하지 않기때문에 보통 조건을 모두 만족시키는 결과값을 전부 내놓는다.

이런 특징때문에 Constraint programming 에도 많이 쓰이고, 특히 일반적인 프로그래머가 가장 쉽게 접할 수 있는 예시가 데이터베이스 쿼리이다.

다만, Prolog 은 논리 프로그래밍 언어지만, 순수 선언형 언어는 아니다.

Mercury 라는 순수 선언형 논리 프로그래밍 언어도 있고, 스페인에서 팍팍 밀어주는 Ciao 도 순수 선언형 서브시스템을 지원한다.
이쪽 언어들은 Prolog 을 제외하면 Prolog 을 기반으로 해서 여러가지 실험적인 확장을 시킨것들이 많기때문에, 유저 매뉴얼이 곧 논문인 경우가 많고, 수리논리학 이론을 잘 모를경우 접근하기 힘든것들이 대부분이다.

하지만, 그렇다고 이론단계에만 머물러 있는 프로그래밍 패러다임은 아니고, Sicstus 같은 상용 컴파일러도 있으며, NASA 같은곳에서도 사용하는등 의외로 쓰이는곳이 있는편이다.


4.5.2.3. 반응형 프로그래밍[편집]

Reactive programming. 데이터를 중심으로 사고하는 방식인데 같은 데이터 중심 시각의 OOP와 다른 점은 반응형 프로그래밍은 데이터의 흐름 즉 데이터 플로우(Data flow)에 더 관심을 갖는다. 반응형 프로그래밍 언어 중 가장 쉽게 볼 수 있는 것은 스프레드시트 프로그램인 엑셀이 있다. 프로그래밍 언어는 모름지기 텍스트 편집기로 작성하는 거라고 따지고 싶다면 ReactiveX라고 하는 게 있다.

반응형 프로그래밍에서는 값의 변화를 추적한다. 사실 반응형 프로그래밍의 기반은 함수형 프로그래밍으로, 함수형 프로그래밍의 '불변식' 개념에 기초한다. 반응형 프로그래밍에서 변수의 값을 바꾸면 해당 변수를 참조하는 모든 식들이 연쇄적으로 재평가되면서 스스로의 값을 갱신한다. 즉 프로그래머가 명시적으로 재계산 명령을 내리지 않는다!

OOP나 함수형, 논리형 프로그래밍과 배척하는 관계는 아니라는 점에 주의. OOP도 세터(Setter)메서드에 적절한 처리를 해 주면 반응형으로 만들 수 있다. 다만 반응형 프로그래밍을 직접 지원하는 언어나 라이브러리는 그런 적절한 처리를 자동으로 해 준다는 차이가 있다.

반응형 프로그램은 정의된 식이 사이클을 형성하지만 않는다면(예를 들어 A = B + 1, B = A + 1 같이 서로가 서로를 참조하는 두 정의가 있으면 사이클이 형성됐다고 한다) 모든 변수는 해당 변수를 정의한 식을 항상 만족한다. 일반적인 명령형 프로그램은 명시적으로 재계산을 수행해줘서 값을 동기화시켜줘야 한다. 따라서 함수에서 특정 값을 갱신하는 작업을 빼먹거나 계산 순서를 실수하면 버그가 발생한다.

반응형 프로그램은 외부 상태를 받아들이는 데에도 관대해서 하드웨어 클록을 변수로 받아들이는 등의 작업이 함수형 프로그램보다 쉬운 편이다. 함수형 프로그램은 함수가 자기 자신에 대해 항상 닫혀 있어야 하지만 반응형 프로그래밍 모델에는 그런 제약이 없다. 사실은 반응형 프로그래밍 모델에서의 함수는 함수형 프로그래밍처럼 자기 자신에 대해 닫혀 있지만 외부의 상태가 변하는 것까지 추적해서 자동 재계산을 수행한다.

단점은 언제 변할지 모르는 수많은 변수를 일일이 추적하다보니 컴퓨터 성능을 상당히 잡아먹는다는 것. 예를 들어 여러 개의 값이 한꺼번에 바뀔 때 명령형이나 함수형 모델에서는 세 값이 다 변할 때까지 기다렸다가 한 번 재계산하는 등의 융통성을 발휘할 수 있지만 반응형 모델은 하나 바뀔 때마다 재계산을 해 댄다. 이를 개선하기 위해 지연 평가 개념을 적극적으로 적용하고 있긴 하지만 그래도 실성능이 상당히 나쁘게 나오고 있다. 그래서 고속 처리가 요구되는 곳에서는 영 사용할 게 못되고 사용자의 입력에 반응하는 UI로직에나 사용할 만 하다. 물론 UI에만 사용하면 되는 문제이므로 게임 같은 고성능 소프트웨어를 개발할 때 이걸 못 쓰는 건 아니다. 사용자 입력을 처리하는 UI프론트엔드는 아무리 게임이라도 사람의 입력 속도는 컴퓨터 입장에서는 충분히 느리기 때문에 사용할 수 있다. 하지만 AI로직 같은데 사용하기에는 아직 성능상에 문제가 있다.



4.6. 특수 목적 언어[편집]


  • SQL - 관계형 데이터베이스에서 데이터를 조작하기 위해 사용되는 표준 언어로 여러 DBMS에서 지원한다.
  • CUDA - 엔비디아GPU를 제어하는 언어.
  • Prolog - 논리 증명 언어
  • Processing - 그래픽스 처리를 위한 언어
  • MATLAB - 공학용 시뮬레이션용 언어
  • PHP - 웹 사이트를 만드는데 특화된 언어
  • Verilog, VHDL - 디지털 하드웨어 설계를 위한 언어로, 반응형 프로그래밍에 속한다.
  • Verilog-A - 아날로그 하드웨어 설계를 위한 언어

특수 목적 언어의 특징은 해당 분야에서는 뛰어난 생산성을 보인다는 것이다. 하지만 해당 분야를 벗어나는 순간 개발이 불가능해지거나 오히려 더 난해해진다. 때문에 이런 언어는 아무도 범용 프로그래밍에 사용하지 않는다. 예를 들어, SQL로는 당장 지원 DB 외의 파일 조작이 매우 어렵다. (SQLite에서는 아예 불가능하다.) 따라서 프로젝트 하나를 하는 데에도 알게 모르게 여러 언어를 써가며 개발하게 되며, 보통은 일반 목적 언어(보통 C, Java 등) 하나에 여러 특수 목적 언어를 물려가며 사용한다. 마크업 언어까지 감안하면 하나의 언어만으로 프로그래밍 하는 일은 초보적인 프로그래밍을 제외하고는 거의 없다고 봐도 된다.


5. 회화 언어와의 관계[편집]


마법의 본질은 이해할 수 없는 자연과의 교섭이고 요청이야.

(중략)

그럼에도 선대들이 발견해온 자연이 '좋아하는' 구절들.

우주가 들으면 마음이 너그러워지는 이상한 말들. 그것이 영창일세.

"불꽃처럼, 비구름의 안과 같이, 왕관이 땅에 떨어지는 소리와 함께!"

반-바지 단편 백엔드 개발자인 내가 이세계로 떨어져 직업교육소를 찾은 건에서

마법의 주문과 컴퓨터 프로그래밍을 동일한 시각으로 본 단편이다.[44]

프로그래밍 언어는 소프트웨어 개발이라는 한정된 목적으로 쓰이고, 일생상활에서는 쓰이지 않는다. 또한 회화 언어보다는 문법이 엄격하고, 2인칭이 존재하지 않는 등 얼핏 보면 일상적으로 쓰는 자연어와 괴리감을 느낄 수 있다. 이는 데이터의 기술을 주요 목적으로 하는 마크업 언어에서도 느낄 수 있는 분위기다.

하지만 프로그래밍 언어와 자연어는 근본적으로 큰 차이가 없다. 비전공자 입장에서는 무슨 뜬금없는 소리인가 싶기도 하지만, 자연어와 프로그래밍 언어의 관계는 상당히 밀접하다. 애초에 프로그래밍 언어 역시 기계와의 '회화'를 위해 사람이 만들어 쓰는 말이며, 다만 프로그래밍 언어는 기계도 청자(聽者)라는 차이만 있을 뿐이다.[45] 그 예로 단어를 소리 단위로 쪼개는 음운론은 프로그래밍 언어로 치면 토큰 구성 이론과 같으며, 문장을 단어 단위로 쪼개는 통사론은 프로그래밍 코드 형태로 쓰인 텍스트를 토큰 단위로 쪼개는 컴파일러 이론과 완전히 동일하다. 기계 번역도 사실 프로그래밍 쪽에서 컴파일러, 인터프리터 등의 형태로 이미 적용되어 있던 것이 자연어로 옮겨간 것뿐이다. 알 토네리코 시리즈휴므노스어Ciel nosurge의 REON-4213 처럼 프로그래밍 언어와 자연어를 접목하는 시도도 있었으며, 로지반 역시 비슷한 노력의 산물이다.

말만 익힌다고 끝나는 게 아니라는 것도 회화 언어와 프로그래밍 언어의 공통점이다. 어떤 회화 언어에 충분히 숙달됐더라도 현지 문화, 법규 등을 익혀야 그 언어를 사용하는 현지에서 원활하게 생활할 수 있듯, 프로그래밍 언어 역시 그 언어에 적용할 수 있는 방법론이나 개발 분야에서의 프로토콜 등을 지속적으로 이해해야 실무에서 살아남을 수 있다. 많은 코더들이 실무에서 좌절하는 이유가 여기에 있으며, 그렇기에 번역가프로그래머도 모두 평생학습을 필요로 하는 직업이다.

실제로 초창기만 해도 존 폰 노이만처럼 기계어를 외국어 구사하듯 구사하는 사람이 많았고, 지금도 언어학과에서 프로그래밍 언어를 가르치기도 한다. 요즘이야 컴파일러 성능이 좋아져서 그런 걸 굳이 인지할 필요가 줄었다고는 하지만, 반대급부로 기계를 이해하지 못해 발적화를 한다거나 기본적인 디버그조차 하지 않아 문제가 발생한 사례들을 보자면, 딱히 필요성이 줄었다 보기도 어렵다. 또한 커뮤니케이션 능력 부족이 치명적인 원인이 되어 형편없는 결과물이 나오는 사례도 주변에 수두룩하다. 따라서 프로그램의 올바른 개발을 위해서는 회화 언어 구사 능력도 좋은 개발자가 있어야 한다.

코더가 양산되는 이유를 이런 관점에서 살펴보면 양판소가 비판받는 이유를 이해하기 쉽다. 다시 말해 프로그래밍은 소설작법과 유사한 측면이 있다. 버그가 발생했다는 것은 오역했다는 말과 같다고 볼 수 있으며, 발적화는 회화에서 아무 말 대잔치를 한 것과 동일하다 볼 수 있다. 특수 케이스를 생각하지 않은 버그는 뉘앙스를 감안하지 않은 오역, 의존 라이브러리의 오류는 사전의 오류 또는 문화 차이에서 발생한 오류로 볼 수 있다. MOTHER 2문사이드에서 나온 '예'와 '아니오'가 바뀐, 그래서 검은 닌텐도의 일환으로 평가받는 언어활용은 프로그래밍으로 치면 하드웨어 결함으로 발생한 버그와 유사하다. 더욱 섬뜩한 예로, 잘못된 언어 활용으로 발생하는 갈등은 악성코드와 유사하다.


그래서 몇몇 교육자들은 소스를 던져주고 한국어로 번역하여 제출하시오 라고 하는 교육자도 있다.

파일:CC-white.svg 이 문단의 내용 중 전체 또는 일부는
문서의 r153 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}}에서 가져왔습니다. 이전 역사 보러 가기
파일:CC-white.svg 이 문단의 내용 중 전체 또는 일부는 다른 문서에서 가져왔습니다.
[ 펼치기 · 접기 ]
문서의 r153 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)
문서의 r14 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)

문서의 r 판{{{#!wiki style="display: inline; display: none;"
, 번 문단}}} (이전 역사)




6. 개발툴[편집]


파일:나무위키상세내용.png   자세한 내용은 통합 개발 환경 문서를 참고하십시오.


예전에는 각각의 개발환경에 맞추어서 다른형태의 툴로 존재했지만 현 시점에서 대부분의 소프트웨어 개발툴들은 개발에 필요한 대부분의 요소가 통합되어 있는 형태로 배포된다. 이를 '통합 개발 환경'이라고 하며 보통은 IDE로도 부른다.

하지만 IDE가 무겁다보니 다수 개발자들은 코드 편집을 전문적으로 지원하는 텍스트 에디터를 쓰기도 한다. 일부 에디터는 문법 하이라이트를 기본으로 지원하고, 심지어는 플러그인만 잘 물리면 컴파일까지 할 수 있다. 이렇게 해도 IDE로 개발하는 것 보다는 훨씬 가볍기에 많이 활용되는 편이다. 보안상 문제나 재정상 문제로 IDE를 도입하기 어려울 때 역시 텍스트 에디터가 자주 활용된다.


7. 예제[편집]


파일:나무위키상세내용.png   자세한 내용은 프로그래밍 언어/예제 문서를 참고하십시오.


7.1. 난해한 프로그래밍 언어의 예제[편집]


파일:나무위키상세내용.png   자세한 내용은 프로그래밍 언어/예제/난해한 프로그래밍 언어 문서를 참고하십시오.


8. 오해[편집]


프로그래밍 언어에 관해 널리 퍼진 오해 중 하나는, '프로그래밍 언어만 알면 개발을 할 수 있다' 이다.

이는 완전히 틀린 말은 아니지만, 현실적으로 프로그래밍 언어는 '명령'(A일 경우 B를 하라 등)을 추상화할 뿐이며 초보자가 이러한 작은 명령들을 가지고 고수준의 프로그램을 제작하는 것은 바퀴를 재발명하는 일이 될 수도 있다. 실용적으로 사용되는 수준의 응용 프로그램(애플리케이션)을 제작하려면 단순히 언어만 배워서 끝이 아니라 적합한 프레임워크, 엔진 등과 여러 작업을 위한 라이브러리들, 넓게는 '자신이 무엇을 만들어야 하는지'에 대한 도메인 지식과 이를 어떻게 기술적으로 구현할지에 대한 소프트웨어 엔지니어링 지식이 필요하다.

예를 들어 백엔드 구축을 위해서는 각 언어별 웹 프레임워크가, 2D/3D 게임 개발을 위해서는 게임 엔진이, SSR 렌더링을 위해서는 템플릿 엔진(Jinja, Pug 등) 또는 관련 서버사이드 언어(JSP, PHP 등)에 대한 지식이, CSR 렌더링을 위해서는 웹 프런트엔드 프레임워크가, 모바일 개발을 위해서는 모바일 운영체제 별 API를 사용해야 한다.[46]

따라서 언어의 문법을 배우는 것은 실제 개발 생태계에서는 단지 기초에 불과한 과정이지만, 그만큼 초보자들이 가장 먼저 접하는 부분이며 좌절하기도 쉬운 부분이기에 인터넷에서 자신이 C언어를 안다고 스스로 해커를 자처하는 등의 오해가 흔히 발생하는 것이다. 마찬가지로 언어와 프레임워크를 구분하지 못하는 오해도 종종 발생하는데, 예를 들어 유니티 엔진의 스크립트를 작성하기 위해 C\#을 사용하는 것은 맞지만, 유니티 = C# 이 아니다. C#은 당초 닷넷 프레임워크에서 쓰이기 위해 만들어진 언어이며, 유니티의 C#은 실제로는 Mono위에서 돌아갔었다.[47]

초보자가 기본 문법만 배워도 텍스트 기반은 컴파일러 또는 기반이 되는 프레임워크나 API 위에서 신나게 돌릴 수 있다. 하지만 프로그래밍에 대해 전혀 모르는 친구에게 "내가 프로그램을 만들었는데 한 번 구경해볼래?" 라고 보여주기 위해서는 각 언어에 해당하는 GUI 프로그래밍[48]을 별도로 익혀야 한다. GUI로 표현이 되지 않은 프로그램은 일반인들로 하여금 "프로그래머가 뭔가를 만들었다고 해서 기대하고 봤더니 정작 검은 화면에 흰색 글씨만 출력되더라!" 인 상황이 될 수밖에 없는데, 이는 반만 맞다고 할 수 있다. 또다른 예로 프론트엔드는 눈에 보이는 가시적인 성과를 내니 진짜로 웹 개발자처럼 보인다. 반면 백엔드는 아무것도 하지 않는 것처럼 보일 수밖에 없다. 하지만 그렇다고 해서 백엔드가 필요없다고 주장하는 개발자는 단 한 명도 없을 것이다. 이처럼 프로그래머와 일반인의 시각차는 매우 크기에 오해를 줄이기 위해서는 서로의 노력을 매우 필요로 한다.

또한 초보자들이 많이 하는 오해 중 하나는 '어떤 프로그래밍 언어 = XX를 하기 위한 언어'로 단정짓는 것이다. 대표적인 오해로 JavaScript는 당시 웹 페이지에서 돌아가도록 만들어진 것이 맞지만, 2022년 현재는 단지 프런트 뿐만 아니라 백엔드나 머신러닝, 스크립팅, 웹 브라우저 확장 프로그래밍, GUI 개발, 클라우드 엣지 컴퓨팅 등 광범위한 분야에서 사용된다. 물론 실제 언어의 사용자층이나 생태계에 따라 '특정 분야에서 더욱 유리한' 언어가 있는 것도 사실이고 실제로 아예 특수 목적 용도로만 쓰이는 언어(SQL, R 등)가 있기도 하니 주의.

반대 경우로 여러 언어를 대충 배우고서 그것을 자신의 지식으로 착각하는 경우도 존재하는데, 상술했듯이 언어의 문법을 배우는 것은 해당 분야의 가장 기초적인 관문이다. 즉, 해당 언어 생태계에 익숙하지 못하다면 '자신이 생각하는 속도 그대로' 코드를 짤 수 없으며, 냄새나는 코드를 찾아낼 능력도 떨어지고, 그런 코드를 리팩토링할 수 있는 능력은 더더욱 떨어지며, 심지어는 남의 코드를 거의 읽지 못하거나 더욱 심하게는 자신이 쓴 코드도 읽어내지 못한다.[49]

비슷한 경우로 한 언어(주로 C++)에 대한 책(또는 강의)을 모두 읽었다고 언어를 모두 익혔다고 착각하는 경우가 있다. 예를 들어 XX 언어에 대한 책을 며칠만에 떼겠다고 계획을 세우는 경우가 있다. 다른 분야와는 다르게 프로그래밍 관련 서적은 하나의 책을 사전(reference)처럼 쓰는 것이 좋으며[50], 따라서 한 책을 (부분적으로) 10번 넘게 읽게 되는 경우도 흔하다. 이는 비단 책에만 한정되는 것이 아니며, 부트캠프 광고에서 흔히 볼 수 있는 'X 언어 100시간 완성' 따위의 캐치프레이즈는 실제론 말도 안 된다는 것을 알 수 있다. 단적으로 외국인을 대상으로 하는 한국어 강의에 '한국어 100시간 완성' 같은 제목을 쓴다면 코웃음밖에 더 나오겠는가?

결국 자신이 수십가지 언어를 모두 잘 안다고 말하는 사람은 자신의 신뢰를 스스로 떨어뜨리는 셈이 되며, 실제로도 각 언어를 입문자만도 못하는 경우가 비일비재하다. 여러 언어를(=폴리글롯) 능숙하게 사용하는 것은 분명 효율적이지만, 만약 실제로 여러 언어를 배우고 싶다면 2,3개 정도만 골라서 집중적으로 배우고 연습하는 것이 미래에 더욱 도움이 된다.

프로그래밍 언어마다 공통되는 개념이 존재하는 경우가 상당히 많지만, 그렇지 않은 경우 또한 존재한다. 대표적으로 파이썬 학습자들이 반드시 짚고 넘어가는 '일급 함수'[51] 라는 개념은 다른 수많은 언어들에도 존재하는데, C언어처럼 객체지향이 아닌 언어에서는 그 개념조차 존재하지 않는 경우도 있다. 그렇다고 해서 C언어나 포인터 문법을 두고 이 세상에 필요없는 언어나 문법이라고 생각하는 사람 또한 없을 것이다. 여러가지 언어를 알고 있다는 건 언어의 차이를 이해하고 강점, 단점을 명확히 파악하는 것에 있어 도움을 준다. 그리고 새로운 언어를 배울 적에도 자신이 이미 알고 있는 개념을 대입하여, 문법만 새로 배우면 나머지 또한 쉽게 배우는 접근성조차 향상시킬 수 있어 도움이 된다. 다만 그것이 절대적인 실력의 척도가 되지 못함 또한 분명하게 알아야 한다.

9. 기타[편집]




10. 둘러보기[편집]







파일:크리에이티브 커먼즈 라이선스__CC.png 이 문서의 내용 중 전체 또는 일부는 2023-12-24 10:52:31에 나무위키 프로그래밍 언어 문서에서 가져왔습니다.

[1] 파이썬자바스크립트같은 언어는 스크립트 언어이므로 인터프리터로 실행시간에 해석된다.[2] 괴델이 고안한 primitive recursive function에서는 termination이 가정되어 있다. 즉, 무한 루프와 같은 것이 불가능하다(!). 이는 불완전성 정리에서(그리고 실제 오늘날 수학에서) 모든 증명이 유한한 길이를 갖는 것을 가정하고 있기 때문이다. primitive recursive function에 mu-operator(말 그대로 최소값을 찾아주는 operator이다.)를 더하면 class of recursive functions로 진화하여 무한루프 등의 알고리즘을 다 표현할 수 있다.[3] 오늘날 C나 자바등의 언어에 익숙한 사람들은 이 While-programming이 가장 친숙하게 느껴질 것이다.[4] S, K, I 3개의 함수로 이루어진 언어이다. 튜링-완전 이기 때문에 다른 체제와 마찬가지로 모든 알고리즘을 표현하는 것이 가능하며, I는 S와 K로부터 연역되기 때문에 사실상 함수 2개로 이루어진 가장 간단한 수학적 프로그래밍 언어라 할 수 있다.[5] 증명이 아니기 때문에 theorem 같은 것이 아닌 thesis가 붙는다.[6] 이 사람은 수백년을 앞서나간 인간이다. 당시 기술력만 되었다면 그 시절에 이미 컴퓨터라는 것을 만들었을지도 모른다.[7] 조성호.초연결 사회를 위한 컴퓨터 개론.한빛아카데미.2020.pp.287[8] 또는 간단하게 이름(name)이라고도 부른다.[9] 특수어(special word)라고도 부른다.[10] 어셈블리어는 바로 컴퓨터가 이해할 수는 없지만 기계어와 1:1 대응이 되므로 저급언어다.[11] Sulong을 통해 JVM 바이트코드로 컴파일되어 JVM에서 실행되던지, Emscripten 등으로 WASM으로 컴파일되어 자바스크립트 VM에서 실행된다[12] 안드로이드의 ART, 또는 SubstrateVM을 통해 AOT 컴파일된다[13] ocamlc와 ocamlopt[14] Erlang과 Erlang HiPE[15] 이 때문에 Native Language라고 불리기도 한다.[16] 일례로, 사람들이 Java언어를 AOT컴파일하면 빨라질 것이라고 착각해, GraalVM으로 AOT컴파일한 후에 최초 실행시를 제외하고 전반적으로 더 느려짐에 당황하는 경우가 많다. AOT 컴파일러는 런타임에 JIT컴파일을 하지 않아도 되기 때문에 실행 직후에 이점이 있을 뿐, 본질적으로 JIT컴파일러보다 더 적은 정보만으로 최적화를 해야 해서 JIT컴파일이 끝난 다음의 결과물을 비교하면 오히려 일반적으로 성능상 떨어진다. # C를 비롯한 대중적인 언어 이외에 깊이 접해보지 못한 사람들이 흔히 하는 착각이라 예시를 들어 서술한다.[17] 대표적인 VM 구현체: HotSpot, OpenJ9[18] 대표적인 VM 구현체: V8, SpiderMonkey[19] 대표적인 VM 구현체: .NET, Mono[20] 실행 시간에 프로그램 자체에 대한 정보를 얻거나 수정하는 것을 뜻함[21] 대표적인 VM 구현체: CPython, Jython, PyPy 등[22] 대표적인 VM 구현체: MRI, JRuby[23] 대표적인 VM 구현체: Zend VM, HHVM[24] 정확히는 TypeScript로 개발하고 별도의 컴파일을 통해 JavaScript로 변환된 코드를 배포하게 되는 케이스. 컴파일이라는 용어를 보면 알겠지만 위의 '해석 방식에 따른 분류' 문단을 모호하게 만드는 예시 중 하나다.[25] IDE를 사용한다면 굳이 컴파일까지 할 것도 없이 코딩 중 코드에 빨간줄까지 그어주며 설명해준다.[26] 가령 JavaScript로 진행하는 프로젝트 vs. TypeScript로 진행하는 프로젝트. 전자의 경우는 값비싼 개발 도구를 사다 쓰는 거랑 그냥 메모장으로 코딩하는 게 사실상 별 차이가 없을 지경으로 답이 없는 개발 환경이다. 물론 무료 개발 도구에서 제공하는 기능들도 TypeScript 쪽이 압도적으로 풍부하다. 이 문단에서도 언급했듯 JavaScript는 뭘 지원해줄래야 할 수가 없는 구조이기 때문.[27] 이쪽은 아에 자바처럼 JVM에서의 동작이 고려된 언어다. 프로그래밍 언어와 그 구현체는 별개로 구분되어야 한다는 전형적인 예시 중 하나라고 할 수 있다.[28] 속사정은 상관없이 결과만 나오면 된다.[29] 각자 따로 자생하며 논다.[30] 파생 상품을 비교적 쉽게 만들 수 있다.[31] C의 #define을 상상하면 된다. scope 안에서 '='로 정의된 변수들은 아무생각없이 우항으로 대체하는것이 가능하다. 명령형 언어에서는 다시 대입되어 수정될 수 있기 때문에 불가능하다.[32] 하위 scope 에서 덮어쓰기는 가능하다. 함수형 언어에서는 이것을 shadow 라 표현한다. 명령형 언어에서는 포인터 등을 이용하여 하위 scope 에서 아예 상위 scope 변수 자체의 값을 바꿔버리는것도 가능하지만, 함수형 언어에서는 하위 scope 에서 shadow 된 값은 해당 scope 바깥에 절대 영향이 없다.[33] 물론, 물론, 언어 내의 Boolean type 의 false 가 아닌, 컴퓨팅 모델 자체의 false 값을 의미한다.[34] Memoization. Memorization이 아닌 것에 주의![35] 이를 동적 계획법(Dynamic Programming)이라고 한다.[36] 메세지 전달 모델은 명령형에서도 똑같이 쓸 수 있다.[37] 다른 데이터와 똑같은 취급을 받는다는 뜻. 대조적으로 자바의 메소드나 C의 함수를 생각해보자. 자바에서 인자로 메소드를 주거나 리턴받는것은 불가능하고, C에서도 포인터가 아니라 함수 자체를 넘겨주는건 불가능하다. 또한 이 개념은 언어 내장 데이터타입(Primitive)와는 다르다. 파이썬같이 분명히 함수형이 아닌 언어에서도 사용하는 경우는 있다.[38] 심지어 하스켈 같은 경우 너무 간결해서 오히려 이해하기가 어려운 것 같다는 불평이 나오기도 할 정도[39] 한번 정의한 변수의 값을 차후에 다른 값으로 업데이트 하는것.[40] 자료구조를 업데이트 할때마다 계속 새로 자료구조를 만드는 모델. 순수 함수형 언어의 경우 한번 정의한 값을 바꿀수가 없어서 이게 선택이 아닌 필연이 된다. fully persistent data structure 에서는 심지어 이전버전의 자료에도 접근할 수 있다.[41] 메시지(생산물)를 통해 메시지를 전달하고, 객체 내부의 변수에 대한 직접 접근을 막는다.[42] 그나마 Scala가 Java의 보조 언어로 쓰이고 있는 정도다.[43] 수학적으로, 함수는 관계의 부분집합이다.[44] 이 시각이 극명하게 드러난 예가 바로 Tell, Don't Ask(TDA; 묻지 않고 시키기) 원칙이며, 그 구현이 바로 람다식이다.[45] 게다가 이 차이점마저 ChatGPT 등 대화형 인공지능의 등장으로 더욱 모호해지고 있다. 프로그래밍 언어와 자연어 사이의 경계가 사실상 없어지는 셈이다.[46] 최근에는 풀스택 프레임워크의 비효율성을 지적하여 아예 마이크로 프레임워크를 중심으로 가르치는 강의 등도 존재한다. 풀스택과 마이크로 모두 일장일단이 있으니 어떤 것을 선택할지에 대해서는 학습자의 뜻에 달렸다.[47] 물론 이것도 이제는 옛말이 되었다.[48] 데스크탑용 UI라이브러리, 모바일 앱, 프런트엔드 뷰, OpenGL등의 그래픽 라이브러리, Plot*등 시각화 라이브러리 등[49] 쉽게 비유해 '봉주르'를 말할 줄 안다고 프랑스인과 자연스럽게 수다를 떨 수는 없는 것과 마찬가지이다. 언어의 문법과 함께 어휘, 청해, 숙어 그리고 실제 연습이 뒷받침되지 않는다면 실제 언어를 구사하기까지는 많은 시간이 걸린다.[50] 온라인 문서도 마찬가지다. 프로그래밍은 언어, 프레임워크, CLI 툴 등 종류를 막론하고 늘 방대한 분량의 매뉴얼이 필요하다.[51] 함수형 언어들의 람다와 비슷하면서도 미묘하게 다른 개념이다. 정확히는 람다는 이름을 가지고 있지 않는, 표현식(expression)으로 생성되는 함수이며 일급 함수는 기본 값(primitive types)과 똑같이 변수에 저장하거나 함수의 인자로 넘길 수 있음을 의미한다(C에서는 콜백 패턴을 쓰려면 함수 포인터를 사용해야 하는 점과 대비된다). 고차 함수에 인자로 '람다인 일급 함수'를 넘길 수 있지만 유명 함수(named function)도 똑같이 전달 가능하기 때문에 완전히 같은 개념은 아니다.