2012-12-12 12 views
6

Piszę rekord data, aby przygotować obiekt JIRAJSON. Problem polega na tym, że wiele obiektów ma takie same etykiety dla par nazwa/wartość. Na przykład:Jak radzić sobie z przestrzenią nazw Haskell?

(wrócił z dyni i sformatowany)

{"expand":"schema,names" 
,"startAt":0 
,"maxResults":2 
,"total":74 
,"issues":[ 
      {"expand":"editmeta,renderedFields,transitions,changelog,operations" 
      ,"id":"183614" 
      ,"self":"https://10.64.16.44/rest/api/latest/issue/183614" 
      ,"key":"BNAP-339" 
      ,"fields":{"versions":[ 
            {"self":"https://10.64.16.44/rest/api/2/version/28240" 
            ,"id":"28240" 
            ,"name":"2012-12-07" 
            ,"archived":false 
            ,"released":false 
            } 
           ] 
        ,"status":{"self":"https://10.64.16.44/rest/api/2/status/1" 
           ,"description":"The issue is open and ready for the assignee to start work on it." 
           ,"iconUrl":"https://10.64.16.44/images/icons/status_open.gif" 
           ,"name":"Open" 
           ,"id":"1" 
           } 
        ,"description":"Do Re Mi Fa" 
        ,"resolution":null 
        } 
      } 
      ] 

kiedy skonstruować problematyczne odpowiadające Haskell data rekordy uzyskać:

data Issue = Issue {expand :: String 
        ,id :: String 
        ,self :: String 
        ,key :: String 
        ,fields :: Fields 
        } deriving Generic 


data Version = Version {self :: String 
         ,id :: String 
         ,name :: String 
         ,archived :: Bool 
         ,released :: Bool 
         } deriving Generic 

i 'id' i 'ja' będzie kolidować . Okazało się, że mogę rozwiązać ten problem, zmieniając tylko nazwy w rekordach i naprawiając je ręcznie utworzoną instancją FromJSON. Wszelkie alternatywne rozwiązania byłyby mile widziane.

Odpowiedz

10

Rozwiązuję to w buforach protokołów, umieszczając takie rzeczy jak Issue i Version w oddzielnych plikach w tej samej hierarchii.

Haskell używa tylko oddzielnych modułów do kontrolowania przestrzeni nazw, więc jest to rozwiązanie ortodoksyjne.

O wiele bardziej wyszukane: zajęcia typu zastosowanie do definiowania dostępną nazwę:

class Has'self a b | a -> bwhere 
    get'self :: a -> b 
    set'self :: b -> a -> b 

instance Has'self Issue String where ... 
instance Has'self Version String where .... 

EDIT: Poniższe komentarze przypominają mi dać bardziej opisowy rady. Nie używaj takich rozwiązań jak ci, którzy poszli tą drogą, mówią, że robi się brzydko. Mogę ręczyć za ścieżkę oddzielnych modułów.

PS: Być może możesz użyć biblioteki lens dla swoich pól!

+7

Zauważ, że 'typeclasses stylu HasFoobar' są prawie zawsze fatalny pomysł, jeśli chodzi o pisanie czyste, dobrze zbudowane kod Haskell. Ale gdy próbuje się dopasować strukturę kodu nie-Haskella do celów międzyoperacyjnych, jeśli druga strona w dużej mierze opiera się na przeciążonych funkcjach i/lub hierarchiach podtypów, może nie być lepszego podejścia. –

+0

Aktualizuję część, w której zalecane są oddzielne pliki. Klasy typów przyczyniają się do złego wykorzystania przestrzeni nazw, ponieważ są bardzo trudne do zrozumienia przez użytkowników w odniesieniu do typów i po cichu ulegają awarii i robią coś niewłaściwego po zastosowaniu do niewłaściwego typu. –

3

Inną alternatywą, która powinna działać, jest użycie pojedynczego typu danych, który zawiera odrębne rekordy. Na przykład, następujący typ danych jest w stanie reprezentować swoją wartość bez konfliktów polu:

import Prelude hiding (id) 

data JIRA = JIRA 
    { expand :: String 
    , startAt :: Int 
    , maxResults :: Int 
    , total :: Int 
    , issues :: [JIRA] 
    } | Issue 
    { expand :: String 
    , id :: Int 
    , self :: String 
    , key :: String 
    , fields :: JIRA 
    } | Field 
    { versions :: [JIRA] 
    , status :: JIRA 
    , description :: String 
    , resolution :: Maybe String 
    } | Version 
    { self :: String 
    , id :: Int 
    , name :: String 
    , archived :: Bool 
    , released :: Bool 
    } | Status 
    { self :: String 
    , description :: String 
    , iconUrl :: String 
    , name :: String 
    , id :: Int 
    } 


yourExample = JIRA 
    { expand = "schema, names" 
    , startAt = 0 
    , maxResults = 2 
    , total = 74 
    , issues = [ Issue 
       { expand = "editmeta, etc..." 
       , id = 12345 
       , self = "https://xyz" 
       , key = "BLAH" 
       , fields = Field 
          { versions = [ Version 
              { self = "https://foobar" 
              , id = 1234 
              , name = "quux" 
              , archived = False 
              , released = False 
              } 
             ] 
          , status = Status 
            { self = "https://etc" 
            , description = "issue" 
            , iconUrl = "https://iconurl" 
            , name = "open" 
            , id = 1 
            } 
          , description = "another description" 
          , resolution = Nothing 
          } 
       } 
       ] 
    } 
+0

Zauważ, że powyższe działa tylko wtedy, gdy wszystkie 'id' są tego samego typu (' Int'), a wszystkie 'self' są tego samego typu (' String') itp. –