주어진 :
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
형식 동의어 (예 : String
for [Char]
) 를 사용할 수 있습니다 .
-XFlexibleInstances
T a b ..
매개 변수가 유형 변수 인 형식의 인스턴스 유형에 대한 제한을 해제합니다 . 이 -XFlexibleInstances
플래그를 사용하면 인스턴스 선언의 헤드가 임의의 중첩 유형을 언급 할 수 있습니다.
이러한 제한을 해제하면 인스턴스 가 겹칠 수 있으며 ,이 시점에서 모호성을 해결하기 위해 추가 언어 확장이 필요할 수 있으므로 GHC가 인스턴스를 선택할 수 있습니다.
참조 : :
- GHC User ‘s Guide 의 인스턴스 헤드에 대한 완화 된 규칙 .
답변
FlexibleInstances는 대부분의 경우 좋은 대답이 아닙니다. 더 나은 대안은 문자열을 newtype으로 래핑하거나 다음과 같은 도우미 클래스를 도입하는 것입니다.
class Element a where
listToFoo :: [a] -> Foo
instance Element Char where
listToFoo = FooString
instance Element a => Fooable [a] where
toFoo = listToFoo
답변
이러한 답변에 추가하여 제한을 해제하는 데 익숙하지 않은 경우 String을 클래스의 인스턴스가 될 수있는 newtype으로 래핑하는 것이 합리적 일 수 있습니다. 트레이드 오프는 코드를 래핑하고 풀어야하는 잠재적 추악함입니다.