2016-10-04 80 views
4

假設兩種數據類型:Elm是否允許循環引用?

type alias Player = 
    { name : String 
    , team : Team 
    } 

type alias Team = 
    { name : String 
    , players : List Player 
    } 

這JSON:

{ 
    "players": [ 
    { "id": 100, "name": "Sam Bradford", "teamId": 200 }, 
    { "id": 101, "name": "Kyle Rudolph", "teamId": 200 }, 
    { "id": 102, "name": "Matthew Stafford", "teamId": 201 }, 
    { "id": 103, "name": "Marvin Jones Jr.", "teamId": 201 }, 
    { "id": 104, "name": "Golden Tate", "teamId": 201 }, 
    ], 
    "teams": [ 
    { "id": 200, "name": "Minnesota Vikings" }, 
    { "id": 201, "name": "Detroit Lions" }, 
    ] 
} 

很明顯,這個JSON可以被解碼成非空鏈接的對象,而這可以通過一個JSON解碼器確定它正在解碼數據。有沒有辦法解碼這個JSON並創建鏈接的數據結構?我不確定如何用純粹不可變的數據結構來做到這一點,或者如果可能的話。

回答

5

對Elm here中的遞歸數據類型有很好的解釋。

如果您嘗試編譯您的數據類型,您會收到以下錯誤:

-- ALIAS PROBLEM --------------------------------------------------------------- 

This type alias is part of a mutually recursive set of type aliases. 

4|>type alias Player = 
5|> { name : String 
6|> , team : Team 
7|> } 

The following type aliases are mutually recursive: 

    ┌─────┐ 
    │  V 
    │ Player 
    │  │ 
    │  V 
    │ Team 
    └─────┘ 

You need to convert at least one `type alias` into a `type`. This is a kind of 
subtle distinction, so definitely read up on this before you make a fix: 
<https://github.com/elm-lang/elm-compiler/blob/0.17.0/hints/recursive-alias.md> 

您也可以以另一種方式解決這個問題。我傾向於使用ID參考文獻,例如

type alias ID = Int 

type alias PlayerList = Dict ID PLayer 
type alias TeamList = Dict ID Team 

type alias Player = 
    { name : String 
    , teamID : ID 
    } 

type alias Team = 
    { name : String 
    , players : List ID 
    } 

UPDATE:你還別說在你的問題null引用。在上面的數據類型中,每個玩家必須有一個團隊。如果團隊是一個可選字段,我將定義如下:

type alias Player = 
    { name : String 
    , teamID : Maybe ID 
    } 

對於ListString類型,你並不真正需要的Maybe類型。 [](空列表)和""(空字符串)對於那些空狀態更容易。

PS:另一個假設是,您有特定的需求將團隊成員列表存儲在模型的每個團隊中。因爲嚴格地說你不需要:玩家名單已經有了找出哪些玩家(如果有的話)屬於X隊的所有數據。 請注意,這是很多工作(並且可能導致令人討厭的錯誤)如果你維持雙向參考:如果一名球員切換球隊,你需要更新3條記錄(1名球員+2名球隊記錄)。

+0

順便說一句:處理帶有'Dict'和'ID'的循環引用也是@ChadGilbert在他的回答中提到的。 – wintvelt

4

對於不可變的數據結構,不可能創建完全耦合的值的層次結構,以某種循環方式相互引用。

在它的核心,這個問題可以通過幾個步驟來說明:

  1. 其父場
  2. 創建指向在孩子
  3. 更新孩子父母創建Nothing孩子指向父代

當你擁有不可變的數據結構時,數字3是不可能的,因爲對子代的「修改」意味着你創建了一個新的值,家長仍然會指出那個舊價值。如果您隨後更新了家長,那麼您必須再次更新孩子,無限次。

我會推薦使用玩家和團隊的Dict,這些玩家和團隊都會根據各自的ID進行索引以進行查找。

+0

如果你真的想要一個像你所描述的結構,隊員和隊員可以在淺層次相互引用,那麼可以用一些「Json.andThen」雜技。我很好奇,所以我繼續前進,掀起了一個如何做到這一點的快速和骯髒的例子,[如果你有興趣](https://gist.github.com/freakingawesome/82109ca1b9eba8dbdef1fbd43293699c)。 –