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

주어진 :

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가 기꺼이이 문제를 해결해 줄 것 같습니다. 이것이 좋은 생각입니까?



답변

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

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

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

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

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


답변

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

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

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

  • -XTypeSynonymInstances

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

  • -XFlexibleInstances

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

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


참조 : :


답변

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


답변

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


답변