String을 typeclass의 인스턴스로 만들 수없는 이유는 무엇입니까?


85

주어진 :

data Foo =
  FooString String
class Fooable a where --(is this a good way to name this?)
  toFoo :: a -> Foo

다음 String의 인스턴스 를 만들고 싶습니다 Fooable.

instance Fooable String where
  toFoo = FooString

GHC는 다음과 같이 불평합니다.

Illegal instance declaration for `Fooable String'
    (All instance types must be of the form (T t1 ... tn)
     where T is not a synonym.
     Use -XTypeSynonymInstances if you want to disable this.)
In the instance declaration for `Fooable String'

대신 사용하면 [Char]:

instance Fooable [Char] where
  toFoo = FooString

GHC는 다음과 같이 불평합니다.

Illegal instance declaration for `Fooable [Char]'
   (All instance types must be of the form (T a1 ... an)
    where a1 ... an are type *variables*,
    and each type variable appears at most once in the instance head.
    Use -XFlexibleInstances if you want to disable this.)
In the instance declaration for `Fooable [Char]'

질문 :

  • String 및 typeclass의 인스턴스를 만들 수없는 이유는 무엇입니까?
  • 추가 플래그를 추가하면 GHC가 기꺼이이 문제를 해결해 줄 것 같습니다. 이것이 좋은 생각입니까?

6
이것은 내가 찬성하고 좋아하는 것으로 표시하는 종류의 질문입니다. 그렇지 않으면 가까운 장래에 내가 물어볼 것이라는 것을 알고 있기 때문입니다.)
Oscar Mederos

3
추가 플래그와 관련하여 : GHC를 신뢰하고 플래그가 수행하는 작업을 이해하는 한 아마도 좋은 생각 일 것입니다. Yesod 가 떠 오릅니다. Yesod 앱을 작성할 때 항상 OverloadedStrings pragma를 사용하도록 권장하며 QuasiQuotes는 Yesod 라우팅 규칙에 필수입니다. 컴파일 타임에 플래그 대신 {-# LANGUAGE FlexibleInstances #-}.hs 파일의 맨 위에 (또는 다른 pragma)를 넣을 수도 있습니다.
Dan Burton

답변:


65

이것은 String형식 [Char]에 대한 형식 생성자의 응용 프로그램 인에 대한 형식 별칭 일 뿐이 므로 []형식 Char이 될 수 있기 때문입니다 ([] Char). 형식 변수가 아니기 (T a1 .. an)때문에 형식 Char이 아닙니다.

이 제한의 이유는 중복 인스턴스를 방지하기위한 것입니다. 예를 들어,을 가지고 있는데 instance Fooable [Char]나중에 누군가가 와서 instance Fooable [a]. 이제 컴파일러는 사용하려는 것을 파악할 수 없으며 오류가 발생합니다.

를 사용 -XFlexibleInstances하면 기본적으로 그러한 인스턴스를 정의하지 않겠다고 컴파일러에 약속하게됩니다.

수행하려는 작업에 따라 래퍼를 정의하는 것이 더 나을 수 있습니다.

newtype Wrapper = Wrapper String
instance Fooable Wrapper where
    ...

4
내가 정말로 원했던 논쟁을 위해 말하자 instance Fooable [a]. Char 인 toFoo경우 함수가 다르게 작동하도록 하는 방법이 a있습니까?
John F. Miller

7
@John : -XOverlappingInstances이를 허용 하는 확장 기능 이 있으며 가장 구체적인 인스턴스를 선택합니다. 자세한 내용은 GHC 사용자 가이드를 참조하십시오 .
hammar

18

고전적인 Haskell98 유형 클래스의 두 가지 제한 사항에 직면 해 있습니다.

  • 인스턴스에서 유형 동의어를 허용하지 않습니다.
  • 차례로 유형 변수를 포함하지 않는 중첩 유형을 허용하지 않습니다.

이러한 번거로운 제한은 두 가지 언어 확장으로 해제됩니다.

  • -XTypeSynonymInstances

형식 동의어 (예 : Stringfor [Char]) 를 사용할 수 있습니다 .

  • -XFlexibleInstances

T a b ..매개 변수가 유형 변수 인 형식의 인스턴스 유형에 대한 제한을 해제합니다 . 이 -XFlexibleInstances플래그를 사용하면 인스턴스 선언의 헤드가 임의의 중첩 유형을 언급 할 수 있습니다.

이러한 제한을 해제하면 인스턴스겹칠 수 있으며 ,이 시점에서 모호성을 해결하기 위해 추가 언어 확장이 필요할 수 있으므로 GHC가 인스턴스를 선택할 수 있습니다.


참조 : :


4

FlexibleInstances는 대부분의 경우 좋은 대답이 아닙니다. 더 나은 대안은 문자열을 newtype으로 래핑하거나 다음과 같은 도우미 클래스를 도입하는 것입니다.

class Element a where
   listToFoo :: [a] -> Foo

instance Element Char where
   listToFoo = FooString

instance Element a => Fooable [a] where
   toFoo = listToFoo

참조 : http://www.haskell.org/haskellwiki/List_instance


2

이러한 답변에 추가하여 제한을 해제하는 데 익숙하지 않은 경우 String을 클래스의 인스턴스가 될 수있는 newtype으로 래핑하는 것이 합리적 일 수 있습니다. 트레이드 오프는 코드를 래핑하고 풀어야하는 잠재적 추악함입니다.

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