4 ms·
I am building a tabletop game trainer for Star Wars FFG as the RPG Sessions app is incomplete and trying to do all of the things manually or remember every rule
by Multicomp 5y ago
I am building a tabletop game trainer for Star Wars FFG as the RPG Sessions app is incomplete and trying to do all of the things manually or remember every rule + every modifier to the rule + what all of my NPCs stats are is impossible, and thus when one looks things up, it slows down gameplay a lot, especially during battles. Since the whole point of the system is to make battles feel fast and cinematic, and we want to strictly adhere to the rules as written so we are not constantly re-adjudicating (read: bickering over) house rules, we need something more.
Enter F#. I've been working on (yet not releasing a usable version yet bc I'm bad) this program entirely in F#, using Giraffe as the web layer, following the general architecture defined in Domain Modelling Made Functional, and using Dapper to write to a Sqlite database.
F# is amazing. I'm able to fully understand the codebase and follow exactly what happens from HTML Form request, through input validation, through the business logic, down to the database and back to the domain, then rendering out to a JS-free webpage via Giraffe.
It gets tedious doing the marshalling/unmarshalling from HTML form to input DTO to domain type to output DTO to database records to input DTO etc. and I need to learn both how to create DSLs (so users can type in rules from the book and the system will extract the keywords and compose them into the closest equivalent set of conditions and effects the system supports) and lock down my bounded contexts so I'm developing them in order of least to most entity dependencies rather than in order of where I feel I need them most, but I can't see a way where I would be able to build this in plain old C# without all of my above challenges + lots of boilerplate code to ensure my expected invariants are met and someone (me) hasn't changed an invariant out from under me somewhere else without realizing it.
- 2mol 5y agoYeah, those boundaries between beautiful domain type <-> DTO <-> database is also something my team and I struggle with. In the end we're happy with just writing it out, but there is this nagging feeling that it could be more ergonomic. I'd be interested to hear about other approaches. You could in theory just dump (automatically generated) JSON to the database and evolve your types with tagged versions, but then you have to write some boilerplatey converters between TypeV1, TypeV2, TypeV3, ... I guess that's the price for staying away from full-blown ORMs? It reminds me a bit of how in Elm you just suck it up and write the JSON en-/decoders by hand. Annoying at first, but then you get super obvious and maintainable code out of it, which is quite a joy.
- de_keyboard 5y ago> It reminds me a bit of how in Elm you just suck it up and write the JSON en-/decoders by hand. Annoying at first, but then you get super obvious and maintainable code out of it, which is quite a joy. The F# take on Elm coders (Thoth.Json) can use reflection to generate them automatically. This is really useful at the prototyping stage.
- 2mol 5y agoYup, I love Thoth. At work we just use System.Text.Json + FSharp.SystemTextJson though, that also generates (de)serializers, and it has a bunch of options to fine-tune naming conventions, sum type encoding, etc. Super useful and easy to hammer out the correct type for very big and nested JSONs.
- iudqnolq 5y agoI kind of like the Elixir Ecto approach where your "DTO"s are mostly plain old data with a single hidden attribute identifying the database table and some other db metadata. They're not live/active, you have to manually pass them to functions to update, but you avoid having two nearly identical types.