문제

저는 C# 개발자입니다. "실제 하스켈" 함수형 프로그래밍을 진정으로 이해하기 위해서는 F#을 배울 때 단순히 "F#에 C# 코드를 작성"하는 것이 아니라 실제로 이해하게 될 것입니다.

글쎄, 오늘 나는 세 번이나 이해했다고 생각했던 예를 발견했는데, 그러다가 내가 놓친 것을 보고 해석을 업데이트하고 반복했습니다(그리고 저를 욕하기도 했습니다).

이제 나는 그것을 실제로 이해했다고 믿고 아래에 자세한 "영어 해석"을 작성했습니다.Haskell 전문가 여러분, 이해한 내용을 확인해 주시거나 제가 놓친 점을 지적해 주실 수 있습니까?

메모:Haskell 코드 조각(책에서 직접 인용)은 내장된 Haskell 목록 유형과 동형인 사용자 정의 유형을 정의합니다.

하스켈 코드 조각

data List a = Cons a (List a)
              | Nil
              defining Show

편집하다:몇 가지 답변을 들은 후에 제가 한 가지 오해를 발견했습니다. 하지만 그 실수를 바로잡을 하스켈 "파싱" 규칙에 대해서는 명확하지 않습니다.그래서 나는 원래의 (잘못된) 해석을 아래에 포함시켰고, 그 뒤에 수정을 가한 다음, 여전히 나에게 불분명하게 남아 있는 질문을 포함시켰습니다.

편집하다:여기 스니펫에 대한 나의 원래(잘못된) "영어 해석"이 있습니다.

  1. "List"라는 유형을 정의하고 있습니다.
  2. 목록 유형이 매개변수화됩니다.단일 유형 매개변수가 있습니다.
  3. List의 인스턴스를 만드는 데 사용할 수 있는 2개의 값 생성자가 있습니다.한 값 생성자는 "Nil"이라고 하고 다른 값 생성자는 "Cons"라고 합니다.
  4. "Nil" 값 생성자를 사용하는 경우 필드가 없습니다.
  5. "Cons" 값 생성자에는 단일 유형 매개변수가 있습니다.
  6. "Cons" 값 생성자를 사용하는 경우 2개의 필드를 제공해야 합니다.첫 번째 필수 필드는 List의 인스턴스입니다.두 번째 필수 필드는 a의 인스턴스입니다.
  7. (나는 "쇼 정의"에 대해 지금 집중하고 싶은 부분이 아니기 때문에 의도적으로 생략했습니다.)

수정된 해석은 다음과 같습니다(굵은 글씨로 변경됨).

  1. "List"라는 유형을 정의하고 있습니다.
  2. 목록 유형이 매개변수화됩니다.단일 유형 매개 변수가 있습니다.
  3. 목록 인스턴스를 만드는 데 사용할 수있는 2 개의 값 생성자가 있습니다.하나의 값 생성자를 "NIL"이라고하고 다른 값 생성자를 "CONS"라고합니다.
  4. "Nil" 값 생성자를 사용하는 경우 필드가 없습니다.

    5.(이 줄은 삭제되었습니다 ...정확하지 않습니다.) "Cons" 값 생성자에는 단일 유형 매개변수가 있습니다.

  5. "Cons"값 생성자를 사용하는 경우 제공되어야하는 2 개의 필드가 있습니다.첫 번째 필수 필드는 a의 인스턴스입니다.두 번째 필수 필드는 "A 목록"인스턴스입니다.

  6. (나는 "쇼 정의"에 대해 지금 집중하고 싶은 부분이 아니기 때문에 의도적으로 생략했습니다.)

아직도 불분명한 질문

초기 혼란은 "Cons a (List a)"라고 표시된 부분에 관한 것이었습니다.사실 그 부분은 아직까지 나에게 불분명한 부분이다.

사람들은 "Cons" 토큰 뒤의 줄에 있는 각 항목이 다음과 같다는 점을 지적했습니다. 유형, 값이 아닙니다.즉, 이 줄은 "Cons 값 생성자에는 2개의 필드가 있습니다.하나는 'a' 유형이고 다른 하나는 'list-of-a' 유형입니다."

알아두면 매우 도움이 됩니다.그러나 아직 뭔가 불분명합니다.CONS 값 생성자를 사용하여 인스턴스를 생성 할 때, 해당 인스턴스는 "첫 번째 'A'를"여기에 전달 된 값을 배치 "하는 것으로"해석하십시오. " 그러나 그들은합니다 ~ 아니다 두 번째 'a'도 같은 방식으로 해석하세요.

예를 들어 다음 GHCI 세션을 생각해 보세요.

*Main> Cons 0 Nil
Cons 0 Nil
*Main> Cons 1 it
Cons 1 (Cons 0 Nil)
*Main> 

"Cons 0 Nil"을 입력하면 "Cons" 값 생성자를 사용하여 List의 인스턴스가 생성됩니다.0부터 유형 매개변수가 "Integer"임을 학습합니다.지금까지는 혼란이 없었습니다.

그러나 그것은 또한 다음을 결정합니다. Cons의 첫 번째 필드는 0입니다.그래도 결정한다 아무것도 아님 대한 두 번째 필드의 ...두 번째 필드에 유형 "목록 정수"의.

그래서 내 질문은 첫 번째 필드의 "a"가 "이 필드의 유형이 'a'임을 의미하는 이유는 무엇입니까? 그리고 이 필드의 값은 'a''이고, 두 번째 필드의 'a'는 다음을 의미합니다. 오직 "이 필드의 유형은 'List of a'입니다."?

편집하다:여러 가지 답변 덕분에 이제 빛을 보았다고 생각합니다.여기서 그것을 분명히 설명하겠습니다.(그리고 만약 그렇다면 아직 잘못된 부분이 있으니 꼭 알려주세요!)

"Cons a (List a)" 조각에서는 "Cons" 값 생성자에 두 개의 필드가 있고 첫 번째 필드는 'a' 유형이고 두 번째 필드는 'List of a' 유형임을 나타냅니다. '.

그것이 우리가 말하는 전부입니다! 특히 우리가 말하는 것은 아무것도 아님 가치에 대해!이것이 제가 놓친 핵심 포인트입니다.

나중에 "Cons" 값 생성자를 사용하여 인스턴스를 생성하려고 합니다.우리는 이것을 인터프리터에 입력합니다:"단점 0 없음".이것 명시적으로 Cons 값 생성자에게 첫 번째 필드의 값으로 0을 사용하고 두 번째 필드의 값으로 Nil을 사용하도록 지시합니다.

그리고 그게 전부입니다.값 생성자 정의가 다음을 지정한다는 것을 알고 나면 유형 외에는 아무것도 없습니다, 모든 것이 명확해집니다.

유용한 답변을 주신 모든 분들께 감사드립니다.그리고 제가 말했듯이, 여전히 문제가 있는 경우 꼭 알려주시기 바랍니다.감사해요.

도움이 되었습니까?

해결책

  • "Cons" 값 생성자에는 단일 유형 매개변수가 있습니다.

아니요:선언할 때 이미 매개변수화했습니다. data List a.이것의 효과적인 속성 중 하나는 Nil ::이 있는 경우입니다.List Int, Nil과 교환할 수 없습니다. ::목록 문자.

  • "Cons" 값 생성자를 사용하는 경우 2개의 필드를 제공해야 합니다.첫 번째 필수 필드는 List의 인스턴스입니다.두 번째 필수 필드는 a의 인스턴스입니다.

당신은 그것을 교환했습니다 :첫 번째 필수 필드는 a의 인스턴스이고 두 번째 필드는 List의 인스턴스입니다.

Real World Haskell의 이 장 관심이 있을 수 있습니다.

감사해요.그것이 제가 지금 진행하고 있는 장입니다.그래서 ...코드에 "Cons a (List a)"라고 표시되면 "Cons a" 부분이 Cons 값 생성자가 매개변수화되었음을 나타내는 것이라고 생각했습니다.그들은 아직 매개변수화된 유형에 대한 구문을 다루지 않았으므로 a를 사용하려는 경우 구문에서 "a"를 다시 지정해야 한다고 추측했습니다.그런데 그럴 필요가 없다는 말씀이신가요?그러면 "a"는 그런 뜻이 아니죠?

아니요.우리가 우리 유형에서 매개 변수를 선언하면, 우리는 "그 유형을 사용해야한다"고 말하기 위해 그것을 재사용 할 수 있습니다. 약간 비슷합니다 a -> b -> a 유형 서명:a가 유형을 매개변수화하고 있지만 반환 값으로 동일한 a를 사용해야 합니다.

좋습니다. 하지만 혼란스럽습니다.첫 번째 "a"는 "첫 번째 필드가 a의 인스턴스입니다"를 의미하는 것 같습니다.

아뇨, 그렇죠 ~ 아니다 진실.이는 데이터 유형이 일부 유형 a에 대해 매개변수화된다는 것을 의미합니다.

이는 또한 "첫 번째 필드가 a에 대해 전달한 값과 동일한 값을 가짐"을 의미합니다.즉, AND 값 유형을 지정합니다.

아니요, 그것도 사실이 아닙니다.

다음은 이전에 본 적이 있거나 본 적이 없는 구문의 유익한 예입니다.

foo :: Num a => a -> a

이것은 숫자를 받아 거기에 어떤 작업을 수행하고 다른 숫자를 제공하는 함수에 대한 상당히 표준적인 서명입니다.그러나 내가 실제로 Haskell 언어에서 "숫자"를 의미하는 것은 "Num" 클래스를 구현하는 임의의 유형 "a"입니다.

따라서 이것은 영어로 구문 분석됩니다.

a가 Num 유형 클래스를 구현하는 유형을 나타내면 이 메소드의 시그니처는 a 유형의 매개변수 하나이고 유형 a의 반환 값입니다.

데이터에서도 비슷한 일이 발생합니다.

Cons 명세에 있는 List 인스턴스도 혼란스럽다는 생각이 들었습니다.구문 분석할 때 정말 조심하세요.Cons는 기본적으로 Haskell이 데이터를 래핑할 패턴인 생성자를 지정하는 반면, (List a)는 생성자처럼 보이지만 실제로는 Int 또는 Double과 같은 단순한 유형입니다.a는 어떤 의미에서든 값이 아닌 유형입니다.

편집하다: 가장 최근 편집에 대한 응답입니다.

해부가 먼저 필요하다고 생각합니다.그러면 귀하의 질문을 하나씩 처리해 드리겠습니다.

Haskell 데이터 생성자는 생성자 서명을 정의하고 다른 스캐폴딩을 만들 필요가 없기 때문에 약간 이상합니다.Haskell의 데이터 유형에는 멤버 변수라는 개념이 없습니다.(메모:이러한 사고방식에 더 적합한 대체 구문이 있지만 지금은 이를 무시하겠습니다.

또 다른 점은 Haskell 코드가 조밀하다는 것입니다.그 유형 서명은 다음과 같습니다.따라서 동일한 기호가 다른 상황에서 재사용되는 것을 볼 수 있습니다.유형 추론도 여기서 큰 역할을 합니다.

이제 귀하의 유형으로 돌아가십시오.

data List a = Cons a (List a)
              | Nil

나는 이것을 여러 조각으로 나눕니다.

data 목록

이는 유형의 이름과 나중에 갖게 될 매개변수화된 유형을 정의합니다.이 표시는 다른 유형 서명에서만 볼 수 있습니다.

단점 a (List a) |

이는 데이터 생성자의 이름입니다. 이것은 유형이 아닙니다..그러나 이에 대한 패턴 일치는 가능합니다.

foo :: List a -> Bool
foo Nil = True

List a가 서명의 유형이고 Nil이 데이터 생성자이자 패턴 일치에 대한 "사물"이라는 점에 주목하세요.

Cons a (목록 a)

이것이 생성자에 삽입되는 값의 유형입니다.Cons에는 두 개의 항목이 있습니다. 하나는 a 유형이고 다른 하나는 List a 유형입니다.

그래서 내 질문은 첫 번째 필드의 "a"가 "이 필드의 유형은 'a'이고 이 필드의 값은 'a'"를 의미하는 반면 두 번째 필드의 "a"는 "유형"만 의미하는 이유입니다. 이 필드의 목록은 '목록'입니다."?

단순한:우리가 유형을 지정한다고 생각하지 마세요.Haskell이 그것에서 유형을 추론하고 있다고 생각하십시오.따라서 우리의 의도와 목적을 위해 거기에 0을 붙이고 두 번째 섹션에는 Nil을 붙이기만 하면 됩니다.그런 다음 Haskell은 우리 코드를 보고 다음과 같이 생각합니다.

  • 흠 Cons 0 Nil이 어떤 종류인지 궁금하네요
  • 음, Cons는 List a의 데이터 생성자입니다.List a의 유형이 무엇인지 궁금합니다.
  • a는 첫 번째 매개변수에 사용되므로 첫 번째 매개변수는 Int이므로(또 다른 단순화;0은 실제로 Num으로 유형 분류되는 이상한 것입니다. 즉, a는 Num이라는 뜻입니다.
  • 음, 이는 실제로 Nil의 유형이 List Int라는 것을 의미합니다. 실제로 그렇게 말할 수 있는 내용은 없지만

(실제로 구현된 방식은 아닙니다.Haskell은 유형을 추론하는 동안 이상한 일을 많이 할 수 있는데, 이것이 부분적으로 오류 메시지가 형편없는 이유입니다.)

다른 팁

일반적으로 비유는 모든 면에서 부족하지만 C#을 알고 있으므로 이것이 도움이 될 것이라고 생각했습니다.

이것이 제가 설명하는 방법입니다. List a C#으로 정의하면 일부 문제가 해결될 수도 있습니다(또는 더 혼란스러울 수도 있음).

class List<A>
{
}

class Nil<A> : List<A>
{
    public Nil() {}
}

class Cons<A> : List<A>
{
    public A Head;
    public List<A> Tail;

    public Cons(A head, List<A> tail)
    {
        this.Head = head;
        this.Tail = tail;
    }
}

보시다시피;

  • 그만큼 List 유형에는 단일 유형 매개변수가 있습니다(<A>),
  • 그만큼 Nil 생성자에는 매개변수가 없습니다.
  • 그리고 Cons 생성자에는 두 개의 매개변수, 즉 값이 있습니다. head 유형의 A 그리고 값 tail 유형의 List<A>.

이제 하스켈에서는 Nil 그리고 Cons 단지 생성자일 뿐입니다. List a 데이터 유형은 C#에서는 그 자체로도 유형이므로 비유가 실패합니다.

하지만 이것이 여러분에게 차이점이 무엇인지 직관적으로 알려줄 수 있기를 바랍니다. A대표합니다.

(그리고 이 끔찍한 비교가 어떻게 Haskell의 데이터 유형을 정의하지 못하는지에 대해 의견을 남겨주세요.)

Cons a (List a)

Cons의 첫 번째 필드는 " 유형의 값입니다.a".두 번째는 " 유형의 값입니다.List a", 즉.현재 목록의 매개변수와 동일한 유형으로 매개변수화된 목록입니다.

5는 틀렸습니다. 두 가지를 모두 대체하려면 다음과 같이 6을 사용하겠습니다.

Cons{1} a{2}(List a){3}는 두 개의 값이 필요한 List a 유형(데이터 List a 부분)의 값에 대한 Cons({1} 앞 부분)라는 생성자입니다.a 유형({1}과 {2} 사이의 부분) 중 하나와 List a 유형({2}와 {3} 사이의 부분) 중 하나입니다.

명백한 혼란의 원인을 해결하려면 다음을 수행하십시오.Haskell에서는 명시적인 유형 매개변수를 제공할 필요가 거의 없습니다. 유형 추론은 값에서 유형을 추론합니다.따라서 어떤 의미에서는 함수나 생성자에 값을 전달할 때 유형, 즉 유형도 지정합니다.전달된 값의 유형입니다.

예, 데이터 구문은 이름과 유형을 말장난하고 실제로는 결과를 내지 못하기 때문에 약간 혼란스럽습니다. 구문론적 그들 사이의 구별.특히 다음과 같은 생성자 정의에서:

Cons a (List a)

첫 번째 단어는 생성자의 이름입니다.다른 모든 단어는 미리 선언된 유형의 이름입니다.그래서 둘 다 a 그리고 List a 이미 범위 내에 있습니다( a 에 의해 범위에 포함되었습니다. a 안에 "data List a"), 이것이 매개변수 유형이라는 뜻입니다.그들의 역할은 다음을 사용하여 동일한 내용을 언급함으로써 더 잘 입증될 수 있습니다. 레코드 구문:

Cons { headL :: a, tailL :: List a }

즉.유형의 값 List Int, 만약에 그것은로 건설되었다 Cons 생성자에는 두 개의 필드가 있습니다.안 Int 그리고 List Int.으로 건축된 경우 Nil, 필드가 없습니다.

"를 입력하면Cons 0 Nil", "를 사용합니다.Cons" 값 생성자를 사용하여 List 인스턴스를 만듭니다.0부터 유형 매개변수가 "라는 것을 학습합니다.Integer".지금까지는 혼란이 없었습니다.

그러나 Cons의 첫 번째 필드 값도 0으로 결정됩니다.그러나 두 번째 필드의 값에 대해서는 아무것도 결정하지 않습니다.두 번째 필드에 "List Integer" 유형이 있는지만 결정합니다.

아니요, 두 번째 필드의 값은 다음과 같습니다. Nil.당신의 정의에 따르면, Nil 유형의 값입니다. List a.그러므로 그렇습니다 Cons 0 Nil.그리고 Cons 1 it 두 번째 필드의 값은 다음과 같습니다. it;즉., Cons 0 Nil.이것이 바로 REPL이 보여주는 내용입니다:Cons 1 (Cons 0 Nil).

귀하의 편집된 질문을 살펴보았습니다.

CONS 값 생성자를 사용하여 인스턴스를 생성 할 때 해당 인스턴스는 여기에 전달 된 값을 배치하는 첫 번째 'A'를 "해석"합니다.

"Cons a (List a)"에서는 "a"와 "List a"가 모두 유형입니다.나는 "가치"가 그것과 어떤 관련이 있는지 이해하지 못합니다.

"cons 0 nil"을 입력하면 "cons"값 생성자를 사용하여 목록 인스턴스를 만듭니다.0부터 유형 매개 변수가 "정수"임을 알게됩니다.지금까지는 혼란이 없었습니다.

그러나 단점의 첫 번째 필드의 값이 0임을 결정합니다.그러나 그것은 두 번째 필드의 가치에 대해 아무것도 결정하지 않습니다 ...두 번째 필드에는 "목록 정수"유형이 있는지 결정합니다.

두 번째 필드의 값은 다음과 같습니다. Nil.

그래서 내 질문은, 첫 번째 필드에서 "a"가 "이 필드의 유형은 'a'이고이 필드의 값은 'a'”이며, 두 번째 필드의"a "는 유형 만 의미합니다. 이 필드의 'A 목록'입니까?

첫 번째 필드의 "a"는 "이 필드의 유형이 'a'입니다"를 의미합니다.두 번째 필드의 "List a"는 "이 필드의 유형이 'List a'입니다"를 의미합니다.위의 "Cons 0 Nil"의 경우 'a'는 "Integer"로 추론됩니다.따라서 "Cons a(List a)"는 "Cons Integer(List Integer)"가 됩니다.0은 Integer 유형의 값입니다.Nil은 "List Integer" 유형의 값입니다.

이 필드의 값은 'a'입니다.

이게 무슨 말인지 이해가 안 돼요.'a'는 유형 변수입니다.그것은 가치관과 무슨 관련이 있습니까?

이 스레드를 계속 시청하고 계시다면 추가 "도움말"을 드리고자 합니다.Haskell에는 일이 어떻게 이루어져야 하는지에 대한 다른 사람들의 생각을 엉망으로 만드는 몇 가지 규칙이 있습니다. Haskell에서는 매개변수화된 유형이 매우 일반적으로 허용되므로 일반적으로 유형 수준 함수로 간주됩니다.마찬가지로, 값 생성자는 "값(또는 그 이상)을 취하고 결과로 값을 생성"하는 것 외에도 패턴 일치를 허용하는 "특수" 함수로 간주됩니다.

Haskell의 또 다른 "재미있는" 특징은 함수에 대한 인수를 명시적으로(또는 암시적으로) 평가하지 않는다는 것입니다. 해당 인수가 괄호 안에 있더라도.조금 다르게 말해 보겠습니다.Haskell 함수는 다른 인수 앞에 있는 괄호 안의 인수를 평가하지 않습니다.인수는 그룹화 목적으로만 괄호 안에 표시되며 "먼저" 평가되도록 하기 위한 것이 아닙니다.Haskell은 다른 작업보다 더 높은 우선 순위로 함수에 인수를 할당("적용")합니다. 자체 인수 중 하나를 암시적으로 함수에 적용하는 것보다 훨씬 더 높습니다.이것이 바로 Cons 생성자는 두 번째 인수 주위에 괄호를 가지고 있습니다. (List a) - 컴파일러에게 다음과 같이 알려줍니다. Cons 두 개의 인수가 있지만 그렇지 않습니다. .괄호는 그룹화에만 사용되며 우선순위에는 적용되지 않습니다!

부수적인 주제로 F#의 형식에 주의하세요.F#은 ML에 뿌리를 두고 있으므로 매개변수화된 유형에는 매개변수가 앞에 있습니다. int list, 아니다 (List Int) 뒤에!Haskell은 다른 방식으로 이를 수행합니다. 왜냐하면 Haskell이 함수를 수행하는 것과 같은 방식이기 때문입니다. 먼저 함수, 다음으로 함수에 대한 인수입니다.이는 일반적인 사용 패턴을 장려하고 Haskell 유형과 값 생성자가 대문자로 표시되는 이유를 설명합니다. 이는 유형/클래스 관련 항목을 다루고 있음을 상기시키기 위한 것입니다.

알았어, 끝났어.이 거대한 Wall O' Text를 귀하의 소유지에 놓을 수 있게 해주셔서 감사합니다...

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top