Question Details

No question body available.

Tags

dictionary f# discriminated-union

Answers (6)

November 29, 2025 Score: 2 Rep: 110,075 Quality: Low Completeness: 50%

Do you need to persist this information is some database (relational, document, or other)? If so you need to consider the properties of all the materials and identify commonalities.

Ie. focus less on the top level types, but on their members.

From your code:

type RawMaterial =
    | Ore    //of Material
        //| IronOre //of Material
        //| Coal    //of Material
        //| CopperOre of Material
        //| AluminumOre of Material
        //| GoldOre of Material
        //| SilverOre of Material
    | Oil    of Material
        | CrudeOil of Material
    | Gas    of Material
        | NaturalGas of Material
    | Timber of Material
        | Logs of Material

These all really have the same attributes? And they are all concrete things, rather than abstractions of similar things?

November 29, 2025 Score: 1 Rep: 17,383 Quality: Medium Completeness: 60%

Welcome to F#!

You can do this with an OO hierarchy just like C#, but that's boring, so here's how to do it with discriminated unions:

type Ore = | IronOre | Coal | CopperOre | GoldOre | SilverOre

type Oil = CrudeOil

type Gas = NaturalGas

type Timber = Logs

type RawMaterial = | RawOre of Ore | RawOil of Oil | RawGas of Gas | RawTimber of Timber

type Rock = | IronImpregnatedRock | CoalImpregnatedRock | CopperImpregnatedRock | GasShale | OilShale | Spoil | Useless | VoidOfAnyRock

type Ingot = | IronIngot | SomeOtherIngot

type Lumber = Planks

type RefinedMaterial = | RefinedIngot of Ingot | RefinedLumber of Lumber

type Material = | Raw of RawMaterial | Refined of RefinedMaterial

Notice how much shorter that is than writing out the entire class hierarchy in C#.

Example list of materials:

let inventory = [ Raw (RawOre IronOre) Raw (RawOil CrudeOil) Raw (RawGas NaturalGas) Refined (RefinedIngot IronIngot) ]
November 30, 2025 Score: 1 Rep: 3,480 Quality: Low Completeness: 30%

I agree with Richard's approach. You can build a type hierarchy using DUs in F# or inheritance in C# to model the diagram, but what's the point? How will this benefit the logic you want to implement?

If you look at the tree of life, that's an example of something you might be tempted to model as an OO hierarchy. Or with a DU in F#. Then you'd have insects with their six legs in one branch.

It depends on the purpose, but the chance of success in modeling it like that is very slim. You'd be hardcoding the structure when it's likely that this structure should be built at runtime, and legs and wings and eyes should be attached dynamically at runtime.

Let's say somebody discovers something new in the tree of life. A new species, or some new fact about an existing species. If an application for maintaining information about the tree of life were to cope with this situation, and it was hardcoded like that, it would be impossible to modify information without releasing a new version of the application.

Ok, reality isn't always that simple, so maybe what you're trying has a purpose. Still, do you need a hardcoded tree structure? Maybe a list based on the diagram would be enough, hard or soft. And again, what's it for within the logic?

November 30, 2025 Score: 1 Rep: 2,117 Quality: Low Completeness: 60%

Thank you all for your responses

@Richard - At the moment no, they only probably need a human readable name for the moment. If I need to add more information later like a sales prices or such I was thinking of creating a generic SalesItem where the specific is composited into that new item. - Or what ever the common practice equivalent for this would be in F#.

@Brian - thank you - I will try and give that approach a go today if I get time. It looks promising, visually. I'll be interested to see how my "operations" functions work with it. It looks close to what I want to achieve in my mind.

@Brent - The plan is to only allow new materials and behaviours for them through new hardcoded types. I understand with F# pattern matching I could probably have a single type with a differentiator say "Ore", "Oil" "Gas" and then have a Name "Iron Ore", "Coal Ore", "Crude Oil", "Natural Gas" but then I would assume I will have some massively long "match" expressions that will have to grow with each new type, anyway? Or am I looking at this wrong.

Sorry for answering to all in a single reply, but normally in Stack Overflow I would comment on each reply, but for some reason SO is not giving me that option today!

November 30, 2025 Score: 1 Rep: 2,117 Quality: Low Completeness: 20%

I have given Brian's approach a go today and it has worked out very well for me. The updated code is pushed to the repository. It still needs a lot of tidying up but the basic type structure is now present, the inventory works and processes can now start to be defined. For example tryMakeSteel steelMill (recipe: Recipe) (materialList: MaterialList): Material option, although I still need to add some test for this function actually does what I want it to.

Thank you to every one who contributed to answering my question.

December 1, 2025 Score: 0 Rep: 3,480 Quality: Low Completeness: 40%

No, of course you are not looking at it wrong. You're figuring it out.

When I started learning F#, I didn't grok it until I stumbled on https://fsharpforfunandprofit.com/ That site has helped a lot of F# developers through the years.

I see a box to the right of your question when I view this on my PC. It is titled "Guidelines for open-ended questions". Then it explains this new feature. This is apparently why it looks so different from the traditional Q&A format in here, and why you can't comment on each reply. I assume this new feature makes it possible to answer questions like this one without the risk of someone flagging it as too broad, or whatever.