2012-12-12 11 views
6

Sto scrivendo un record data per eseguire il marshalling di un oggetto JIRAJSON. Il problema è che più oggetti hanno le stesse etichette per le coppie nome/valore. Per esempio:Come far fronte allo spazio dei nomi Haskell?

(tornato da curl e formattato)

{"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 
        } 
      } 
      ] 

quando costruisco le problematiche corrispondenti Haskell data record ottengo:

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 

e 'id' e 'sé' si scontreranno . Mi è stato possibile risolvere questo problema semplicemente cambiando i nomi nei record e risolvendolo con un'istanza FromJSON creata manualmente. Qualsiasi soluzione alternativa sarebbe gradita.

risposta

10

Ho risolto questo problema nei buffer di protocollo inserendo elementi come Issue e Version in file separati nella stessa gerarchia.

Haskell utilizza solo moduli separati per controllare gli spazi dei nomi, quindi questa è la soluzione ortodossa.

Molto molto più elaborato: classi di tipo impiegato per definire disponibile Nome:

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: I commenti che seguono mi ricordano per dare consigli più prolisso. Non usare Ha le stesse soluzioni - quelli che sono andati su quella strada dicono che diventa brutto. Posso garantire il percorso di moduli separati.

PS: Forse puoi usare la libreria lens per i tuoi campi!

+7

Si noti che 'typeclasses stile HasFoobar' sono quasi sempre una pessima idea quando si tratta di scrivere codice pulito, ben strutturato Haskell. Ma quando si cerca di far corrispondere la struttura del codice non Haskell ai fini dell'interoperabilità, se l'altra parte si basa pesantemente su funzioni sovraccaricate e/o gerarchie di sottotipi, potrebbe non esserci un approccio migliore. –

+0

Sto facendo l'upvoting della parte in cui hai raccomandato i file separati. Le classi di tipi costituiscono una soluzione di namespaces scadente in quanto sono molto difficili per gli utenti ragionare sui tipi e falliscono silenziosamente e fanno la cosa sbagliata quando vengono applicate al tipo sbagliato. –

3

Un'altra alternativa che dovrebbe funzionare consiste nell'utilizzare un singolo tipo di dati che contiene i record distinti. Ad esempio, il seguente tipo di dati è in grado di rappresentare il vostro valore senza conflitti di campo:

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

Si noti che quanto sopra funziona solo se tutti 'id' sono dello stesso tipo (' Int') e tutti 'self' sono dello stesso tipo (' String'), ecc. –