대부분의 프로그래밍 언어에 함수 선언을위한 특수 키워드 또는 구문이있는 이유는 무엇입니까? [닫은]


39

대부분의 프로그래밍 언어 (동적 및 정적 유형의 언어 모두)에는 함수 선언을위한 변수 선언과 크게 다른 특수 키워드 및 / 또는 구문이 있습니다. 다른 명명 된 엔티티를 선언하는 것처럼 함수를 봅니다.

예를 들어 파이썬에서 :

x = 2
y = addOne(x)
def addOne(number): 
  return number + 1

왜 안되 겠어요 :

x = 2
y = addOne(x)
addOne = (number) => 
  return number + 1

마찬가지로 Java와 같은 언어로 :

int x = 2;
int y = addOne(x);

int addOne(int x) {
  return x + 1;
}

왜 안되 겠어요 :

int x = 2;
int y = addOne(x);
(int => int) addOne = (x) => {
  return x + 1;
}

이 구문은 무언가 (함수 또는 변수)와 일부 언어에서 def또는 키워드와 같이 키워드를 선언하는보다 자연스러운 방법으로 보입니다 function. 그리고 IMO는 더 일관성이 있으며 (변수 또는 함수의 유형을 이해하기 위해 같은 장소를 찾습니다) 아마도 구문 분석기 / 문법을 조금 더 작성하기가 더 쉽습니다.

이 아이디어를 사용하는 언어는 거의 없지만 (CoffeeScript, Haskell) 가장 일반적인 언어에는 함수 (Java, C ++, Python, JavaScript, C #, PHP, Ruby)에 대한 특수 구문이 있습니다.

스칼라에서도 두 가지 방식을 모두 지원하고 형식 유추가 가능합니다.

def addOne(x: Int) = x + 1

오히려 :

val addOne = (x: Int) => x + 1

스칼라에서 적어도 IMO는 아마도 가장 이해하기 쉬운 버전이지만이 관용구는 거의 따르지 않습니다.

val x: Int = 1
val y: Int = addOne(x)
val addOne: (Int => Int) = x => x + 1

나는 내 장난감 언어로 일하고 있는데 그런 방식으로 언어를 디자인 할 때 어떤 함정이 있는지, 그리고 역사적 또는 기술적 이유가 있다면이 패턴이 널리 따르지 않는지 궁금합니다.


9
그 이유는 역사적인 것 같습니다. 누가 먼저 그렇게했는지, 다른 사람들은 모두 복사했습니다. 그러나 우리가 확실하게 알 수 있을지는 의문입니다.

29
함수 나 메소드가 단순히 또 다른 명명 된 엔티티 가 아니기 때문이라고 생각합니다 . 기능적 언어에서는 (당신이 전달할 수 있습니다.)하지만 다른 언어 (Java와 같은)에서는 함수 또는 메소드가 간단한 변수와 완전히 다르며 그렇게 취급 될 수 없습니다 (Java 8 종류의 약점을 인정합니다. 함수 / 메소드가 다르게 동작 하므로 다르게 정의하는 것이 좋습니다 .
11684

15
귀하의 제안이 기능을 선언하는 더 자연스러운 방법이라는 데 동의하지 않습니다. 나는 Coffeescript의 구문을 정말로 싫어하며 그중 일부는 함수 선언 구문과 관련이 있습니다. 사실, 그것이 깨지지 않으면 고치지 마십시오. 'def'또는 'function'을 작성하는 것은 큰 문제가 아니며 생각하지 않거나 피곤한 눈으로 쉽게 다른 것으로 착각 할 수있는 멋진 Perl과 같은 기호에 비해 한 눈에 더 분명합니다. 완전히 개인적인 메모에서 나는 예제에서 제안 된 구문이 현재 구문보다 훨씬 나쁘다고 생각합니다.
Roy

22
'=>'가 " 함수 선언을위한 특수 키워드 또는 구문 "이 아닌 방법은 무엇입니까?
TML

11
나는 당신에 대해 모르지만, 나 (int => int) addOne = (x) => {보다 훨씬 더 "특별하고"복잡한 것입니다 int addOne(int) {.
Bogdan Alexandru

답변:


48

그 이유는 대부분의 인기있는 언어가 기능 언어와 그 뿌리 인 람다 미적분학과 달리 C 언어의 언어에서 왔거나 영향을 받았기 때문이라고 생각합니다.

그리고이 언어들에서 함수는 또 다른 가치 가 아닙니다 :

  • C ++, C # 및 Java에서는 함수를 오버로드 할 수 있습니다. 이름은 같지만 서명이 다른 두 개의 함수를 가질 수 있습니다.
  • C, C ++, C # 및 Java에서 함수를 나타내는 값을 가질 수 있지만 함수 포인터, 펑터, 델리게이트 및 함수 인터페이스는 모두 함수 자체와 다릅니다. 그 이유 중 일부는 대부분이 실제로 함수가 아니라 일부 (변경 가능) 상태와 함께 함수이기 때문입니다.
  • 변수는 기본 (당신이 사용할 필요에 의해 변경할 수 있습니다 const, readonly또는 final돌연변이를 금지하는),하지만 기능은 재 할당 할 수 없습니다.
  • 보다 기술적 인 관점에서 코드 (함수로 구성됨)와 데이터는 분리되어 있습니다. 그들은 일반적으로 메모리의 다른 부분을 차지하며 다르게 액세스됩니다 : 코드는 한 번로드 된 다음 실행 (하지만 읽기 또는 쓰기는 아님) 반면 데이터는 종종 지속적으로 할당 및 할당 해제되고 쓰기 및 읽기는하지만 실행되지는 않습니다.

    그리고 C는 "금속에 가깝게"되어야했기 때문에 언어의 구문에서도 이러한 차이를 반영하는 것이 합리적입니다.

  • 함수형 프로그래밍의 기초를 형성하는 "함수는 단지 가치"접근 방식으로 C ++, C # 및 Java에서 람다가 늦게 소개됨에 따라 비교적 최근에 공통 언어에서 인기를 얻었습니다 (2011, 2007, 2014).


12
2005 년부터 - 타입 추론과 투박한 구문없이 단지 람다 있습니다 - C #은 익명 함수를했다
에릭 Lippert의에게

2
"보다 기술적 인 관점에서 볼 때 코드 (함수로 구성됨)와 데이터는 분리되어 있습니다."일부 언어에서 LISP는 이러한 분리를하지 않으며 코드와 데이터를 너무 다르게 취급하지 않습니다. (LISP는 가장 잘 알려진 예이지만이를 수행하는 REBOL과 같은 다른 언어가 많이 있습니다.)
Benjamin Gruenbaum

1
@ BenjaminGruenbaum 언어 수준이 아니라 메모리의 컴파일 된 코드에 대해 이야기했습니다.
svick

C에는 함수 포인터가 있는데,이 포인터는 최소한 다른 값으로 간주 될 수 있습니다. 확실하게 엉망으로 만드는 위험한 가치이지만 낯선 일이 일어났습니다.
Patrick Hughes

@PatrickHughes 네, 두 번째 요점에서 언급합니다. 그러나 함수 포인터는 함수와 상당히 다릅니다.
svick

59

인간 이 기능이 "또 다른 명명 된 실체"가 아니라는 것을 인식하는 것이 중요하기 때문 입니다. 때때로 그것들을 그렇게 조작하는 것이 합리적이지만 여전히 한눈에 알아볼 수 있습니다.

컴퓨터가 이해하기 어려운 문자를 해석하는 것이 좋기 때문에 컴퓨터가 구문에 대해 어떻게 생각하는지는 중요하지 않지만 인간이 이해하고 유지하는 것은 거의 불가능합니다.

그것은 왜 우리가 while 및 for 루프, 스위치 및 기타를 가지고 있는지와 같은 이유입니다. 그 이유는 코드를 유지하고 이해하는 인간의 이익을 위해 있기 때문입니다.

제안한 방식대로 함수를 "다른 명명 된 엔터티"로 사용하면 코드를보기 어렵게되어 이해하기가 더 어려워집니다.


12
나는라는 이름의 단체 등의 기능을 치료하는 것을 동의 반드시 코드를 어렵게 이해할 수 있습니다. 절차 적 패러다임을 따르는 모든 코드에는 거의 해당되지만 기능적 패러다임을 따르는 코드에는 해당되지 않을 수 있습니다.
Kyle Strand

29
"왜 우리가 while 및 for 루프, 스위치 및 기타를 가지고 있는지와 같은 이유입니다. 비록 그것들이 궁극적으로 비교 및 ​​점프 명령으로 귀결됩니다. "+1. 모든 프로그래머가 기계 언어에 익숙하다면이 고급 프로그래밍 언어 (높은 수준 또는 낮은 수준)가 모두 필요한 것은 아닙니다. 그러나 진실은 모든 사람들이 자신의 코드를 작성하려는 기계와 얼마나 가까운 지에 대해 다른 안락함을 가지고 있다는 것입니다. 레벨을 찾으면 주어진 구문을 고수해야합니다 (또는 실제로 귀찮은 경우 자신의 언어 사양과 컴파일러를 작성하십시오).
Hoki

12
+1. 더 간결한 버전은 "왜 모든 자연 언어가 명사와 동사를 구별합니까?"
msw

2
"이해할 수없는 인물의 얼룩은 기계가 해석하기에는 좋지만 인간이 이해하고 유지하기에는 거의 불가능할 것입니다." 구문에 빠르게 적응합니다. 이 제안은 OO 메소드 호출 구문 object.method (args) 가 그 당시의 관례보다 훨씬 급격하게 벗어 났지만, 후자는 인간이 이해하고 유지할 수없는 것으로 판명되었습니다. 대부분의 OO 언어에서 메소드는 클래스 인스턴스에 저장된 것으로 생각해서는 안됩니다.
Marc van Leeuwen

4
@MarcvanLeeuwen. 귀하의 의견은 사실이며 합리적이지만 원래 게시물이 수정되는 방식에 따라 약간의 혼란이 발생합니다. 실제로 두 가지 질문이 있습니다. (i) 왜 이런 식입니까? 그리고 (ii) 왜 이런 식으로 대신하지 않습니까? . 대답 whatsisname은 첫 번째 요점을 더 많이 다루고 (이러한 안전 장치를 제거 할 수있는 위험에 대해 경고 함) 의견은 질문의 두 번째 부분과 더 관련이 있습니다. (당신이 설명하고, 그것은 ... 이미 여러 번 해본 적이있다)이 구문을 변경 실제로 가능하지만 그것을하지 않습니다 정장 모두 (같은 OOP가 하나하지 정장 모두를 수행가.
호키

10

선사 시대를 거슬러 올라가면 ALGOL 68이라는 언어는 당신이 제안한 것에 가까운 구문을 사용했다는 것을 배우고 싶을 것입니다. 함수 식별자가 다른 식별자와 마찬가지로 값에 바인딩되어 있음을 인식하면 해당 언어에서 구문을 사용하여 함수 (일정)를 선언 할 수 있습니다.

함수 유형 이름 = ( 매개 변수 목록 ) 결과 유형 : body ;

구체적으로 당신의 예는

PROC (INT)INT add one = (INT n) INT: n+1;

선언의 RHS에서 초기 유형을 읽을 수 있다는 중복성을 인식하고 함수 유형은 항상로 시작 PROC합니다.

PROC add one = (INT n) INT: n+1;

그러나 =여전히 매개 변수 목록 앞에 옵니다 . 또한 함수 변수 (동일한 함수 유형의 다른 값을 나중에 할당 할 수 있는)를 원할 =경우을로 :=바꿔서 다음 중 하나를 제공 해야합니다.

PROC (INT)INT func var := (INT n) INT: n+1;
PROC func var := (INT n) INT: n+1;

그러나이 경우 두 형식은 사실상 약어입니다. 식별자 func var는 로컬로 생성 된 함수에 대한 참조를 지정하므로 완전히 확장 된 형식은

REF PROC (INT)INT func var = LOC PROC (INT)INT := (INT n) INT: n+1;

이 특정 구문 형식은 익숙해지기 쉽지만 다른 프로그래밍 언어에서는 크게 뒤지지 않았습니다. Haskell과 같은 기능적 프로그래밍 언어조차도 매개 변수 목록 f n = n+1= 따르는 스타일 을 선호 합니다. 그 이유는 주로 심리적이라고 생각합니다. 결국 모든 수학자조차도 종종처럼 f = nn + 1을 f ( n ) = n + 1 보다 선호하지 않습니다 .

그런데, 상기 설명은 강조 표시 함수와 변수 간의 하나의 중요한 차이점을 수행 보통 함수 정의 바인드 변수 정의는 일반적으로 식별자 도입 반면 후에 변경할 수없는 하나의 특정 함수 값 이름, 초기 값이지만 하나 그 나중에 변경할 수 있습니다. (절대적인 규칙은 아니며 함수 변수 및 비 기능 상수는 대부분의 언어에서 발생합니다.) 또한 컴파일 된 언어에서 함수 정의에 바인딩 된 값은 일반적으로 컴파일 타임 상수이므로 함수를 호출 할 수 있습니다. 코드에서 고정 주소를 사용하여 컴파일되었습니다. C / C ++에서 이것은 심지어 요구 사항이기도합니다. ALGOL 68에 해당

PROC (REAL) REAL f = IF mood=sunny THEN sin ELSE cos FI;

함수 포인터를 도입하지 않으면 C ++로 작성할 수 없습니다. 이러한 종류의 특정 제한은 함수 정의에 다른 구문을 사용하여 정당화됩니다. 그러나 그것들은 언어 의미론에 의존하며 모든 언어에 타당성이 적용되는 것은 아닙니다.


1
"수학자는 선호하지 않는 F을 = NN + 이상 (1) F ( N ) = N 쓰기 전용 좋아 물리학, 언급하지 않기 위하여 ... + 1" f를 = N + 1 ...
leftaroundabout

1
@leftaroundabout은 ⟼가 쓰기 힘들 기 때문입니다. 솔직히
Pierre Arlaud

8

예를 들어 Java와 Scala를 언급했습니다. 그러나 당신은 중요한 사실을 간과했습니다. 그것들은 기능이 아니며, 방법입니다. 방법과 기능은 근본적으로 다릅니다. 함수는 객체이고 메소드는 객체에 속합니다.

함수와 메소드가 모두있는 스칼라에서는 메소드와 함수간에 다음과 같은 차이점이 있습니다.

  • 메소드는 일반적 일 수 있고 함수는 할 수 없습니다
  • 메소드는 하나 이상의 매개 변수 목록을 가질 수 없으며, 함수는 항상 정확히 하나의 매개 변수 목록을 갖습니다.
  • 메서드는 암시 적 매개 변수 목록을 가질 수 있으며 함수는
  • 메소드는 기본 인수가있는 선택적 매개 변수를 가질 수 있으며 함수는
  • 메소드는 반복되는 매개 변수를 가질 수 있으며 함수는
  • 메서드는 이름 별 매개 변수를 가질 수 있고 함수는
  • 명명 된 인수를 사용하여 메서드를 호출 할 수 있지만 함수는
  • 함수는 객체이고 메소드는 그렇지 않습니다

따라서 제안 된 대체품은 최소한 그러한 경우에는 작동하지 않습니다.


2
"함수는 객체이며, 메소드는 객체에 속합니다." C ++와 같은 모든 언어에 적용됩니까?
svick

9
"메소드는 이름 별 매개 변수를 가질 수 있고 함수는 할 수 없습니다"-이것은 메소드와 함수의 근본적인 차이가 아니며 스칼라의 단점 일뿐입니다. 다른 대부분의 (모두?)와 동일합니다.
user253751

1
메소드는 기본적으로 특별한 종류의 함수입니다. -1. Java (및 확장명 Scala)에는 다른 종류의 기능이 없습니다. "함수"는 하나의 메소드를 가진 객체입니다 (일반적으로 함수는 객체 또는 일류 값이 아닐 수 있습니다 (언어에 따라 다름)).
Jan Hudec

@JanHudec : 의미 적으로 자바는 함수와 메소드를 모두 가지고있다. 함수를 언급 할 때 "정적 방법"이라는 용어를 사용하지만 이러한 용어는 의미 상 구별을 지우지 않습니다.
supercat

3

내가 생각할 수있는 이유는 다음과 같습니다.

  • 컴파일러가 선언 한 내용을 쉽게 알 수 있습니다.
  • 이것이 함수인지 변수인지 (사소한 방법으로) 아는 것이 중요합니다. 함수는 일반적으로 블랙 박스이며 내부 구현에 신경 쓰지 않습니다. Scala의 반환 유형에 대한 유형 유추를 싫어합니다. 반환 유형이있는 함수를 사용하는 것이 더 쉽다고 생각하기 때문에 종종 제공되는 유일한 문서입니다.
  • 그리고 가장 중요한 것은 프로그래밍 언어 설계에 사용되는 다음과 같은 군중 전략입니다. C ++은 C 프로그래머를 훔치기 위해 만들어졌으며, Java는 C ++ 프로그래머를 겁내지 않는 방식으로, C #은 Java 프로그래머를 끌어들이도록 설계되었습니다. 놀라운 팀이있는 매우 현대적인 언어라고 생각하는 C #조차도 Java 또는 C의 실수를 복사했습니다.

2
"… 그리고 Java 프로그래머를 유치하기위한 C #"-의심 스럽다. C #은 Java보다 C ++과 훨씬 유사하다. 때로는 자바와 의도적으로 호환되지 않는 것처럼 보입니다 (와일드 카드, 내부 클래스, 반환 유형 공분산 없음)
Sarge Borsch

1
@SargeBorsch 의도적으로 Java와 호환되지 않는다고 생각하지 않습니다 .C # 팀이 단순히 올바르게하려고하거나 더 잘하려고했지만 확실하게 보려고합니다. Microsoft는 이미 자체 Java 및 JVM을 작성하여 고소당했습니다. 그래서 C #을 팀은 가능성이되었다 매우 자바 일식 동기 부여. 확실히 호환되지 않으며 더 좋습니다. Java는 몇 가지 기본 제한 사항으로 시작했으며 C # 팀이 예를 들어 제네릭을 다르게 수행하기 위해 선택했습니다 (설탕뿐만 아니라 유형 시스템에서도 볼 수 있음).
codenheim

2

RAM을 많이 사용하지 않는 컴퓨터에서 소스 코드를 편집하려고하거나 플로피 디스크에서 읽을 수있는 시간을 최소화하려는 데 관심이없는 경우 키워드를 사용하면 무엇이 문제입니까?

확실히 x=y+z보다 읽는 것이 더 store the value of y plus z into x좋지만, 문장 부호 문자가 본질적으로 키워드보다 "더 나은"것을 의미하지는 않습니다. 변수가 있다면 i, j하고 k있다 Integer, 그리고 x이며 Real, 파스칼에서 다음 줄을 고려 :

k := i div j;
x := i/j;

첫 번째 줄은 잘린 정수 나누기를 수행하고 두 번째 줄은 실수 나누기를 수행합니다. Pascal은 div이미 다른 목적 (실수 나누기)이있는 문장 부호를 사용하지 않고 잘린 정수 나누기 연산자로 사용하기 때문에 구별 할 수 있습니다 .

함수 정의를 간결하게 만드는 데 도움이되는 몇 가지 컨텍스트가 있지만 (예 : 다른 표현식의 일부로 사용되는 람다) 함수는 일반적으로 눈에 잘 띄고 함수로 쉽게 시각적으로 인식 할 수 있어야합니다. 구별을 훨씬 더 미묘하게 만들고 구두점 문자 만 사용하는 것이 가능할 수 있지만 요점은 무엇입니까? 말하면 Function Foo(A,B: Integer; C: Real): String함수의 이름이 무엇인지, 예상되는 매개 변수 및 반환되는 내용이 무엇인지 명확하게 알 수 있습니다. Function구두점 문자 로 대체 하여 6 ~ 7 자 정도 단축 할 수는 있지만 무엇을 얻을 수 있습니까?

주목해야 할 또 다른 점은 이름을 항상 특정 메소드 또는 특정 가상 바인딩과 연관시키는 선언과 특정 메소드 또는 바인딩을 처음 식별하는 변수를 작성하는 선언 사이에는 근본적인 차이점이 있다는 것입니다. 런타임시 다른 것을 식별하기 위해 변경 될 수 있습니다 . 이것들은 대부분의 절차 적 프레임 워크에서 매우 의미 상 매우 다른 개념이므로 서로 다른 구문을 가져야한다는 것이 합리적입니다.


3
나는이 질문이 간결함에 관한 것이라고 생각하지 않습니다. 특히 예를 들어 void f() {}C ++ ( auto f = [](){};), C # ( Action f = () => {};) 및 Java ( Runnable f = () -> {};) 의 람다보다 실제로 짧기 때문 입니다. 람다의 간결함은 형식 유추와 생략에서 비롯 return되지만이 질문이 묻는 것과 관련이 있다고 생각하지 않습니다.
svick

2

아마도 그 언어는 충분히 기능하지 못하기 때문일 것입니다. 다시 말해 함수를 거의 정의하지 않는 것입니다. 따라서 추가 키워드를 사용할 수 있습니다.

ML 또는 Miranda Heritage (OTOH)의 언어로 대부분의 시간에 기능을 정의합니다. 예를 들어, 하스켈 코드를보십시오. 말 그대로 대부분 함수 정의 시퀀스이며, 많은 함수는 로컬 함수와 로컬 함수의 로컬 함수를 가지고 있습니다. 따라서 Haskell 의 재미있는 키워드는 assign 으로 시작하기 위해 명령형 언어로 주장 진술을 요구하는 것만 큼 큰 실수 입니다. 원인 할당은 아마도 가장 빈번한 진술 일 것입니다.


1

개인적으로, 나는 당신의 생각에 치명적인 결함이 보이지 않습니다. 새로운 구문을 사용하여 특정 것을 표현하는 것이 예상보다 까다 롭거나 수정해야 할 수도 있습니다 (다양한 특수 사례 및 기타 기능 추가 등). 아이디어를 완전히 포기해야합니다.

제안한 구문은 수학에서 함수 또는 함수 유형을 표현하는 데 종종 사용되는 일부 표기법 스타일의 변형과 비슷합니다. 이는 모든 문법과 마찬가지로 다른 프로그래머보다 일부 프로그래머에게 더 호소력이 있음을 의미합니다. (수학자로서 나는 그것을 좋아한다.)

그러나 대부분의 언어에서 def-style 구문 (예 : 기존 구문) 표준 변수 할당과 다르게 작동합니다.

  • 에서 CC++가족, 함수는 일반적으로 즉 "객체", 입력 데이터의 덩어리가 복사 스택 및 이것 저것에 넣어하기로 취급되지 않습니다. (예, 함수 포인터를 사용할 수 있지만 일반적인 의미에서 "데이터"가 아닌 실행 코드를 가리 킵니다.)
  • 대부분의 OO 언어에는 메소드 (예 : 멤버 함수)에 대한 특수 처리가 있습니다. 즉, 클래스 정의 범위 내에서 선언 된 함수가 아닙니다. 가장 중요한 차이점은 메서드가 호출되는 객체가 일반적으로 암시적인 첫 번째 매개 변수로 메서드에 전달된다는 것입니다. 파이썬은 이것을 명시 적으로 만듭니다 self(실제로는 키워드가 아니며 유효한 식별자를 메소드의 첫 번째 인수로 만들 수 있습니다).

새 구문이 컴파일러 또는 인터프리터가 실제로 수행하는 작업을 정확하고 직관적으로 나타내는 지 여부를 고려해야합니다. 루비의 람다와 메소드의 차이점을 읽는 것이 도움이 될 수 있습니다. 이것은 당신의 함수가 단지 데이터 패러다임이 전형적인 OO / 절차 패러다임과 어떻게 다른지에 대한 아이디어를 줄 것입니다.


1

일부 언어의 경우 함수는 값이 아닙니다. 그런 언어로

val f = << i : int  ... >> ;

함수 정의 인 반면

val a = 1 ;

하나의 구문을 사용하여 두 가지를 의미하기 때문에 상수를 선언하고 혼란 스럽습니다.

ML, Haskell 및 Scheme과 같은 다른 언어는 함수를 1 클래스 값으로 취급하지만 함수 값 상수를 선언하는 특수 구문을 사용자에게 제공합니다. 즉, 구문이 일반적이고 장황한 경우 사용자에게 속기를 제공해야합니다. 사용자에게 정확히 동일한 것을 의미하는 두 가지 다른 구문을 제공하는 것은 부적절합니다. 때로는 우아함을 유용하게 희생해야합니다.

귀하의 언어로, 기능이 1 등분이라면, 구문 설탕을 찾는 유혹을받지 않을 정도로 간결한 구문을 찾으십시오.

-- 편집하다 --

아무도 제기하지 않은 또 다른 문제는 아직 재귀입니다. 당신이 허용하는 경우

{ 
    val f = << i : int ... g(i-1) ... >> ;
    val g = << j : int ... f(i-1) ... >> ;
    f(x)
}

그리고 당신은 허용

{
    val a = 42 ;
    val b = a + 1 ;
    a
} ,

그것은 당신이 허용해야한다는 것을 따르고 있습니까?

{
    val a = b + 1 ; 
    val b = a - 1 ;
    a
} ?

Haskell과 같은 게으른 언어에서는 여기에 문제가 없습니다. 본질적으로 정적 검사가없는 언어 (예 : LISP)에는 문제가 없습니다. 그러나 정적으로 확인 된 간절한 언어에서는 처음 두 개를 허용하고 마지막 두 개를 금지하려면 정적 검사 규칙을 정의하는 방법에주의해야합니다.

-편집 끝-

* 하스켈이이 목록에 속해 있지 않다고 주장 할 수 있습니다. 함수를 선언하는 두 가지 방법을 제공하지만 두 가지 방법 모두 다른 유형의 상수를 선언하는 구문의 일반화입니다.


0

이것은 유형이 그렇게 중요하지 않은 동적 언어에서는 유용 할 수 있지만 항상 변수 유형을 알고 싶은 정적 유형 언어에서는 읽을 수 없습니다. 또한 객체 지향 언어에서는 지원되는 연산을 알기 위해 변수 유형을 아는 것이 중요합니다.

귀하의 경우 4 변수를 가진 함수는 다음과 같습니다

(int, long, double, String => int) addOne = (x, y, z, s) => {
  return x + 1;
}

함수 헤더를보고 (x, y, z, s)를 볼 때 이러한 변수의 유형을 모릅니다. 유형이 z세 번째 매개 변수 인 유형을 알고 싶다면 함수의 시작 부분을보고 1, 2, 3을 세고 유형이임을 확인해야합니다 double. 전자의 방식으로 나는 직접보고 참조하십시오 double z.


3
물론 타입 추론이라는 멋진 것이 있는데, 이것은 var addOne = (int x, long y, double z, String s) => { x + 1 }당신이 선택한 비모 론적 정적 타입 언어 (예 : C #, C ++, 스칼라) 와 같은 것을 쓸 수있게합니다 . C #에서 사용하는 매우 제한된 로컬 형식 유추조차도 이것으로 충분합니다. 따라서이 답변은 처음에는 모호한 특정 구문을 비판하고 실제로는 어디에도 사용되지 않습니다 (하스켈의 구문은 매우 유사한 문제가 있음).
amon

4
@amon 그의 대답은 구문을 비판 할 수도 있지만, 질문의 목적 구문이 모든 사람에게 "자연적"이 아닐 수도 있다고 지적합니다.

1
@amon 타입 추론의 유용성은 모든 사람에게 훌륭한 것은 아닙니다. 코드를 작성할 때보 다 코드를 더 자주 읽는 경우 코드를 읽을 때 머리에서 유형을 유추해야하므로 형식 유추가 잘못됩니다. 실제로 어떤 유형이 사용되는지 생각하는 것보다 유형을 읽는 것이 더 빠릅니다.
m3th0dman

1
비판은 나에게 유효한 것 같습니다. Java의 경우 OP는 [입력, 출력, 이름, 입력]이 단순히 [출력, 이름, 입력]보다 더 간단하고 명확한 함수 정의라고 생각하는 것 같습니다. @ m3th0dman은 서명이 길어지면서 OP의 '더 깨끗한'접근 방식이 매우 길어졌습니다.
Michael

0

대부분의 언어에서 이러한 구별을해야하는 매우 간단한 이유가 있습니다 . 평가선언 을 구별 할 필요가 있습니다. 귀하의 예가 좋습니다 : 왜 변수를 좋아하지 않습니까? 변수 표현식은 즉시 평가됩니다.

Haskell은 평가와 선언이 구별되지 않는 특별한 모델을 가지고 있으므로 특별한 키워드가 필요하지 않습니다.


2
그러나 람다 함수에 대한 일부 구문도이 문제를 해결합니다.
svick

0

함수는 다르게 사용되거나 다르게 디버깅되며 잠재적 인 오류의 원인이 다양하기 때문에 대부분의 언어에서 리터럴, 객체 등과 다르게 선언됩니다.

동적 객체 참조 또는 변경 가능한 객체가 함수에 전달되면 함수가 실행될 때 객체의 값을 변경할 수 있습니다. 이러한 종류의 부작용은 복잡한 표현식 내에 중첩 된 함수가 수행 할 작업을 따르기가 어려워 질 수 있으며 이는 C ++ 및 Java와 같은 언어에서 일반적인 문제입니다.

모든 객체에 toString () 연산이있는 Java에서 일종의 커널 모듈을 디버깅하는 것을 고려하십시오. toString () 메소드는 오브젝트를 복원해야하지만, 값을 String 오브젝트로 변환하려면 오브젝트를 분해하고 재 조립해야합니다. toString ()이 (후크 앤 템플레이트 시나리오에서) 호출하여 메소드를 디버깅하려고 시도하고 실수로 대부분의 IDE 변수 창에서 객체를 강조 표시하면 디버거가 충돌 할 수 있습니다. IDE가 디버깅 과정에서 코드를 호출하는 객체를 toString ()하려고 시도하기 때문입니다. 프리미티브 값의 의미 적 의미는 프로그래머가 아닌 언어로 정의되므로 프리미티브 값은 이와 같이 허약하지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.