2016-12-16 7 views
4

더 복잡한 유형을 만들기 위해 내 코드에 newtype 래퍼를 추가로 사용하여 탐색했습니다. 또한 읽기/표시를 사용하여 저렴한 직렬화를 많이 수행합니다. 특히 강력한 형식의 구성 파일의 간단한 형식으로 사용하는 것이 좋습니다. 나는이 오늘로 실행 :데이터 구조를 포함 할 때 유형 클래스 인스턴스가 사용되지 않습니다.

의 예는 다음과 같이 시작하고, 내가 언 래핑 명명 된 필드와 함께 지능을 주위에 포장하는 간단한 newtype은 정의 :

module Main where 

import Debug.Trace (trace) 
import Text.Read (readEither) 


newtype Bar = Bar { unBar :: Int } 
    deriving Show 

사용자 정의 인스턴스에서 다음 중 하나를 읽고 간단한 Int 구문. 여기서 아이디어는 "Bar {unBar = 42}"대신 "42"를 설정 파일에 넣는 것이 좋을 것입니다.

이 인스턴스에는 추적이 있으므로이 인스턴스가 언제 표시되는지 확인할 수 있습니다. 문제를 관찰 할 때 실제로 사용됩니다.

instance Read Bar where 
    readsPrec _ s = [(Bar i, "")] 
     where i = read (trace ("[debug \"" ++ s ++ "\"]") s) 

이제 막대가있는 다른 유형입니다. 이 것은 읽기를 자동으로 유도합니다.

data Foo = Foo { bar :: Bar } 
    deriving (Read, Show) 


main :: IO() 
main = do 

혼자 바 타입을 직렬화 복원하는 것은 잘 작동하고

print $ ((readEither "42") :: Either String Bar) 
    putStrLn "" 

위의 읽기 인스턴스를 사용하지만 줄을 포함하는 어떤 이유로 푸, 대한, 자동 읽기로 유도, 드릴 다운 및 따기되지 않는다 바의 인스턴스!

print $ ((readEither "Foo { bar = 42 }") :: Either String Foo) 
    putStrLn "" 

그래서 확인, 바의 기본 표시 형식에 대해 일치하는 방법의 기본은 바로 읽기 (추적에서 디버그 메시지 중 하나가 표시되지 않는 것을 알)?

print $ ((readEither "Foo { bar = Bar { unBar = 42 } }") :: Either String Foo) 

아니요! 어느 쪽도 일하지 않는다!! 다시 말하지만, 디버그 메시지가 없습니다.

$ stack exec readbug 
    [debug "42"] 
    Right (Bar {unBar = 42}) 

    Left "Prelude.read: no parse" 

    Left "Prelude.read: no parse" 

이 나에게 버그를 보이지만, 내가 잘못하고 있어요 듣고 싶어요 :

다음은 실행 출력입니다.

위의 코드에 대한 완전한 예제가 제공됩니다. 파일 src/Main.lhstest project on darcshub

+3

아주 좋은 질문입니다. 나는 누군가가 당신의 코드를 디버깅하기 시작했다는 것을 얼마나 쉽게 만들어 주 었는지 좋아한다. 바라기를 나의 대답은 당신이 가지고있는 특정한 문제를 확인하는 데 도움이됩니다. 그건 제쳐두고, 나는 디버깅 이상으로'Read'를 사용하는 것을 추천하지 않을 것입니다 - 그리고'read '를 확실히 해두십시오. show = id'. 내 설정을 JSON (그리고'aeson'을 사용하여 encode/decode)에 넣거나 (사용자 정의 파서를 주장한다면)'attoparsec' 나'megaparsec' 같은 것을 사용합니다. 'Read'는 비효율적 인 파서입니다. 왜냐하면 어디서나 되돌아 가려고하기 때문입니다. – Alec

+2

틀렸어. 'Foo' *의 파생 된 인스턴스는 작성한'Bar'에 대한'Read' 인스턴스를 사용합니다! 'Bar'가 나머지 입력을 모두 소비했다는 것을 잘못보고하기 때문에'Foo' 인스턴스가 실패하기 전에'Bar'의 값을 강요하기 전에 (따라서'trace'를 사용하여 썽크를 강요하지 않습니다) 그래서'Foo' 독자는 성공하는데 필요한'}'을 보지 못합니다. –

+0

@Alec configs에 JSON 사용을 고려하지 않았습니다. 타이핑과 계층 적 구조를 유지합니다. 그런 다음 다른 언어/시스템에서 사용할 수있는 구성 파일로 끝납니다. 나는 이것을 약간 새로운 타입으로 탐구 할 것이다. 감사! – dino

답변

4

Read에 있습니다. readsPrecBar 이후에 더 많은 콘텐츠가 표시 될 가능성을 고려해야합니다. Quoting the Prelude :

readsPrec d s 시도 (<parsed value>, <remaining string>)쌍리스트를 돌려, 문자열의 전면에서 값을 구문 분석합니다. 구문 분석에 성공하지 못하면 리턴 된 목록은 비어 있습니다.귀하의 경우에는

, 당신이 원하는 그런 다음

instance Read Bar where 
    readsPrec d s = [ (Bar i, s') | (i, s') <- readsPrec d tracedS ] 
     where tracedS = trace ("[debug \"" ++ s ++ "\"]") s 

, 다음 작품 : 즉

ghci> print $ ((readEither "Foo { bar = 42 }") :: Either String Foo) 
[debug " 42 }"] 
Right (Foo {bar = Bar {unBar = 42}}) 

귀하의 다른 문제 :

그래서 좋아, 방법에 대한 막대의 기본 표시 양식은 기본 읽기 권한과 일치해야합니까?

print $ ((readEither "Foo { bar = Bar { unBar = 42 } }") :: Either String Foo) 

당신의 잘못입니다 : 당신이 read . show이 신원 작동되지 않도록 Bar에 대한 Read 인스턴스를 정의했다. FooRead 일 때 인스턴스를 사용합니다 (Read을 파생 한 경우 Bar이 생성 한 코드를 다시 생성하지 않습니다).

+1

나는 그것이 접하는 것이란 것을 알고 있지만 그것을 제안하는 것을 도울 수는 없다 :'인스턴스 읽기 바 where readsPrec = coerce (readsPrec @Int)' –

+0

@Alec Ah! 필자는'read'와'readsPrec'의 차이점을 알지 못했지만 같은 기능을 사용하는 것이 더 합리적입니다. 고맙습니다. – dino

+0

@DanielWagner 오, 나는 그것의 간소 함을 좋아합니다. 나는 데이터에 대해 몰랐다. 상인. 더 효율적인 지 궁금합니다. 또한'-XTypeApplications'가 필요합니다. 고마워요! – dino