Cross/self reference structure

Hello everyone!

I’m facing an issue with the structure of my data.
I have data that references one another bidirectionally and can’t find a satisfying way to deal with it.

type RequestCollection = RequestCollection Int [Request]
type ScenarioCollection = ScenarioCollection Int [Scenario]

-- a request can be attached to 0 to n scenario
type Request = 
    { id: Int
    ...
    }

-- a scenario is always attached to one request
type Scenario =
    { id : Int
    , requestId : Int
    }

requestColl : RequestCollection = ...
scenarioColl : ScenarioCollection = ...

I can easily deal with requestColl because it has directly access to requests. OTOH I scenarioColl only have a request ids so if I want to deal with a scenario’s request, I first need to first find it in requestColl. That is inconvenient because you then have to deal with Maybe.

Is there a way to deal with that kind of structure or should I am just stuck with dealing with Maybe?

If you use references, you place yourself in the context where the data might no longer be available.

So, either accept this and just use Maybe or copy the data in Scenario.

If you are 100% sure that the retrieval of the request cannot fail you can get rid of the Maybe by using defaults. For example:

type alias RequestCollection = Dict Int Request 

getRequest : RequestCollection -> Int -> Request 
getRequest requestDict  id= 
   Dict.get id requestDict |> Maybe.withDefault defaultRequest

Please note that using patterns like this makes your code a little bit more error prone. Use it with care.

1 Like

Thank you for your answer @pdamoc!

I unfortunately cannot use Maybe.withDefault because the defaultRequest I have is wrapped in a Maybe.
I hoped there was some pattern to deal with this kind of use case but I guess I’m stuck with either copying data or dealing with Maybe :frowning:

defaultRequest is just a dummy request that you manually create. You use it because, in theory, all the calls to retrieve the requests from the requests collection will succeed.

Oh yeah right… that would work indeed!

A bit of offtopic, but instead of holding List Request and List Scenario, the “relational database” model is a pretty good alternative (but yeah, leaves you with a Maybe. @pdamoc’s note about Maybe.withDefault applies here as well.)

Here’s how it looks:

type alias Store =
  { requests : Dict Int Request
  , scenarios : Dict Int Scenario
  }

Or in case you’re getting your all data from API at once:

type alias Store =
  { requests : WebData (Dict Int Request)
  , scenarios : WebData (Dict Int Scenario)
  }

Or in case you’re getting your your data lazily, one by one, from the API:

type alias Store =
  { requests : Dict Int (WebData Request)
  , scenarios : Dict Int (WebData Scenario)
  }

WebData is defined here: https://package.elm-lang.org/packages/krisajenkins/remotedata/latest/
(See also the obligatory blogpost: http://blog.jenkster.com/2016/06/how-elm-slays-a-ui-antipattern.html)

3 Likes

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.