Type explosion?

I’m new to typed functional programming, and am asking to check what’s deem “better”

Background:

  • I have several landing pages meant for different users revolving around an appointment bookings table.
    1. customer has a page showing their appointments with a particular user; we know the customer & target user; so we only need to display List { a | timeslot : Time.Posix }
    1. users has a page showing their schedules with customers; we know the target user; we now need to display List { a | timeslot : Time.Posix, customer_name : String }
    1. admin has a page showing appointments with customers of all users; we need to display List { a | timeslot : Time.Posix, customer_name : String, user_name : String }
  • in the backend, the appointments table only has user_id, customer_id; we have to perform joins to pull up the names from respective tables.

So, I’m already dealing with 3 types around just one “booking” table; worrying about when the app grows…

  • I could create a type for each, meaning separate decoders and related functions. I could put some effort to make helper functions work with extensible records so I could have some reuse between the 3 booking variants (e.g. to display a booking date time)
  • I’m also tempted to just use 1 booking super type (always include user_name and customer_name) but now I’m over fetching (network resources, db inner joins). Also, I’ll likely end up passing entire records of extraneous data around functions and making it hard to know which parts of my program actually need extra / lesser stuff

Whenever I have a new need (oh I need customer_email here…) I’m super tempted to simply extend the existing super Booking type, but I know that will just muddle the other areas.

I think I “didn’t have such problems” previously (not typed functional languages) likely because I’ve been “using the same class” but loose/ambiguous about missing attributes in my data (would .customer_name be null?) or wasn’t using any “type” in the first place (a list of key-values)

I’m currently

  • preferring to accept the problem (it exists regardless of whether I want to face it or not)
  • not preferring the “super type” approach
  • accepting I’ll have many types (since it provide a more accurate reflection of the data I’m dealing with at every point) but hoping life doesn’t get worse

Thoughts?

1 Like

So, instead of making 3 views you want to have 1 view with multiple if statements around the authorized user, correct? If that’s the case why not having Maybe for each field?

type alias Appointment =
    { timeslot : Time.Posix
    , customerName : Maybe String
    , userName : Maybe String
    }

It resembles all typeless languages. However, I would avoid using this pattern too much, because that would lead to inconsistent data and worse understanding for a future developer who learns the code and doesn’t know the full backend/frontend context.

Otherwise, I’d be thinking about separating those views or it’s parts at least.

Apart the architecture and types, the timeslots in the example seem to be same thing as a type but they’re not same semantically. We’re talking about the bounded contexts here. The semantic appointment is very different for a user and customer. The user may have some additional data and thus it may be displayed completely different than for an admin or a customer. So maybe you should use a custom type:

type Appointment
    = UserAppointment { timeslot : Time.Posix }
    | CustomerAppointment { timeslot : Time.Posix, userName : String }
    | AdminAppointment { timeslot : Time.Posix, customerName : String, userName : String }

 viewAppointment : Appointment -> Html Msg
 viewAppointment app =
     case app of
         UserAppointment a ->
             -- ...
         CustomerAppointment a ->
             -- ...
         AdminAppointment a ->
             -- ...

It actually gives you a possibility to have timeslots defined in different formats. For example, you could change the type of UserAppointment -> timeslot to a simple String and compiler will notify you about this specific change.

BUT, here’s where you wonder. So many types. Well, you want those types. Those have your back when you try to change anything.

Instead, what would you do in your favourite language?

2 Likes

Sorry I wasn’t clear. That’s not a goal. Since the views are for pretty different purposes, so I’m happy with different views. I was more interested to know if type explosion is deem a good thing (or lesser of the evils) in type functional languages.

Yes exactly. I’m not preferring this approach. Maybe String or even :speak_no_evil: blank strings

Yes, exactly my current sentiment is such also.

On the front end, it will be typeless (e.g. list of key-values) and truth is, we are dealing with different types since the keys are different, except we didn’t have to define them and get to pretend we’re working with “bookings”…

On the backend where I spend most time, I will wield the same type (Booking class) but different fields are actually nil (but unreferenced) in different context.

Pretending we’re dealing with the same type is more convenient at the onset, but gets unmanageable / hard to keep track.

UPDATE: Thanks for the link to BoundedContext :bowing_man: helpful reinforcement of thought

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.