Refactoring types is a subject of interest to me; although your model does not look particularly flattenable.
Flattening
There is a purely mechanical process by which a model can be flattened, described here: Mapping Tagged Union types to other languages?.
Essentially you just expand out the products and sums to collapse a nested type down. If you only have products and sums you can always do this and get down to a model which is just a sum of products. If there are other non-sum-or-products in the model, usually List, Dict, Set, etc. then the math cannot be pulled through these, so they can prevent getting down to a flat model. As your model contains a few Lists, there is a limit to which this process can be applied, and even where it can be applied, does it result in a better model?
At any rate, I worked it through (note: you have 2 Cell definitions, I think I guessed right which is which):
type alias Player =
{ name: String
, grid: Grid
, words: List String }
type alias Grid = List Cell
type Cell
= EmptyCell { coordinatesX: Int, coordinatesY: Int }
| FullCell { coordinatesX: Int, coordinatesY: Int, val : String }
type Model =
{ players: List Player
, dragging: Bool
, dragStartX: Int
, dragStartY: Int
, dragEndX: Int
, dragEndY: Int }
I can’t get it any flatter than that.
And did expanding out Point
improve the model? I expect you are likely to have helper functions that work with Points doing stuff like calculating distance and so on, so I don’t think it was so helpful to do it.
Nested Updates
Having established that there can be limits to flattening, there is a great piece of advice on writing nested updates here: Updating nested records, again
For some record type Foo:
asBarIn : Foo -> Bar -> Foo
asBarIn foo bar =
{ foo | bar = bar }
Note also, it can often be applied to sum types too:
type Foo
= First
| Second { bar : Bar, whatever : Whatever }
asBarIn : Foo -> Bar -> Foo
asBarIn foo bar =
case foo of
First -> First
Second second -> Second { second | bar = bar }