템플릿 Haskell의 나쁜 점은 무엇입니까?


252

템플릿 Haskell은 종종 Haskell 커뮤니티에서 불행한 편의로 여겨지는 것 같습니다. 이와 관련하여 내가 관찰 한 것을 정확하게 말하기는 어렵지만 몇 가지 예를 고려하십시오.

사람들이 Template Haskell을 사용하여 깔끔한 작업을 수행하는 다양한 블로그 게시물을 보았습니다. 일반 Haskell에서는 불가능한 더 예쁘고 구문이 뛰어나고 보일러 플레이트가 줄어 들었습니다. 그렇다면 왜 Haskell Template이 이런 식으로 내려다 보입니까? 무엇이 바람직하지 않습니까? 어떤 상황에서 Haskell 템플릿을 피해야합니까? 그 이유는 무엇입니까?


56
나는 투표하기로 동의하지 않는다. Lazy I / O에 대해 무엇이 나쁜지 물었던 것과 같은 정신으로이 질문을하고 있습니다 . 그리고 나는 같은 방식으로 대답을 볼 것으로 기대합니다. 도움이된다면 질문을 다시 할 수 있습니다.
Dan Burton

51
@ErikPhilips 왜이 태그를 자주 사용하는 사람들이 여기에 속하는지 결정하지 못하게 하시겠습니까? Haskell 커뮤니티와의 유일한 상호 작용은 질문을 내리는 것 같습니다.
Gabriel Gonzalez 2016 년

5
@GabrielGonzalez 그것은 현재의 질문에 명백하며 어떤 종류의 질문에 대해서는 FAQ를 따르지 않는다고 대답합니다. 실제로 해결할 문제는 없습니다 . 이 문제는 코드 특정 문제가 해결되지 않았으며 본질적으로 개념적입니다. 그리고 질문에 따라 템플릿 haskell을 피해야 합니다. 스택 오버플로는 권장 사항 엔진이 아닙니다 .
Erik Philips

27
@ErikPhilips 권장 사항 엔진 측면은이 질문과 관련이 없습니다. 다른 도구 간의 결정 (예 : "어떤 언어를 사용해야하는지 알려주세요")을 언급하고 있기 때문입니다. 대조적으로, 나는 단지 Haskell 템플릿에 대한 설명을 요구할 뿐이며, FAQ에 "동기 부여가"다른 사람에게 나에게 설명해 주길 원한다면 "괜찮을 것입니다." 예를 들어, GOTO가 여전히 해로운 것으로 간주됩니까?
Dan Burton

29
재개 투표. 더 높은 수준의 질문이라해서 좋은 질문이 아니라는 의미는 아닙니다.
György Andrasek

답변:


171

템플릿 Haskell을 피하는 한 가지 이유는 전체적으로 형식이 안전하지 않기 때문에 "Haskell의 정신"의 많은 부분에 위배됩니다. 이에 대한 몇 가지 예는 다음과 같습니다.

  • TH 코드 일부가 생성되는 위치를 넘어서 어떤 종류의 Haskell AST를 제어 할 수 없습니다. type의 값을 가질 수 Exp있지만 그것이 a [Char]또는 a (a -> (forall b . b -> c))또는 무엇 을 나타내는 표현식인지는 알 수 없습니다 . 함수가 특정 유형의 표현식 만 생성하거나 함수 선언 만, 또는 데이터-구체 일치 패턴 만 생성 할 수 있다고 표현할 수 있다면 TH가 더 신뢰할 수 있습니다.
  • 컴파일되지 않은 표현식을 생성 할 수 있습니다. foo존재하지 않는 자유 변수 를 참조하는 표현식을 생성 했습니까? 운이 좋으면 실제로 코드 생성기를 사용할 때와 특정 코드의 생성을 트리거하는 환경에서만 볼 수 있습니다. 단위 테스트도 매우 어렵습니다.

TH는 또한 매우 위험합니다.

  • 컴파일 타임에 실행되는 코드는 IO미사일 발사 또는 신용 카드 도용 등 임의의 작업을 수행 할 수 있습니다 . TH 익스플로잇을 찾기 위해 다운로드 한 모든 카발 패키지를 살펴볼 필요는 없습니다.
  • TH는 "모듈 전용"기능 및 정의에 액세스하여 일부 경우 캡슐화를 완전히 차단할 수 있습니다.

TH 함수를 라이브러리 개발자로 사용하기에 덜 재미있게 만드는 몇 가지 문제가 있습니다.

  • TH 코드가 항상 작성 가능한 것은 아닙니다. 누군가 렌즈를위한 발전기를 만든다고하자. 그 발전기는 예를 들어 매개 변수로 렌즈를 생성하는 유형 생성자 목록 코드로 해당 목록을 생성하는 것은 까다 롭지 만 사용자는 작성하기 만하면 generateLenses [''Foo, ''Bar]됩니다.
  • 개발자는 TH 코드를 구성 할 수 있다는 사실 조차 모릅니다 . 당신은 당신이 쓸 수 있다는 것을 알고 forM_ [''Foo, ''Bar] generateLens있습니까? Q모나드 일 뿐이므로 일반적인 모든 기능을 사용할 수 있습니다. 어떤 사람들은 이것을 알지 못하기 때문에 동일한 기능을 가진 본질적으로 동일한 기능의 여러 오버로드 버전을 생성하며 이러한 기능은 특정 팽창 효과를 유발합니다. 또한, 대부분의 사람들 Q은 필요가 없을 때에도 모나드에 발전기를 씁니다 bla :: IO Int; bla = return 3. 함수에 필요한 것보다 더 많은 "환경"을 제공하고 있으며 함수의 클라이언트는 해당 환경을 그 효과로 제공해야합니다.

마지막으로 TH 기능을 최종 사용자로 사용하기에 덜 재미있게 만드는 몇 가지 사항이 있습니다.

  • 불투명. TH 함수가 type Q Dec이면 모듈의 최상위 레벨에서 절대적으로 무엇이든 생성 할 수 있으며 생성 될 항목을 전혀 제어 할 수 없습니다.
  • 모 놀리 식. 개발자가 허용하지 않으면 TH 함수가 생성하는 양을 제어 할 수 없습니다. 데이터베이스 인터페이스 JSON 직렬화 인터페이스 를 생성하는 함수를 찾으면 "아니요, 데이터베이스 인터페이스 만 필요합니다. 감사합니다. 내 JSON 인터페이스를 롤하겠습니다"라고 말할 수 없습니다.
  • 런타임. TH 코드를 실행하는 데 시간이 오래 걸립니다. 이 코드는 파일이 컴파일 될 때마다 새로 해석되며, 종종 실행중인 TH 코드에 필요한 수많은 패키지가로드되어야합니다. 컴파일 시간이 상당히 느려집니다.

4
여기에 template-haskell이 역사적으로 매우 잘 문서화되어 있지 않다는 사실이 추가됩니다. (나는 방금 다시 살펴 보았지만 지금은 조금 더 나아진 것처럼 보입니다.) 또한 템플릿 Haskell을 이해하려면 Haskell 언어 문법을 이해해야합니다. 이 두 가지는 내가 Haskell 초보자 일 때 TH를 이해하려고 의도적으로 귀찮게하지 않는 데 도움이되었습니다.
mightybyte 2016 년

14
템플릿 Haskell을 사용한다는 것은 갑자기 선언 순서가 중요하다는 것을 잊지 마십시오! TH는 Haskell의 매끄러운 광택 (1.4, '98, 2010 또는 심지어 Glasgow)을 고려할 때 원하는만큼 단단히 통합되지 않았습니다.
Thomas M. DuBuisson 2016 년

13
하스켈에 대해 너무 많은 어려움없이 추론 할 수 있습니다.
8

15
그리고 TH에 대한 형식 안전 대안에 대한 Oleg의 약속은 어떻게 되었습니까? 나는 "최종 태그가없고 부분적으로 평가 된"논문과 그의 메모에 더 많은 것을 바탕으로 그의 작업을 언급하고 있습니다 . 그들이 발표했을 때 너무 유망 해 보였고 나는 그것에 대해 다른 말을 듣지 못했습니다.
Gabriel Gonzalez 2016 년


53

이것은 나만의 의견입니다.

  • 사용하기 싫습니다. $(fooBar ''Asdf)그냥 좋아 보이지 않습니다. 피상적이지만 확실히 기여합니다.

  • 쓰기가 더 나쁘다. 인용은 때때로 작동하지만 수동 AST 접목 및 배관 작업을 수행해야하는 경우가 많습니다. API는 당신에 대해 걱정하지만 여전히 파견 할 필요가 없습니다 많은 경우는 항상 거기에, 크고 다루기 힘든, 그리고 당신이 치료를 수행 사례에 대한 여러 유사하지만 동일하지 않은 형태로 존재하는 경향이있다 (데이터 대 newtype은, 기록 스타일 대 일반 생성자 등). 쓰기가 지루하고 반복적이며 기계적이지 않을 정도로 복잡합니다. 개혁 제안 주소이 중 일부 (따옴표가 더 광범위하게 적용하고).

  • 무대 제한은 지옥입니다. 동일한 모듈에 정의 된 함수를 스플 라이스 할 수없는 것은 그 중 작은 부분입니다. 다른 결과는 최상위 스플 라이스가있는 경우 모듈에서 그 뒤에있는 모든 것이 범위를 벗어납니다. 이 속성 (C, C ++)을 가진 다른 언어는 선언을 전달할 수있게 해주지 만 하스켈은 그렇지 않습니다. 스 플라이 싱 된 선언 또는 그 종속 항목과 종속 항목 사이에 순환 참조가 필요한 경우 일반적으로 문제가 있습니다.

  • 잘 훈련되지 않았습니다. 이것이 의미하는 바는 대부분 추상화를 표현할 때 그 추상화 뒤에 어떤 원리 나 개념이 있다는 것입니다. 많은 추상화에서, 그 뒤에있는 원리는 그들의 유형으로 표현 될 수 있습니다. 유형 클래스의 경우, 어떤 인스턴스를 준수하고 클라이언트가 가정 할 수있는 법률을 공식화 할 수 있습니다. GHC의 새로운 제네릭 기능 을 사용 하여 모든 데이터 유형 (범위 내)에 대한 인스턴스 선언 양식을 추상화하면 "합계 유형의 경우 제품 유형의 경우 이와 같이 작동합니다."라고 말합니다. 반면에 Haskell은 매크로 일뿐입니다. 아이디어 수준의 추상화가 아니라 AST 수준의 추상화입니다. 일반 텍스트 수준의 추상화보다 낫지 만 겸손합니다. *

  • GHC에 연결됩니다. 이론적으로 다른 컴파일러가 그것을 구현할 수는 있지만 실제로는 이것이 일어날 것이라고 의심합니다. (이것은 현재 GHC에 의해서만 구현 될 수 있지만 다양한 컴파일러가 확장되어 다른 컴파일러에 의해 채택되고 결국 표준화되는 것을 쉽게 상상할 수 있습니다.)

  • API가 안정적이지 않습니다. 새로운 언어 기능이 GHC에 추가되고이를 지원하기 위해 template-haskell 패키지가 업데이트 될 때 TH 데이터 유형에 대해 이전 버전과 호환되지 않는 변경이 종종 발생합니다. TH 코드가 여러 버전의 GHC와 호환되도록하려면 매우주의해서 사용해야 CPP합니다.

  • 작업에 가장 적합한 도구를 사용하고 충분할 가장 작은 도구를 사용해야한다는 일반적인 원칙이 있으며, 유추하여 템플릿 Haskell은 이와 같습니다 . 템플릿 Haskell이 아닌 방법이 있다면 일반적으로 바람직합니다.

Template Haskell의 장점은 다른 방법으로는 할 수 없었던 일을 할 수 있다는 것입니다. TH가 사용되는 대부분의 경우는 컴파일러 기능으로 직접 구현 된 경우에만 수행 할 수 있습니다. TH는 이러한 작업을 수행 할 수 있고 훨씬 더 가볍고 재사용 가능한 방식으로 잠재적 인 컴파일러 확장을 프로토 타입 할 수 있기 때문에 두 가지 모두를 갖는 것이 매우 유리합니다 (예 : 다양한 렌즈 패키지 참조).

템플릿 Haskell에 대해 부정적인 감정이 있다고 생각하는 이유를 요약하면 다음과 같습니다. 많은 문제를 해결하지만 주어진 문제가 해결되면 해당 문제를 해결하는 데 더 적합하고 더 우아하고 잘 훈련 된 솔루션이 있어야합니다. 상용구를 자동으로 생성하여 문제를 해결하지 않고 상용구를 가질 필요를 제거합니다 .

* 나는 종종 CPP그것이 해결할 수있는 문제에 대해 더 나은 힘 대 무게 비율을 느낍니다 .

편집 23-04-14 : 위의 내용을 자주 얻으려고했지만 최근에 정확히 얻은 것은 추상화와 중복 제거 사이에 중요한 차이점이 있다는 것입니다. 적절한 추상화는 종종 중복 제거를 부작용으로 야기하며, 중복은 종종 부적절한 추상화의 징조적인 징후이지만, 이것이 귀중한 이유는 아닙니다. 올바른 추상화는 코드를 정확하고 이해 가능하며 유지 관리하기 쉽게 만드는 것입니다. 중복 제거는 더 짧아집니다. 일반적으로 매크로와 같은 템플릿 Haskell은 중복 제거 도구입니다.


"CPP는 해결할 수있는 문제에 대해 중량 대비 전력비가 더 좋습니다". 과연. 그리고 C99에서는 원하는 것을 해결할 수 있습니다. 이것을 고려하십시오 : COS , Chaos . 또한 사람들이 왜 AST 생성이 더 낫다고 생각하는지 이해하지 못합니다. 그것은 다른 언어 기능에 단지 더 애매하고 덜 직교이다
브리튼 Kerin

31

dflemstr이 제기하는 몇 가지 요점을 다루고 싶습니다.

나는 당신이 TH를 그렇게 걱정할 수 없다는 사실을 찾지 못했습니다. 왜? 오류가 있더라도 여전히 컴파일 시간이됩니다. 이것이 내 주장을 강화시키는 지 확실하지 않지만 C ++에서 템플릿을 사용할 때 발생하는 오류와 정신적으로 유사합니다. 생성 된 코드의 인쇄본을 얻을 수 있기 때문에 이러한 오류는 C ++의 오류보다 이해하기 쉽다고 생각합니다.

TH 표현 / 준 인용자가 까다로운 모서리가 숨길 수있는 고급 기능을 수행하는 경우 어쩌면 좋지 않은 조언입니까?

- 난 (메타 / 하스켈-SRC-EXTS 사용)이 규칙을 준 quoters 요즘에 일한지와 꽤 휴식 https://github.com/mgsloan/quasi-extras/tree/master/examples . 나는 이것이 일반화 된 목록 이해에서 결합 할 수없는 것과 같은 몇 가지 버그를 소개한다는 것을 알고 있습니다. 그러나 http://hackage.haskell.org/trac/ghc/blog/Template%20Haskell%20Proposal의 일부 아이디어 가 컴파일러에서 끝날 가능성이 높다고 생각합니다 . 그때까지 Haskell을 TH 트리로 파싱하는 라이브러리는 거의 완벽한 근사치입니다.

컴파일 속도 / 종속성과 관련하여 "제로"패키지를 사용하여 생성 된 코드를 인라인 할 수 있습니다. 이것은 주어진 라이브러리의 사용자에게는 적어도 좋지만 라이브러리를 편집하는 경우에는 훨씬 나을 수 없습니다. TH 의존성이 생성 된 바이너리를 팽창시킬 수 있습니까? 컴파일 된 코드에서 참조하지 않는 모든 것을 생략했다고 생각했습니다.

Haskell 모듈의 컴파일 단계의 스테이징 제한 / 분할은 빨라집니다.

RE 불투명도 : 이것은 호출하는 모든 라이브러리 함수에 동일합니다. Data.List.groupBy가 수행 할 작업을 제어 할 수 없습니다. 버전 번호가 호환성에 대해 알려주는 합리적인 "보증인"/ 협약이 있습니다. 때에 따라 다른 변화의 문제입니다.

여기에서 제로 지불을 사용하면 이미 생성 된 파일을 버전 화하고 있으므로 생성 된 코드의 형식이 언제 변경되었는지 항상 알 수 있습니다. 그러나 대량의 생성 된 코드의 경우 diff를 보는 것이 약간 나쁠 수 있으므로 더 나은 개발자 인터페이스가 편리한 곳입니다.

RE 모 놀리 식 : 고유 한 컴파일 타임 코드를 사용하여 TH 표현식의 결과를 확실히 후 처리 할 수 ​​있습니다. 최상위 선언 유형 / 이름을 필터링하는 코드는 그리 많지 않습니다. 대체로이 기능을 수행하는 함수를 작성하는 것을 상상할 수 있습니다. 모노 쿼리 쿼터를 수정 / 제거하는 경우 "QuasiQuoter"에서 패턴 일치를 사용하여 사용 된 변환을 추출하거나 이전의 관점에서 새로운 변환을 수행 할 수 있습니다.


1
불투명도 / 모노 리즘 관련 : 물론 [Dec]원하지 않는 것을 제거하고 제거 할 수 있지만 JSON 인터페이스를 생성 할 때 함수가 외부 정의 파일을 읽는다고 가정 해 봅시다. 사용하지 않기 때문에 Dec생성기가 정의 파일을 찾지 않고 컴파일에 실패합니다. 이러한 이유로, Q새로운 이름 (및 그와 같은 것)을 생성 할 수 있지만 허용 할 수없는 더 제한적인 모나드 버전을 갖는 것이 좋을 것 IO입니다. .
dflemstr 2016 년

1
동의합니다. 비 IO 버전의 Q / 견적이 있어야합니다! 이것은 또한 stackoverflow.com/questions/7107308/…를 해결하는 데 도움이 됩니다. 컴파일 타임 코드가 안전하지 않은 작업을 수행하지 않으면 결과 코드에서 안전 검사기를 실행하고 개인 정보를 참조하지 않는 것 같습니다.
mgsloan 2016 년

1
반론을 작성한 적이 있습니까? 여전히 약속 된 링크를 기다리고 있습니다. 이 답변의 오래된 내용을 찾는 사람들 : stackoverflow.com/revisions/10859441/1 , 삭제 된 내용을 볼 수있는 사람들 : stackoverflow.com/revisions/10913718/6
Dan Burton

1
해커 지 버전보다 최신 버전의 0이 있습니까? 그 중 하나 (및 darcs 저장소)는 2009 년에 마지막으로 업데이트되었습니다. 두 복사본 모두 현재 ghc (7.6)로 빌드되지 않습니다.
aavogt

1
@aavogt tgeeky는 그것을 고치기 위해 노력하고 있었지만, 그가 끝내지 않았다고 생각합니다 : github.com/technogeeky/zeroth 아무도 그것을 위해 / 무언가를 만들지 않으면 결국 그것에 갈 것입니다.
mgsloan

16

이 답변은 illissius가 제기 한 문제에 대한 답변입니다.

  • 사용하기 싫습니다. $ (fooBar ''Asdf)는 멋지게 보이지 않습니다. 피상적이지만 확실히 기여합니다.

나는 동의한다. 나는 Haskell의 친숙한 기호 팔레트를 사용하여 $ ()가 언어의 일부인 것처럼 보이도록 선택되었다고 생각합니다. 그러나, 매크로 스 플라이 싱에 사용되는 심볼에서 정확히 원하지 않는 것입니다. 그들은 너무 많이 섞여 있으며,이 미용 측면은 매우 중요합니다. 나는 시각적으로 뚜렷하기 때문에 스플 라이스의 {{}} 모양을 좋아합니다.

  • 쓰기가 더 나쁘다. 인용은 때때로 작동하지만 수동 AST 접목 및 배관 작업을 수행해야하는 경우가 많습니다. [API] [1]은 크고 다루기 힘들며, 항상 신경 쓰지 않지만 파견해야 할 경우가 많으며, 신경 쓰인 경우는 여러 가지 유사하지만 동일한 형태가 아닌 경향이 있습니다 (데이터 대 newtype, 레코드 스타일 대 일반 생성자 등). 쓰기가 지루하고 반복적이며 기계적이지 않을 정도로 복잡합니다. [개혁 제안] [2]는이 중 일부를 언급한다 (견적을 더 널리 적용 가능하게 함).

그러나 "TH의 새로운 방향"에 나오는 일부 의견에서 알 수 있듯이, 즉시 사용할 수있는 AST 인용의 부족이 중요한 결점이 아닙니다. 이 WIP 패키지에서는 https://github.com/mgsloan/quasi-extras 라이브러리 형식으로 이러한 문제를 해결하려고합니다 . 지금까지 평소보다 몇 곳 더 접합 할 수 있으며 AST에서 패턴 일치를 할 수 있습니다.

  • 무대 제한은 지옥입니다. 동일한 모듈에 정의 된 함수를 스플 라이스 할 수없는 것은 그 중 작은 부분입니다. 다른 결과는 최상위 스플 라이스가있는 경우 모듈에서 그 뒤에있는 모든 것이 범위를 벗어납니다. 이 속성 (C, C ++)을 가진 다른 언어는 선언을 전달할 수있게 해주지 만 하스켈은 그렇지 않습니다. 스 플라이 싱 된 선언 또는 그 종속 항목과 종속 항목 사이에 순환 참조가 필요한 경우 일반적으로 문제가 있습니다.

이전에 순환 TH 정의가 불가능하다는 문제에 부딪 쳤습니다. 상당히 성가신 일입니다. 해결책이 있지만 추악한 것입니다. 순환 종속성과 관련된 사항을 생성 된 모든 선언을 결합하는 TH 식으로 래핑하십시오. 이러한 선언 중 하나는 Haskell 코드를 받아들이는 준 인용 부호 일 수 있습니다.

  • 원칙이 아닙니다. 이것이 의미하는 바는 대부분 추상화를 표현할 때 그 추상화 뒤에 어떤 원리 나 개념이 있다는 것입니다. 많은 추상화에서, 그 뒤에있는 원리는 유형으로 표현 될 수 있습니다. 유형 클래스를 정의 할 때, 어떤 인스턴스가 준수해야하고 클라이언트가 가정 할 수있는 법률을 공식화 할 수 있습니다. GHC의 [새로운 제네릭 기능] [3]을 사용하여 (한도 내에서) 모든 데이터 유형에 대한 인스턴스 선언 형식을 추상화하면 "합계 유형의 경우 제품 유형의 경우 다음과 같이 작동합니다. ". 그러나 템플릿 Haskell은 단순한 매크로입니다. 아이디어 수준의 추상화가 아니라 AST 수준의 추상화입니다. 일반 텍스트 수준의 추상화보다 낫지 만 겸손합니다.

원칙을 세우지 않은 경우에만 원칙이 적용되지 않습니다. 유일한 차이점은 컴파일러가 추상화를위한 메커니즘을 구현하면 추상화가 유출되지 않는다는 확신이 더 커진다는 것입니다. 아마도 언어 디자인을 민주화하는 것은 약간 무섭게 들립니다! TH 라이브러리의 제작자는 문서를 잘 문서화하고 제공하는 도구의 의미와 결과를 명확하게 정의해야합니다. 원칙 TH의 좋은 예는 파생 패키지 : http://hackage.haskell.org/package/derive - 이는 DSL을 사용하도록 유도 / 지정 / 실제 유도 다수의 예.

  • GHC에 연결됩니다. 이론적으로 다른 컴파일러가 그것을 구현할 수는 있지만 실제로는 이것이 일어날 것이라고 의심합니다. (이것은 현재 GHC에 의해서만 구현 될 수 있지만 다양한 컴파일러가 확장되어 다른 컴파일러에 의해 채택되고 결국 표준화되는 것을 쉽게 상상할 수 있습니다.)

TH API는 꽤 크고 복잡합니다. 다시 구현하면 힘들 수 있습니다. 그러나 Haskell AST를 나타내는 문제를 해결하는 방법은 실제로 몇 가지뿐입니다. TH ADT를 복사하고 내부 AST 표현에 변환기를 작성하면 많은 도움이 될 것입니다. 이것은 haskell-src-meta를 만드는 (의의하지 않은) 노력과 동등합니다. TH AST를 예쁘게 인쇄하고 컴파일러의 내부 파서를 사용하여 간단하게 다시 구현할 수도 있습니다.

내가 틀릴 수는 있지만 TH는 구현 관점에서 컴파일러 확장이 복잡하다고 생각하지 않습니다. 이것은 실제로 "단순하게 유지"하는 이점 중 하나이며 기본 계층이 이론적으로 매력적이고 정적으로 검증 가능한 템플릿 시스템이되지는 않습니다.

  • API가 안정적이지 않습니다. 새로운 언어 기능이 GHC에 추가되고이를 지원하기 위해 template-haskell 패키지가 업데이트 될 때 TH 데이터 유형에 대해 이전 버전과 호환되지 않는 변경이 종종 발생합니다. TH 코드가 여러 버전의 GHC와 호환되도록하려면 매우주의해서 사용해야 CPP합니다.

이것은 또한 좋은 지적이지만 다소 극적입니다. 최근 API 추가가 있었지만 전반적으로 중단을 유발하지는 않았습니다. 또한 앞에서 언급 한 우수한 AST 인용을 통해 실제로 사용해야하는 API를 크게 줄일 수 있다고 생각합니다. 구성 / 일치에 고유 한 기능이 필요하지 않고 리터럴로 표현되면 대부분의 API가 사라집니다. 또한 작성한 코드는 Haskell과 유사한 언어의 AST 표현으로 더 쉽게 이식됩니다.


요약하면 TH는 강력하고 반 무시 된 도구라고 생각합니다. 증오가 적을수록 더욱 생생한 도서관 생태계가 생겨 더 많은 언어 기능 프로토 타입을 구현할 수 있습니다. TH는 강력한 도구이므로 거의 모든 작업을 수행 할 수 있습니다. 무정부 상태! 글쎄,이 능력을 통해 대부분의 한계를 극복하고 상당히 체계적인 메타 프로그래밍 방식이 가능한 시스템을 만들 수 있다고 생각합니다. "적절한"구현의 디자인이 점차 명확 해 지므로 "적절한"구현을 시뮬레이션하기 위해 추악한 해킹을 사용할 가치가 있습니다.

개인적으로 이상적인 너바나 버전에서 대부분의 언어는 실제로 컴파일러에서 이러한 다양한 라이브러리로 이동합니다. 기능이 라이브러리로 구현된다는 사실은 충실하게 추상화하는 기능에 큰 영향을 미치지 않습니다.

상용구 코드에 대한 일반적인 Haskell의 답변은 무엇입니까? 추출. 우리가 가장 좋아하는 추상화는 무엇입니까? 함수와 타입 클래스!

타입 클래스를 사용하면 메소드 세트를 정의 할 수 있으며, 그런 다음 해당 클래스에서 일반적인 모든 방식의 함수에 사용할 수 있습니다. 그러나이 외에도 클래스가 상용구를 피하는 유일한 방법은 "기본 정의"를 제공하는 것입니다. 다음은 원칙에 맞지 않는 기능의 예입니다!

  • 최소 바인딩 세트는 선언 가능 / 컴파일러 확인 가능하지 않습니다. 이로 인해 상호 재귀로 인해 우발적 인 정의가 만들어 질 수 있습니다.

  • 이 얻을 것이 매우 편리 전력에도 불구하고, 당신으로 인해 고아 인스턴스에, 슈퍼 클래스의 기본값을 지정할 수 없습니다 http://lukepalmer.wordpress.com/2009/01/25/a-world-without-orphans/ 이들은 우리가 문제를 해결 할 것 우아하게 숫자 계층!

  • 분석법 기본값에 대한 TH와 같은 기능을 수행 한 결과 http://www.haskell.org/haskellwiki/GHC.Generics가되었습니다 . 이것은 멋진 것들이지만, 이러한 제네릭을 사용하는 코드 디버깅 경험은 거의 불가능했습니다. 유도 한 유형의 크기와 AST만큼 복잡한 ADT로 인해 거의 불가능했습니다. https://github.com/mgsloan/th-extra/commit/d7784d95d396eb3abdb409a24360beb03731c88c

    다시 말해, TH가 제공 한 기능을 따르지만, 구성 언어 인 언어의 전체 영역을 유형 시스템 표현으로 끌어 올려야했습니다. 복잡한 문제의 경우 일반적인 문제에서 잘 작동하는 것을 볼 수 있지만 TH 해커보다 훨씬 많은 기호가 생성되는 경향이 있습니다.

    TH는 출력 코드의 값 수준 컴파일 타임 계산을 제공하지만 제네릭은 코드의 패턴 일치 / 재귀 부분을 유형 시스템으로 들어 올리도록합니다. 이것이 유용한 몇 가지 방법으로 사용자를 제한하지만 복잡성이 가치가 있다고 생각하지 않습니다.

TH와 lisp와 같은 메타 프로그래밍을 거부하면 인스턴스 선언과 같이보다 유연하고 매크로 확장이 아닌 메소드 기본값과 같은 것을 선호한다고 생각합니다. 예상치 못한 결과를 초래할 수있는 것들을 피하는 원칙은 현명하지만, 우리는 Haskell의 유능한 유형 시스템이 다른 많은 환경보다 (생성 된 코드를 검사함으로써)보다 안정적인 메타 프로그래밍을 허용한다는 것을 무시해서는 안됩니다.


4
이 답변은 그 자체로 잘 견디지 못합니다. 당신은 내가 당신의 답변을 제대로 읽기 전에 가야 할 또 다른 답변에 대한 많은 언급을하고 있습니다.
벤 밀우드

사실입니다. 나는 그럼에도 불구하고 무엇에 대해 이야기하고 있는지 분명히하려고 노력했다. 아마도 일리 수 이스의 포인트를 인라인하도록 편집 할 것입니다.
mgsloan 2016

나는 아마도 "원칙"이 내가 사용했던 것보다 더 강한 단어라고 말해야 할 것이다. 그 글 머리 기호는 제가 생각하기에 가장 어려웠습니다. 머리에 이런 느낌이 있거나 틀에 박힌 아이디어가 있고 말로 표현하는 데 어려움이 있었기 때문에 "원칙"은 근처 어딘가에있는 단어 중 하나였습니다. "수양"은 아마도 더 가깝습니다. 명확하고 간결한 제형을 갖지 않으면 서 대신 몇 가지 예를 들었습니다. 누군가 내가 아마도 무엇을 의미했는지 더 명확하게 설명 할 수 있다면 감사하겠습니다!
glaebhoerl

(또한 스테이징 제한과 추악한 따옴표를 전환했을 수도 있습니다.)
glaebhoerl

1
도! 지적 해 주셔서 감사합니다! 결정된. 예, 잘 훈련되지 않은 것이 아마도 더 나은 방법 일 것입니다. 여기서의 차이점은 실제로 "기본 제공"과 "잘 이해 된"추상화 메커니즘, "ad-hoc"또는 사용자 정의 추상화 메커니즘 사이에 있다고 생각합니다. 필자가 의미하는 바는 타입 클래스 디스패치 (런타임이 아닌 컴파일 타임에도 불구하고)와 비슷한 것을 구현 한 TH 라이브러리를 정의 할 수 있다고 생각한다.
mgsloan

8

Template Haskell의 실질적인 문제 중 하나는 GHC의 바이트 코드 인터프리터가 사용 가능한 경우에만 작동한다는 것입니다. 이는 모든 아키텍처에서 해당되는 것은 아닙니다. 따라서 프로그램이 Template Haskell을 사용하거나이를 사용하는 라이브러리에 의존하는 경우 ARM, MIPS, S390 또는 PowerPC CPU가있는 머신에서 실행되지 않습니다.

이것은 실제로 관련이 있습니다 : git-annex 는 Haskell로 작성된 도구로, 스토리지를 걱정하는 머신에서 실행되는 것이 합리적입니다. 이러한 머신에는 종종 비 i386-CPU가 있습니다. 개인적으로 NSLU 2 (32MB의 RAM, 266MHz CPU 에서 git-annex를 실행합니다 . Haskell이 이러한 하드웨어에서 제대로 작동한다는 것을 알고 있습니까?) Template Haskell을 사용하는 경우 불가능합니다.

(ARM에서 GHC에 대한 상황은 요즘 많이 향상되고 있으며 7.4.2조차도 효과가 있다고 생각하지만 요점은 여전히 ​​존재합니다).


1
"템플릿 Haskell은 GHC의 내장 바이트 코드 컴파일러와 인터프리터를 사용하여 스플 라이스 표현식을 실행합니다." – haskell.org/ghc/docs/7.6.2/html/users_guide/…
Joachim Breitner

2
아, 아니, TH는 바이트 코드 인터프리터 없이는 작동하지 않지만 ghci와는 다릅니다. ghci가 바이트 코드 인터프리터에 의존한다는 점을 감안할 때 ghci의 가용성과 바이트 코드 인터프리터의 가용성 사이에 완벽한 관계가 있었더라도 놀라지 않을 것입니다. 그러나 ghci의 부족이 아니라 바이트 코드 인터프리터의 부족 구체적으로 특별히.
무 흐무 텐

1
(우연히, ghci는 TH의 대화식 사용에 대한 지원이 상당히 부족합니다. ghci -XTemplateHaskell <<< '$(do Language.Haskell.TH.runIO $ (System.Random.randomIO :: IO Int) >>= print; [| 1 |] )')
muhmuhten

좋아, ghci와 함께 나는 코드가 ghci 바이너리 또는 TH 스플 라이스에서 대화식으로 오는지 여부와 상관없이 GHC가 컴파일 대신 코드를 해석하는 능력을 언급했다.
Joachim Breitner

6

TH는 왜 나쁜가요? 나를 위해, 그것은 다음과 같습니다.

TH를 사용하여 자동 생성하는 반복 코드를 너무 많이 생성해야하는 경우 잘못된 코드입니다!

생각 해봐 Haskell의 반 매력은 높은 수준의 디자인으로 다른 언어로 작성해야하는 쓸모없는 상용구 코드를 피할 수 있다는 것입니다. 컴파일 타임 코드 생성 이 필요한 경우 기본적으로 언어 또는 응용 프로그램 디자인이 실패했다고 말합니다. 그리고 우리 프로그래머들은 실패하는 것을 좋아하지 않습니다.

물론 필요합니다. 그러나 때로는 디자인에 조금 더 영리하여 TH가 필요하지 않을 수도 있습니다.

(또 다른 것은 TH가 상당히 저수준이라는 것입니다. 그랜드 하이 레벨 디자인은 없으며 GHC의 내부 구현 세부 정보가 많이 노출되어 API가 변경되기 쉽습니다 ...)


특히 QuasiQuotes를 고려할 때 언어 나 응용 프로그램이 실패했다고 생각하지 않습니다. 구문에 관해서는 항상 상충 관계가 있습니다. 일부 구문은 특정 도메인을 더 잘 설명하므로 때때로 다른 구문으로 전환하려고합니다. QuasiQuotes를 사용하면 구문을 우아하게 전환 할 수 있습니다. 이것은 매우 강력하며 Yesod 및 기타 앱에서 사용됩니다. HTML처럼 느껴지는 구문을 사용하여 HTML 생성 코드를 작성할 수 있다는 것은 놀라운 기능입니다.
CoolCodeBro

@CoolCodeBro 네, 인용 부호는 꽤 좋으며 TH와 약간 직교한다고 가정합니다. (물론 TH 위에 구현되었습니다.) TH를 사용하여 클래스 인스턴스를 생성하거나 여러 민족의 기능 또는 이와 유사한 기능을 만드는 것에 대해 더 많이 생각하고있었습니다.
MathematicalOrchid
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.