함수 매개 변수에 주석을 달지 않는 이유는 무엇입니까?


28

이 질문에 대답하기 위해 프로그래머의 마음에 드는 모호한 비용이 몇 번의 추가 키 입력보다 훨씬 비싸다고 가정 해 봅시다.

그렇다면 왜 팀원이 기능 매개 변수에 주석을 달지 않고 벗어날 수 있습니까? 훨씬 더 복잡한 코드가 될 수있는 예제로 다음 코드를 사용하십시오.

let foo x y = x + y

이제 툴팁을 빠르게 살펴보면 F #에서 x와 y를 정수로 결정했음을 알 수 있습니다. 그것이 당신이 의도 한 것이라면, 모든 것이 잘됩니다. 하지만 그게 당신이 의도인지 모른다. 두 개의 문자열을 연결하기 위해이 코드를 만든 경우 어떻게해야합니까? 아니면 아마도 복식을 추가 할 생각이라면 어떨까요? 또는 모든 단일 기능 매개 변수 위로 마우스를 가져 가서 유형을 결정하지 않으려면 어떻게해야합니까?

이제 이것을 예로 들어 보겠습니다.

let foo x y = "result: " + x + y

F #은 이제 문자열을 연결하려고 한 것으로 가정하므로 x와 y는 문자열로 정의됩니다. 그러나 코드를 유지 관리하는 빈약 한 schmuck은 이것을보고 x와 y (ints)를 함께 추가 한 다음 UI 목적으로 문자열에 결과를 추가하려고했는지 궁금 할 것입니다.

분명히 그러한 간단한 예를 들어 보자. 그러나 명시 적 유형 주석 정책을 시행하지 않는 이유는 무엇입니까?

let foo (x:string) (y:string) = "result: " + x + y

모호하지 않은 데 어떤 해가 있습니까? 물론 프로그래머는 그들이하려고하는 일에 대해 잘못된 유형을 선택할 수 있지만 적어도 그들이 의도 한 것은 알고 있지만 그것이 단순한 감독이 아니 었습니다.

이것은 심각한 질문입니다 ... 나는 여전히 F #을 처음 접했고 회사의 흔적을 타 오르고 있습니다. 내가 채택한 표준은 미래의 모든 F # 코딩의 기초가 될 것이며, 앞으로도 계속해서 문화에 스며들 것입니다.

그렇다면 ... F #의 형식 유추에 특별한 점이 있습니까? 필요할 때만 주석을 달아 소중한 가치를 지니고 있습니까? 또는 전문가 F # 사용자는 사소한 응용 프로그램에 대해 매개 변수에 주석을 달는 습관을 듭니까?


주석이 달린 버전은 덜 일반적입니다. 같은 이유로 서명에서 IEnumerable <>을 사용하고 SortedSet <>이 아닌 것이 좋습니다.
Patrick

2
@Patrick-아니요, F #은 문자열 유형을 유추하기 때문에 아닙니다. 함수의 서명을 변경하지 않았으며 명시 적으로 만들었습니다.
JDB

1
@ 패트릭-그리고 그것이 사실이더라도 왜 나쁜지 설명 할 수 있습니까? 프로그래머가 의도 한 것이라면 서명 덜 일반적 일 것입니다. 아마도 더 일반적인 서명이 실제로 문제를 일으키는 것일 수도 있지만, 프로그래머가 많은 연구를하지 않고 얼마나 구체적으로 의도했는지는 잘 모르겠습니다.
JDB

1
기능적 언어에서는 일반성을 선호하고 더 구체적인 경우에는 주석을 달았다 고 말하는 것이 공정하다고 생각합니다. 예를 들어 더 나은 성능을 원할 때 유형 힌트를 사용하여 얻을 수 있습니다. 어노테이션이있는 함수를 어디에서나 사용하려면 각 특정 유형에 대해 과부하를 작성해야하며, 일반적으로는 보증하지 않을 수 있습니다.
Robert Harvey

답변:


30

F #을 사용하지 않지만 Haskell에서는 언어에 널리 사용되는 형식 유추가 있지만 최상위 수준 정의에 주석을 달고 때로는 로컬 정의에 주석을 추가하는 것이 좋습니다. 몇 가지 이유가 있습니다.

  • 읽기
    함수를 사용하는 방법을 알고 싶을 때는 타입 서명을 사용하는 것이 매우 유용합니다. 직접 추론하려고하거나 도구를 사용하지 않고 간단히 읽을 수 있습니다.

  • 리팩토링
    함수를 변경하려고 할 때 명시 적 서명을 사용하면 변환에서 원래 코드의 의도를 보존 할 수 있습니다. 형식 유추 언어에서는 고도로 다형성 코드가 형식 검사를 수행하지만 의도 한대로 수행하지 않을 수 있습니다. 타입 시그니처는 인터페이스에서 타입 정보를 구체화하는 "장벽"입니다.

  • 성능
    Haskell에서 유추 된 함수 유형은 (유형 클래스를 통해) 오버로드 될 수 있으며, 이는 런타임 디스패치를 ​​의미 할 수 있습니다. 숫자 유형의 경우 기본 유형은 임의 정밀도 정수입니다. 이러한 기능의 전체 일반성이 필요하지 않은 경우 필요한 특정 유형에 맞게 기능을 전문화하여 성능을 향상시킬 수 있습니다.

로컬 정의, let-bound 변수 및 람다 형식 매개 변수의 경우 형식 서명의 코드 비용이 일반적으로 추가하는 값보다 더 비싸다 는 것을 알았습니다 . 따라서 코드 검토에서 최상위 정의의 서명을 주장하고 다른 곳 에서 신중한 주석을 요청하는 것이 좋습니다 .


3
이것은 매우 합리적이고 균형 잡힌 응답처럼 들립니다.
JDB

1
명시 적으로 정의 된 유형의 정의를 사용하면 리팩토링 할 때 도움이 될 수 있지만 단위 테스트 도이 문제를 해결할 수 있으며 단위 테스트를 사용하기 전에 함수가 설계된대로 작동하는지 확인하기 위해 함수 프로그래밍과 함께 단위 테스트를 사용해야한다고 생각합니다 함수를 다른 함수와 함께 사용하면 유형을 정의에서 벗어나고 주요 변경 사항을 적용하면 유형이 잡힐 것이라는 확신을 가질 수 있습니다.
Guy Coder

4
Haskell에서는 함수에 주석을 달는 것이 올바른 것으로 간주되지만 모호성을 제거하는 데 필요한 경우가 아니라면 관용적 F # 및 OCaml은 주석을 생략하는 경향이 있다고 생각합니다. 그것은 해롭다는 말은 아닙니다 (F #에서 주석을 달기위한 구문이 Haskell보다 더 나쁘지만).
KChaloux

4
@KChaloux OCaml에는 이미 인터페이스 ( .mli) 파일 에 타입 주석이 있습니다 (만약 당신이 강력히 권장하는 타입 주석이 있습니다. 타입 주석은 인터페이스와 중복되기 때문에 정의에서 생략되는 경향이 있습니다.
Gilles ' SO- 그만해라 '

1
@Gilles @KChaloux는 실제로 .fsi하나의 경고와 함께 F # 서명 ( ) 파일 에서 동일 합니다. F #은 형식 유추에 서명 파일을 사용하지 않으므로 구현에서 무언가가 모호한 경우 에도 다시 주석을 달아야합니다. books.google.ca/…
Yawar Amin

1

존은 여기서 반복하지 않을 합리적인 대답 을했습니다. 그러나 귀하에게 귀하의 요구를 충족시킬 수있는 옵션을 보여 드리고 그 과정에서 예 / 아니오 이외의 다른 유형의 답변을 보게됩니다.

최근에 나는 파서 결합기를 사용하여 파싱 작업을 해왔다. 파싱을 알고 있다면 일반적으로 첫 번째 단계에서 렉서를 사용하고 두 번째 단계에서 파서를 사용한다는 것을 알고 있습니다. 어휘 분석기는 텍스트를 토큰으로 변환하고 파서는 토큰을 AST로 변환합니다. 이제 F #이 기능 언어이고 결합기가 결합되도록 설계되었으므로 파서 결합기는 렉서와 파서에서 동일한 기능을 사용하도록 설계되었지만 파서 결합기 기능의 유형을 설정하면 lex 또는 구문 분석 및 둘 다 아닙니다.

예를 들면 다음과 같습니다.

/// Parser that requires a specific item.

// a (tok : 'a) : ('a list -> 'a * 'a list)                     // generic
// a (tok : string) : (string list -> string * string list)     // string
// a (tok : token)  : (token list  -> token  * token list)      // token

또는

/// Parses iterated left-associated binary operator.


// leftbin (prs : 'a -> 'b * 'c) (sep : 'c -> 'd * 'a) (cons : 'd -> 'b -> 'b -> 'b) (err : string) : ('a -> 'b * 'c)                                                                                    // generic
// leftbin (prs : string list -> string * string list) (sep : string list -> string * string list) (cons : string -> string -> string -> string) (err : string) : (string list -> string * string list)  // string
// leftbin (prs : token list  -> token  * token list)  (sep : token list  -> token  * token list)  (cons : token  -> token  -> token  -> token)  (err : string) : (token list  -> token  * token list)   // token

코드는 저작권이므로 여기에 포함하지 않지만 Github . 여기에 복사하지 마십시오.

함수가 작동하려면 일반 매개 변수를 남겨 두어야하지만 함수 사용에 따라 유추 된 유형을 보여주는 주석을 포함시킵니다. 이를 통해 유지 보수를위한 기능을 쉽게 이해할 수 있으며 기능을 일반적으로 사용할 수 있습니다.


1
왜 일반 버전을 함수에 쓰지 않습니까? 작동하지 않습니까?
svick

@svick 귀하의 질문을 이해하지 못합니다. 함수 정의에서 유형을 제외했지만 제네릭 형식이 함수의 의미를 변경하지 않기 때문에 제네릭 형식을 함수 정의에 추가했을 수 있습니까? 그렇다면 그 이유는 개인적인 취향에 더 가깝습니다. F #을 처음 시작했을 때 추가했습니다. 고급 F # 사용자와 작업을 시작했을 때 서명없이 코드를 수정하기가 쉽기 때문에 주석으로 남겨두기를 선호했습니다. ...
Guy Coder

장기적으로는 코드가 작동하면 주석이 모든 사람에게 효과가 있음을 보여줍니다. 함수 작성을 시작하면 유형을 입력합니다. 함수가 작동하면 형식 서명을 주석으로 옮기고 가능한 많은 유형을 제거합니다. F #을 사용하는 경우 추론을 돕기 위해 유형을 그대로 두어야합니다. 잠시 동안 let과 =로 주석을 작성하여 실험을 위해 줄을 주석 해제 한 다음 다시 주석을 달았습니다.하지만 바보처럼 보이고 let을 추가하는 것은 어렵지 않습니다.
가이 코더

이러한 의견이 귀하가 요청한 내용에 답변하는 경우 답변으로 이동할 수 있도록 알려주십시오.
가이 코더

2
따라서 코드를 수정할 때 주석을 수정하지 않아 오래된 문서가 생성됩니까? 그것은 나에게 이점처럼 들리지 않습니다. 그리고 나는 지저분한 코드보다 지저분한 주석이 어떻게 더 나은지 실제로 보지 못합니다. 나 에게이 접근법은 두 가지 옵션의 단점을 결합한 것 같습니다.
svick

-8

버그 수는 프로그램의 문자 수에 정비례합니다!

이것은 일반적으로 코드 라인에 비례하는 버그 수로 표시됩니다. 그러나 같은 양의 코드로 줄이 적 으면 같은 양의 버그가 발생합니다.

따라서 명시 적으로 표현하는 것이 좋지만 입력 한 추가 매개 변수로 인해 버그가 발생할 수 있습니다.


5
“버그 수는 프로그램의 문자 수에 정비례합니다!”따라서 모두 APL로 전환해야 합니까? 너무 간결한 것은 너무 장황한 것처럼 문제입니다.
svick

5
" 이 질문에 대답하기 위해 프로그래머의 마음에 드는 모호한 비용이 몇 번의 추가 키 입력보다 훨씬 비싸다고 가정 해 봅시다. "
JDB
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.