Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to model a subset of a discriminated union in F#?

I want a thing (person, object, whatever) to have abilities (jump, run, etc.). I want some things to only have certain abilities. These are my current types:

type Ability =
  | Jump
  | Stay
  | Run
  | Walk

type Person = {
  abilities : Ability Set // OK, since a person should be able to do all of the above
}

type InanimateObject = {
  abilities : Ability Set // Not OK, it should only be able to "Stay"
}

type ThingWithAbilities =
  | Person of Person
  | InanimateObject of InanimateObject

I want a caller of my API to be able to ask for a ThingWithAbilities with a specific ability. Example: Give me all objects of ThingWithAbilities that has the ability "Jump". How can I model this in a good way? I want to make it impossible to create an InanimateObject with the ability "jump" in the code.

like image 643
L42 Avatar asked Sep 24 '19 09:09

L42


2 Answers

If you want to do this in a type-safe way, then you'll need to define different types for different sets of abilities:

type InanimateAbility =
  | Stay

type AnimateAbility = 
  | Jump
  | Run
  | Walk

type Ability = 
  | Inanimate of InanimateAbility
  | Animate of AnimateAbility

type Person = {
  abilities : Ability Set
}

type InanimateObject = {
  abilities : InanimateAbility Set
}

Here, InanimateAbility is a type for abilities that only inanimate objects have and AnimateAbility is a type for abilities that are unique to animate objects. Ability combines both of these and represents any kind of ability. Person can then have a set of Abilitiy values, but you can restrict abilities of InanimateObject to a set of InanimateAbility values.

This works fine as long as you do not have too many combinations - if you had four different kinds of objects with various subsets of abilities, then it might get messy. In that case, you would probably just use one type with a runtime check to make sure that you only assign allowed abilities to each object.

like image 136
Tomas Petricek Avatar answered Sep 28 '22 00:09

Tomas Petricek


you could model them seperately and then define your Person/Object types with reference to the atomic abilities

type Jump = Jump
type Stay = Stay
type Run = Run
type Walk = Walk

type Person = {
  abilities : Choice<Jump,Stay,Run,Walk> Set
}

type InanimateObject  = {
  abilities : Stay Set // actually haveing this as a set is a bit odd...but whatever
}

type ThingWithAbilities =
    | Person of Person
    | InanimateObject of InanimateObject
like image 42
MrD at KookerellaLtd Avatar answered Sep 27 '22 22:09

MrD at KookerellaLtd