#Understanding and managing type dependencies and preventing circular dependencies

1 messages · Page 1 of 1 (latest)

shut vault
#

Heyho,

i'm currently working on a little game to improve my F# skills. Now I'm at a point where I have to think about dependencies and cycles a lot so I hope you all can give me a pointer in the right direction.

I actually have multiple questions about dependency management.

  1. How do you manage bigger Codebases effectively?

I split my types into different modules, depending on the type. Eg. I made a Item module for the item type(s) and helperfunctions for these types. The same goes for things like the Gameworld and the character. I had multiple occurrences where I had to rearange my dependencies or move funktions and types around to prevent circular dependencies.
The modules with the types are relatively small (20-50 lines) and I thought about merging them into one bigger file with all types for improved overview. As I come from a Java background this didn't really feel right, so here my question: Should I keep related Types together in one file or is it more common to keep them seperate.

  1. How to keep my types without circular dependencies?

Lets start with an example:

type Item = {
    ItemId: string
    Name: string
}
type Inventory = {
    Items: Item[]
}
type Character = {
    CharacterId: string
    Inventory: Inventory
}
type World = {
    WorldId: string
    ItemsOnTheGround: Item[]
}

I now want add actions, that can be performed by the player. Each action can have multiple parameters of my already defined types.

type ActionParameter = Character|Item|World
type Action = {
    Name: string
    Parameter: ActionParameter[]
}

So far so good. My next idea was to add actions in my other types, for example my Item type, so that it's easy to access which actions can be used with an Item:

type Item = {
    ItemId: string
    Name: string
    Actions: Action[]}
#

This, of course is a circular dependency. What are recommended ways to prevent this kind of situation?
My first idea was to make some kind of type ActionId = string on top of everything else and reference the Actions with only the ID. While this is possible, it would require a separate system for getting and storing the action. An other idea was to use object[] as the ActionParameter, but that felt just plain wrong.

weary rose
#

You'll want to group related types together in a single file, and it's not uncommon to just put all your core domain types in a file called "types" as one of the first files in your project. I'd start there and split things out when they start to feel messy.

#

If you have types that directly depend on each other, you can use and when defining them to make them mutually related

#

I'm on my phone so it's hard to type it, but it looks something like:

Type foo = {} 
and bar = {} 
#

That said, recursive types are generally to be avoided due to complexity and they're often (but not always) a code smell to simplify things. In your example I'd probably go the ID-based route, for what that's worth