5

My database consists of a table of items and a table of associated ads.

I would like my elm app to display either an item with its associated ads, or an ad with information on the parent item.

To match those interface needs, I would have liked to write the following two modules in elm, matching what my API already sends :

module Item.Model exposing (..)

import Ad.Model exposing (Ad)

type alias Item =
   { ads : Maybe List Ad
   }

and

module Ad.Model exposing (..)

import Item.Model exposing (Item)

type alias Ad =
   { item : Maybe Item
   }

This definition however results in the following dependencies cycle error :

ERROR in ./elm-admin/Main.elm
Module build failed: Error: Compiler process exited with error Compilation failed
Your dependencies form a cycle:

  ┌─────┐
  │    Item.Model
  │     ↓
  │    Ad.Model
  └─────┘

You may need to move some values to a new module to get rid of the cycle.

    at ChildProcess.<anonymous> (/opt/app/assets/node_modules/node-elm-compiler/index.js:141:27)
    at emitTwo (events.js:106:13)
    at ChildProcess.emit (events.js:191:7)
    at maybeClose (internal/child_process.js:886:16)
    at Socket.<anonymous> (internal/child_process.js:342:11)
    at emitOne (events.js:96:13)
    at Socket.emit (events.js:188:7)
    at Pipe._handle.close [as _onclose] (net.js:497:12)
 @ ./js/admin.js 3:12-44

It looks like defining both type alias in the same module doesn't make the compiler stop, but this is not a satisfying solution to me because I don't want every Model of my app to end up in the same file.

Is there a way solve this without defining the two type aliases Ad and Item in the same module ?

AlexHv
  • 1,694
  • 14
  • 21

1 Answers1

6

I can't think of an immediate solution to the question you pose, but I would not store data that way - I would use pointers instead

module Item.Model exposing (..)

type alias Items = Dict String Item

type alias Item =
   { ads : List String
   }
   -- you don't need a Maybe and a List 
   -- as the absence of data can be conveyed by the empty list

and

module Ad.Model exposing (..)

import Item.Model exposing (Item)

type alias Ads = Dict String Ad 

type alias Ad =
   { itemKey : Maybe String
   }

Then you could something like

model.ads 
    |> L.filterMap (\key -> Dict.get key model.items)
    |> ...

model.itemKey
    |> Maybe.andThen (\itemKey -> Dict.get itemKey model.ads)
    |> ...

Such an approach works, and also offers opportunities for memoisation subsequently

Simon H
  • 20,332
  • 14
  • 71
  • 128
  • Another problem appears further though : `HTTP.request` needs a decoder. I would need this decoder to produce both an `Item` with ads, and a list of `Ads` so that I can update the items and the ads in the model. I could define a new `type alias DecodedItem = { ads : List Ad, item : Item }` and a decoder to obtain it, but I am still struggling to figure out how to define this decoder. – AlexHv Oct 22 '17 at 20:09
  • That should be OK. Put them in a module that imports both models – Simon H Oct 22 '17 at 20:10