#Record Custom Types

1 messages · Page 1 of 1 (latest)

stoic bluff
#

Hey folks, how are you? I'm experimenting gleam for the first time in a personal project. I'm trying to build a digital version of a very complex tabletop game. I thought that since it is a game with turns etc, and it has very complex rules that various things happening may trigger some effect, using event sourcing and CQRS would be a good idea, but I'm currently struggling implementing it with generic custom types.

I have a type Command defined as:

pub type Command(command_data) {
  Command(issued_at: Time, issuer: String, data: command_data)
}

And an Event type defined as:

pub type Event(event_payload) {
  Event(id: String, data: event_payload, source: EventSource, created_at: Time)
}

For the command_data type I have the GameCommand type, defined as:

pub type GameCommand {
  CreateGame(players: List(Player), map: Map)
}

So I can generate a structure like:

Command(issuer: "system", issued_at: birl.now(), data: CreateGame(players: [], map: map)) 

I'm trying to use actors from gleam_otp to create a pub-sub system for my event store, so when I try to process a new command:

fn handle_commands(
   command: Command(d),
  events: List(Event(p)),
) -> actor.Next(Command(d), List(Event(p))) {
  case command {
    Command(data: CreateGame(players, map), issuer: issuer, ..) -> GameCreated(players: players, map: map) |> list.wrap() |> list.append() |> actor.continue

But I'm having the following error (attachment).

Must I have to define each possible value of d as alternative types? Shouldn't I be able to just make the type generic?

old dome
#

It is not possible to match on a generic type and get a concrete type in gleam

what you will likely need to do is something like having variants of the GameCommand type wrap whatever you might need, for example:


pub type Command {
  Command(issued_at: Time, issuer: String, data: GameCommand)
}
pub type Event {
  Event(id: String, data: GameCommand, source: EventSource, created_at: Time)
}
pub type GameCommand {
  CreateGame(players: List(Player), map: Map)
  DeleteGame(id: String)
}

fn handle_commands(
   command: Command,
  events: List(Event),
) -> actor.Next(Command, List(Event)) {
  case command {
    Command(data: CreateGame(players, map), issuer: issuer, ..) -> ...
    Command(data: DeleteGame(id), issuer: issuer, ..) -> ...
stoic bluff
#

hmm, I was thinking about having different command types stored in separated modules, like GameCommands, in game module, PlayerCommands in player module etc.. and use Command(t) to group everything. But what you're saying is that I should have only a single type Command that would store every possible variant in the system? @old dome

old dome
#

i don't know enough about what youre building to say for certain but at least if you want to keep the structure the same as you've laid out here then yes you'll have to do that

#

Nothing saying you can’t also have Command be divided into variants for GameCommand and PlayerCommand

#

but that’s up to you

hallow bear
#

Yeah you could have:

type Command {
  PlayerCommand(player.PlayerCommand)
  GameCommand(game.GameCommand)
}

And have each of those have their own logic and data

stoic bluff
#

ah ok

#

duh, I didn't thought about that hahaha thanks!

#

Not sure if this is the correct place (forgive me if not), but since we're talking about this, is there any intent to add polymorphic variants to gleam?

#

for example, the tabletop game that I intend to port to the digital world has expansions (releases of new bits that could change the way how you play the base game), and I could use the structural sharing to "extend" the base game code, add new functionality to it without necessarily have to change it