Haskell의`head`가 빈 목록에서 충돌하는 이유는 무엇입니까 (또는 왜 빈 목록을 반환하지 * 않습니까 *)? (언어 철학)
다른 잠재적 기여자에 대한 참고 사항 : 주저하지 말고 추상 또는 수학적 표기법을 사용하여 요점을 만드십시오. 답이 불분명하다고 생각되면 해명을 부탁 드리지만 그렇지 않으면 편하게 자신을 표현해주십시오.
확실하게하려면 내가 하지 "안전한"을 찾고 head, 나의 선택을 head특히 매우 의미. 질문의 핵심은 컨텍스트를 제공하는 역할을하는 head및에 대한 논의를 따릅니다 head'.
나는 몇 달 동안 Haskell을 해킹 해 왔지만 (그것이 나의 주요 언어가 될 때까지), 일부 고급 개념이나 언어 철학의 세부 사항에 대해서는 잘 알지 못합니다. 나는 기꺼이 배우고 싶다). 내 질문은 철학의 하나이기 때문에 기술적 인 질문이 아닙니다.
이 예에서는 head.
당신이 알게 될 것이라고 상상합니다.
Prelude> head []
*** Exception: Prelude.head: empty list
이것은 head :: [a] -> a. 그럴 수 있지. 분명히 (손을 흔들면서) 유형이없는 요소를 반환 할 수 없습니다. 그러나 동시에 정의하는 것은 간단합니다 (사소하지는 않지만).
head' :: [a] -> Maybe a
head' [] = Nothing
head' (x:xs) = Just x
특정 진술의 주석 섹션에서 여기 에 대한 약간의 논의를 보았습니다 . 특히 Alex Stangl은
'모든 것을 "안전"하게 만들지 않고 전제 조건을 위반할 때 예외를 던지는 데는 좋은 이유가 있습니다.'
나는 반드시이 주장에 의문을 제기하지는 않지만 이러한 "좋은 이유"가 무엇인지 궁금합니다.
또한 Paul Johnson은 다음과 같이 말합니다.
예를 들어 "safeHead :: [a]-> Maybe a"를 정의 할 수 있지만 이제는 빈 목록을 처리하거나 발생할 수 없음을 증명하는 대신 "Nothing"을 처리하거나 발생할 수 없음을 증명해야합니다. . '
내가 그 코멘트에서 읽은 어조는 이것이 난이도 / 복잡성 / 무언가의 현저한 증가임을 시사하지만 그가 무엇을 내놓고 있는지 잘 모르겠습니다.
한 Steven Pruzina는 말합니다 (2011 년에는 그 이하도 아닙니다),
"예를 들어 'head'가 크래시 방지 기능이 될 수없는 더 깊은 이유가 있습니다. 다형성이면서 빈 목록을 처리하려면 'head'는 항상 특정 빈 목록에없는 유형의 변수를 반환해야합니다. 하스켈이 할 수 있다면 Delphic ... ".
빈 목록 처리를 허용하면 다형성이 손실됩니까? 그렇다면 어떻게, 왜? 이것을 명백하게 만드는 특별한 경우가 있습니까? 이 섹션은 @Russell O'Connor가 충분히 답변했습니다. 물론 더 많은 생각을 주시면 감사하겠습니다.
명확성과 제안에 따라 이것을 편집하겠습니다. 당신이 제공 할 수있는 모든 생각, 논문 등은 가장 감사 할 것입니다.
빈 목록 처리를 허용하면 다형성이 손실됩니까? 그렇다면 어떻게, 왜? 이것을 명백하게 만드는 특별한 경우가 있습니까?
head상태에 대한 자유 정리
f . head = head . $map f
이 이론을 적용하는 []것을 의미한다
f (head []) = head (map f []) = head []
이 이론은 모든을 위해 보유해야 f하므로 특히 그 동안 누르고 있어야 const True하고 const False. 이것은 의미
True = const True (head []) = head [] = const False (head []) = False
이렇게하면 head적절 다형성이며, head []그리고, 전체 값이었다 True같을 것이다 False.
추신. 목록이 비어 있지 않다는 전제 조건이있는 경우 목록을 사용하는 대신 함수 서명에 비어 있지 않은 목록 유형을 사용하여 적용해야하는 효과에 대한 질문의 배경에 대한 다른 의견이 있습니다.
왜 head :: [a] -> a패턴 매칭 대신 사용 합니까? 그 이유 중 하나는 인수가 비어있을 수 없다는 것을 알고 인수가 비어있는 경우를 처리하는 코드를 작성하고 싶지 않기 때문입니다.
물론 head'형식 [a] -> Maybe a은 표준 라이브러리에서 Data.Maybe.listToMaybe. 당신의 사용을 대체하지만 head과를 listToMaybe, 당신은 사용의 목적을 패배 빈 케이스를 처리하는 코드를 작성해야합니다 head.
나는 사용 head이 좋은 스타일 이라고 말하는 것이 아닙니다 . 예외가 발생할 수 있다는 사실을 숨기고 이러한 의미에서 좋지 않습니다. 그러나 때로는 편리합니다. 요점은에서 head제공 할 수없는 일부 목적 을 제공 한다는 것 listToMaybe입니다.
질문의 마지막 인용문 (다형성에 관한)은 단순히 [a] -> a빈 목록에 값을 반환하는 유형의 함수를 정의하는 것이 불가능하다는 것을 의미 합니다 (Russell O'Connor가 그의 답변 에서 설명했듯이 ).
다음이 유지 될 것으로 예상하는 것은 당연한 일입니다 xs === head xs : tail xs.-목록은 첫 번째 요소와 동일하고 나머지 요소는 다음과 같습니다. 논리적으로 보이죠?
이제 :실제 요소를 무시하고 '법칙'을에 적용 할 때의 결과 (의 적용) 수를 계산해 봅시다. '법칙'을에 적용 할 때 []:와 []동일해야 foo : bar하지만 전자는 0 개의 결과가 있고 후자는 (적어도) 하나가 있습니다. 어 오, 여기에 뭔가가 없습니다!
Haskell의 유형 시스템은 모든 강점 head에 대해 비어 있지 않은 목록 만 호출해야한다는 사실을 표현할 수 없습니다 (그리고 '법칙'은 비어 있지 않은 목록에만 유효 함). 사용 head은 증명의 부담을 프로그래머에게 옮깁니다. 프로그래머는 빈 목록에서 사용되지 않도록해야합니다. 나는 Agda와 같은 의존적으로 입력 된 언어가 여기서 도움이 될 수 있다고 믿습니다.
마지막으로, 약간 더 운영 철학적 인 설명 : 어떻게 head ([] :: [a]) :: a구현 해야 합니까? a허공 에서 유형의 값을 조정하는 것은 불가능하며 (예 : 무인도 유형을 생각해보십시오 data Falsum), (Curry-Howard 동형을 통해) 무엇이든 증명할 수 있습니다.
이에 대해 생각하는 방법에는 여러 가지가 있습니다. 그래서 나는 찬성하고 반대 할 것입니다 head'.
반대 head':
필요가 없습니다 head'. 목록은 구체적인 데이터 유형이므로 head'패턴 일치 로 할 수있는 모든 작업을 수행 할 수 있습니다.
또한, head'당신은 하나의 펑터를 다른 펑터와 교환하고 있습니다. 어느 시점에서 황동 압정으로 내려 가서 기본 목록 요소에 대한 작업을 수행하려고합니다.
방어 head':
그러나 패턴 매칭은 무슨 일이 일어나고 있는지 모호합니다. Haskell에서 우리는 함수를 계산하는 데 관심이 있습니다. 이는 컴포지션과 결합자를 사용하여 포인트없는 스타일로 작성함으로써 더 잘 수행됩니다.
또한 []및 Maybe펑터 에 대해 생각 head'하면 이들 사이를 앞뒤로 이동할 수 있습니다 (특히 with Applicative인스턴스 ).[]pure = replicate
If in your use case an empty list makes no sense at all, you can always opt to use NonEmpty instead, where neHead is safe to use. If you see it from that angle, it's not the head function that is unsafe, it's the whole list data-structure (again, for that use case).
I think this is a matter of simplicity and beauty. Which is, of course, in the eye of the beholder.
If coming from a Lisp background, you may be aware that lists are built of cons cells, each cell having a data element and a pointer to next cell. The empty list is not a list per se, but a special symbol. And Haskell goes with this reasoning.
In my view, it is both cleaner, simpler to reason about, and more traditional, if empty list and list are two different things.
...I may add - if you are worried about head being unsafe - don't use it, use pattern matching instead:
sum [] = 0
sum (x:xs) = x + sum xs
'Program Club' 카테고리의 다른 글
| Amazon EC2 권한 거부 됨 (공개 키) (0) | 2020.10.26 |
|---|---|
| 원시 값에 대한 PHP 유형 힌트? (0) | 2020.10.26 |
| 이전 git 커밋 제거 (0) | 2020.10.26 |
| PowerShell에서 % AppData %의 경로 가져 오기 (0) | 2020.10.26 |
| jQuery 및 HTML을 사용하여 CSV로 내보내기 (0) | 2020.10.26 |