2013-08-04 7 views
4

나는 다음과 같은 데이터 유형에 대한 Serialize 인스턴스를 작성해야합니다 : 컴파일러가 해결할 수있는 방법이 없기 때문에,실존 데이터 형식을 직렬화 복원

data AnyNode = forall n . (Typeable n, Serialize n) => AnyNode n 

이 아무 문제 없다 직렬화,하지만 난 직렬화를 구현할 수 없습니다 n이 외부 범위와 분리되어 있으므로 Serialize n의 특정 인스턴스

a related discussion in 2006이 있습니다. 나는 현재 어떤 종류의 해결책이나 해결 방법이 도착했는지 궁금합니다.

답변

9

직렬화 할 때 유형에 태그를 지정하고 역 직렬화 할 때 사전을 사용하여 유형을 태그 해제하십시오. 당신은 단지 이 기능 유형의 우주를 폐쇄 역 직렬화 할 수

serialAnyNode (AnyNode x) = serialize (typeOf n, serialize x) 

deserialAnyNode s = case deserialize s of 
      (typ,bs) -> case typ of 
         "String" -> AnyNode (deserialize bs :: String) 
         "Int" -> AnyNode (deserialize bs :: Int) 
         .... 

참고 : 여기에 등을 확인하는 일부 의사 생략하는 오류입니다. 몇 가지 추가 작업을 통해 튜플, 마비 및 둘 중 하나와 같은 파생 유형을 deserialize 할 수도 있습니다.

그러나 전혀 새로운 유형의 "Gotcha"가 TypeableSerialize으로 파생되었다고 선언하는 경우 확장자가없는 경우 deserialAnyNode 물론 처리 할 수 ​​없습니다.

+0

2006 년 토론에서 그들은 비슷한 제안을했습니다. 앞으로 더 나은 솔루션이 나타날 경우를 대비하여 질문을 공개하겠습니다. –

2

정확히 여기 물어 보는 건 어렵습니다. 확실히 특정 유형 T을 선택하고 ByteString을 비 직렬화하여 AnyNode에 저장할 수 있습니다. 어쨌든 AnyNode의 사용자는 좋지 않습니다. 결국 당신은 여전히 ​​T을 골랐습니다. Typeable 제약 조건이 아니라면 사용자는 형식이 무엇인지 알 수 없기 때문에 (Typeable 제약 조건을 없애기 때문에 더 복잡해집니다). 어쩌면 당신이 원하는 것은 실존 적이 아니라 보편적 인 것일 수 있습니다. (그래서 예를 들어, read이 실패 할 수 없습니다) 그들에게 조금 단순화 - 그들 ReadShow 전화 -

이의 두 클래스로 최대 Serialize 분할 보자.

그래서 우리는

class Show a where show :: a -> String 
class Read a where read :: String -> a 

우리는 Show -able 값에 대한 실존 적 컨테이너를 만들 수 있습니다

data ShowEx where 
    ShowEx :: forall a. Show a => a -> ShowEx 
-- non-GADT: data ShowEx = forall a. Show a => ShowEx a 

하지만 물론 ShowEx

String에 동형, 그래서 전체가 아니다 이 점은 이것을 가리킨다. 그러나 Read에 대한 실존 적 짝수 적은 포인트가되어 있습니다 : - 즉 ∃a. Read a *> a - 내가 당신에게 ReadEx을 줄 때

data ReadEx where 
    ReadEx :: forall a. Read a => a -> ReadEx 
-- non-GADT: data ReadEx = forall a. Read a => ReadEx a 

을 당신이 어떤 타입의 값을 가지고 있음을 의미하며, 당신이 모르는 무엇을 유형은 0이지만 String을 동일한 유형의 다른 값으로 사용할 수 있습니다. 그러나 당신은 그것으로 무엇이든 할 수 없다! reada을 생성하지만 어떤 것이 a인지 모르는 경우에는 좋은 결과를 얻지 못합니다.

Read으로 무엇을 할 수 있겠습니까? 발신자가 선택할 수있는 유형, 즉 범용입니다.

newtype ReadUn where 
    ReadUn :: (forall a. Read a => a) -> ReadUn 
-- non-GADT: newtype ReadUn = ReadUn (forall a. Read a => a) 

같은 뭔가 (- 즉 ∀a. Show a => a -처럼 ReadEx, 당신은 ShowUn을 만들 수 있으며 그냥 쓸모가있을 것입니다.) ShowEx은 본질적으로 show의 인수 것을

주 - 즉 show :: (∃a. Show a *> a) -> String - - 그리고 ReadUn은 기본적으로 read - 즉 read :: String -> (∀a. Read a => a)의 반환 값입니다.

그래서 당신은 무엇을 요구하고 있습니까? 존재 적입니까, 보편적입니까? 당신은 확실히 ∀a. (Show a, Read a) => a 또는 ∃a. (Show a, Read a) *> a과 같은 것을 만들 수 있습니다. 진짜 문제는 한정어이다.

(나는 다른 맥락에서이 중 일부에 대해 이야기 어디서 얼마 전에 a question를 물었다.) 당신은 (실제 유형에 전달할 수 있도록 직렬화 기능의 중앙 집중식 "레지스트리"어떤 종류의가 필요

+0

흠, @ sclv의 답변을 보면, 나는 단지 질문을 오해하고 실제로 유용한 것을 말하지 않고있을 가능성이 있습니다. 하지만 어쨌든 내 대답을 떠날거야. – shachaf

6

Typeable 정보에서 추출). 역 직렬화하고자하는 모든 유형이 동일한 모듈에 있다면 이것은 설정하기가 매우 쉽습니다. 모듈이 여러 개있는 경우 매핑이있는 하나의 모듈이 있어야합니다.

형식 컬렉션이 동적인데 컴파일 타임에 쉽게 사용할 수없는 경우 동적 연결을 사용하여 디시리얼라이저에 액세스 할 수 있습니다. 비 직렬화하려는 각 유형에 대해 Typeable 정보에서 파생 된 이름으로 C 호출 가능 함수를 내 보냅니다 (TH를 사용하여 생성 할 수 있음). 그런 다음 런타임에 형식을 deserialize하려면 동일한 이름을 생성하고 동적 링커를 사용하여 함수의 주소를 얻은 다음 FFI 래퍼를 사용하여 Haskell 호출 가능 함수를 가져옵니다. 이것은 다소 복잡한 과정이지만 라이브러리에서 정리할 수 있습니다. 아뇨, 미안 해요. 그런 도서관이 없어요.

+0

그래, 상당히 복잡하게 들린다. 현재 문제의 관점에서 동적 링크에서 읽을 수있는 부분에 대한 제안 사항은 무엇입니까? –

관련 문제