2012-03-14 2 views
4

이 코드를 int로. 나는 전환이 필요하다는 것을 알고있다.변환 형 가족 인스턴스는

class IdToInt a where 
    idToInt :: Id a -> Int 

instance IdToInt Box where 
    idToInt s = s 

을 그리고 실제로 컴파일 :

나는 어쩌면 클래스가 작동합니다 만들 생각했다. 내가 시도 할 때이 기능을 사용하려면

testFunc :: Id a -> Int 
testFunc x = idToInt x 

내가 오류 :

그래서
src/Snowfall/Spatial.hs:29:22: 
Couldn't match type `Id a0' with `Id a' 
NB: `Id' is a type function, and may not be injective 
In the first argument of `idToInt', namely `x' 
In the expression: idToInt x 
In an equation for `testFunc': testFunc x = idToInt x 

, 어떻게 int를 얻을 수있는 유형의 가족 ID에 대한 변환을 만들 수 있습니까? ehird로 답변에 따라

, 나는 다음과 같은 시도했지만 중 하나가 작동하지 않습니다 : 당신은 할 수 없습니다

src/Snowfall/Spatial.hs:45:22: 
Could not deduce (Id a0 ~ Id a) 
from the context (IdStuff a) 
    bound by the type signature for 
      testFunc :: IdStuff a => Id a -> Int 
    at src/Snowfall/Spatial.hs:45:1-22 
NB: `Id' is a type function, and may not be injective 
In the first argument of `idToInt', namely `x' 
In the expression: idToInt x 
In an equation for `testFunc': testFunc x = idToInt x 

답변

2

다른 사람들도 지적했듯이 컴파일러는 어떤 a을 사용할지를 결정할 수 없다는 문제점이 있습니다. 데이터 제품군은 하나의 솔루션이지만 작업하기가 더 쉬운 대안은 유형 감시를 사용하는 것입니다.어떤 Id을 부여하기위한

class IdToInt a where 
    idToInt :: a -> Id a -> Int 

instance IdToInt Box where 
    idToInt _ s = s 

-- if you use this a lot, it's sometimes useful to create type witnesses to use 
box = undefined :: Box 

-- you can use it like 
idToInt box someId 

-- or 
idToInt someBox (getId someBox) 

당신이 대답해야 할 질문은 당신의 클래스를 변경

, 그것은으로 표시되어야 한 종류 a이있다? 즉, aId a 사이에 일대일 대응이 있습니까? 그렇다면 데이터 제품군이 올바른 접근 방식입니다. 그렇지 않은 경우 증인을 선호 할 수 있습니다.

3

:

class IdStuff a where 
    type Id a :: * 
    idToInt :: Id a -> Int 

instance IdStuff Box where 
    type Id Box = Int 
    idToInt s = s 

testFunc :: (IdStuff a) => Id a -> Int 
testFunc x = idToInt x 

그것은 오류가 있습니다. testFunc :: (IdToInt a) => Id a -> Int이 필요합니다. 유형 가족이 열려 있기 때문에 누구나 언제든지

type instance Id Blah =() 

을 선언 할 수 있으며 변환 기능을 제공 할 수 없습니다. 가장 좋은 방법은 클래스에 가족을 배치하는 것입니다.

class HasId a where 
    type Id a 
    idToInt :: Id a -> Int 

instance IdToInt Box where 
    type Id Box = Int 
    idToInt s = s 

그래도 컨텍스트가 필요합니다.

+0

감사합니다. 나는 아직도 그것을 얻지 못하고있다. 질문에 답을 기반으로 결과를 게시하여 제대로 형식을 지정했습니다. – mentics

+2

@taotree : 아, 데이터 유형 패밀리가 아닌 유형 동의어 패밀리를 사용하고 있기 때문입니다. 이 특정 문제는 실제로 버그 일지 모르지만 일반적으로 동의어를 입력하면 꽤 쓸모가 없습니다. 두 인스턴스가 같은 연관 타입을 갖는 것은 완벽하게 가능하기 때문에, GHC는 무엇이든 추론하는 것을 거의 포기하고 엉망이됩니다. 데이터 유형 패밀리를 사용하면 모든 것을 해결할 수 있습니다. – ehird

3

IdToInt a => Id a -> Int 유형의 기능을 사용할 수 없습니다. a 유형을 결정할 방법이 없기 때문입니다. 다음 예제에서는이를 보여줍니다.

type family Id a :: * 
type instance Id() = Int 
type instance Id Char = Int 

class IdToInt a where idToInt :: Id a -> Int 

instance IdToInt() where idToInt x = x + 1 
instance IdToInt Char where idToInt x = x - 1 

main = print $ idToInt 1 

Id() = Id Char = Int 때문에, 상기 컨텍스트 idToInt의 종류 및 Id() -> IntId Char -> Int 같다 Int -> Int이다. 오버로드 된 메서드는 유형에 따라 선택된다는 점을 기억하십시오. 두 클래스 인스턴스 모두 Int -> Int 유형의 idToInt 함수를 정의하므로 형식 검사기에서 사용할 함수를 결정할 수 없습니다.

유형 패밀리 대신 데이터 패밀리를 사용하고 newtype 인스턴스를 선언해야합니다. newtype은 인스턴스

data family Id a :: * 
newtype instance Id() = IdUnit Int 
newtype instance Id Char = IdChar Int 

, Id()Id Char 모두의 int,하지만 그들은 서로 다른 유형이있다. Id 형식은 오버로드 된 함수가 사용할 형식 검사기에 알립니다.