2012-04-08 6 views
3

상당히 다형성 라이브러리를 작성하려고합니다. 나는 말해주는 것보다보기 쉬운 상황에 처했습니다. 그것은 조금 다음과 같습니다유형 패밀리 유형 Hackery

{-# LANGUAGE ScopedTypeVariables #-} 
{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE TypeFamilies #-} 
import Data.Map (Map) 
import qualified Data.Map as Map 

class Format f where type Target f 
class Format f => Formatter x f where 
    target :: forall y. Formatable y => Target f -> x -> y 
class Formatable y where name :: y -> String 

instance Formatable Integer where name = show 
instance Formatable Int where name = show 

split :: forall x f. (Format f, Formatter x f) => x -> f -> String -> [Either String (Target f)] 
split = undefined 

display :: forall x f. (Format f, Formatter x f) => f -> String -> x -> String 
display f str x = let 
    chunks = split x f str 
    built = foldr apply "" chunks 
    apply (Left s) accum = accum ++ s 
    apply (Right t) accum = accum ++ name (target t x) 
    in foldr apply "" chunks 

는 기본적으로, 우리는 Target s의 수를 정의 다형성 Format들,있다. 또한 다른 형식 옵션 (여기서는 간단히 name으로 줄임)에 응답하는 방법을 알고있는 Formattable 개체가 여러 개 있습니다.

Formattables은 다양한 방법으로 구성되며 다양한 대상에 응답 할 수 있습니다. Formatter은 본질적으로 FormatFormattable 사이의 라우터입니다. 특정 형식의 대상이 있으면 적절한 Formattable 개체로 응답합니다.

이것은 모두 매우 추상적입니다.

  • DateFormatYear, MonthDay 같은 목표를 지정 예를 들면 다음과 같습니다이다.
  • MonthType는 "2월"와 같은 이름을 가지고 IntFormattable newtype은입니다
  • 간단한 instance Formattable Int where name = show
  • DateTime(Int, MonthType, Int)에 대한 타입 동의어가 될 수도있다.

는 (물론, 나는 그런 주위에 올바른 값을 배관으로, 여기에 기계를 많이 잘라했지만, 당신은 아이디어를 얻을.)

display 기능은 매우 간단하다. 형식을 지정하는 문자열, 표시 할 객체 및 형식화 문자열을 모두 문자열로 렌더링합니다.

먼저 문자열을 대상과 문자열로 나눕니다. 예를 들어, 날짜 포맷터는 "%Y-%m-%d" 문자열을 [Right Year, Left "-", Right Month, Left "-", Right Day]으로 나눌 수 있습니다. split 함수는이를 수행하며 여기에서 수정되었습니다.

display 함수는 단순히 각 대상의 Formattable을 추적하고 문자열을 누적합니다.

아니면 적어도, 그렇게해야합니다.

Reduced.hs:20:16: 
    Could not deduce (Target f ~ Target f0) 
    from the context (Format f, Formatter x f) 
     bound by the type signature for 
       display :: (Format f, Formatter x f) => f -> String -> x -> String 
     at Reduced.hs:(19,5)-(24,30) 
    NB: `Target' is a type function, and may not be injective 
    Expected type: [Either [Char] (Target f0)] 
     Actual type: [Either String (Target f)] 
    In the return type of a call of `split' 
    In the expression: split x f str 
    In an equation for `chunks': chunks = split x f str 
Failed, modules loaded: none. 

나는 내 인생은 왜 알아낼 수 없기 :

는 그러나 다음과 같은 오류 유형 검사를 실패합니다. 내가 도대체 ​​뭘 잘못하고있는 겁니까?

답변

2

문제는 Target f

target :: (Formatter f x, Formatable y) => Target f -> x -> y 

가 호출되지 않을 수있는 기능 즉, f을 결정하지 않는다는 것입니다. 어떤 유형의 주석을 target에 부여 하든지간에 f이 무엇인지 파악할 수 없으므로 컴파일러는 어떤 Formatter 인스턴스를 사용할지를 결코 파악할 수 없습니다.100 % 확신 할 수는 없지만 아마도 다중 매개 변수 유형 클래스를 사용하지 말고 x 또는 f 중 하나를 다른 함수로 사용할 수 없습니다. 또한 Format 클래스를 완전히 삭제해야합니다. 유형을 사용하려면 클래스가 필요하지 않습니다. 아마도 다음과 같은 것일 수 있습니다 :

class Formatter x where 
    type Format x 
    target :: Formatable y => Format x -> x -> y 
+0

나는 여전히 붙어 있습니다. 형식 패밀리와'Format' 클래스를 완전히 생략해도 (그리고 class :: Ord f => Formatter xf 인 target :: forall y. f -> x -> y'를 사용하면) 다음과 같이 실패합니다 : Reduced.hs : 23 : 40 : 'name'을 사용하여 발생하는 (형식 가능 y0)의 모호한 유형 변수 'y0' 가능한 수정 : 이러한 유형의 변수를 수정하는 유형 서명을 추가하십시오. '(++) ', 즉'name (target tx)'에서 다음 표현식에서 누적 ++ 이름 (대상 tx) 'apply '의 방정식에서 : 누적 값 = accum ++ name (target tx) – So8res

+0

@ So8res 오류는 인라인 읽을 수 없지만 적어도 새로운 오류처럼 보입니다. 아마도 새로운 질문을 던지겠습니까? –

+0

Hrm. 그것은. 내 사과. 나는'Formatable'을위한'Box' 데이터 타입을 생성함으로써 에러를 해결할 수 있습니다. 가장 우아한 솔루션은 아니지만 지금은 효과가 있습니다. – So8res