writings

Thoughts on functions

published on Monday, June 24, 2024

Lately I’ve been thinking about some of the fundamental programming concepts and have been trying to see if I can discover new things about them, or look at them through a new lens.

The other day, I was thinking about functions - the most important fundamental concept that you’re taught when you take an introductory programming class. Functions (and instance methods) are normally presented as a way to create reusable pieces of logic. This description is accurate, but I feel there are other lenses though which we can view functions and they may be more appropriate in a lot of circumstances. I discuss a few here:

export type DateOfBirth = {
  day: number;
  month: number;
  year: number;
};

export type User = {
 firstName: string | undefined;
 lastName: string | undefined;
 dateOfBirth: DateOfBirth | undefined;
 license: number | undefined;
 planType: 'free' | 'premium' | 'pro';
 numAccounts: number;
};

const createUser = () => {
  const user = {
   firstName: undefined,
   lastName: undefined,
   dateOfBirth: undefined,
   license: undefined,
   planType: 'free',
   numAccounts: 1
  };

  const setName = (firstName, lastName) => {
    user.firstName = firstName;
    user.lastName = lastName;
   }
    /* ... other setters */
  const getName = () => {
    const { firstName, lastName } = user;
    return { firstName, lastName};
   }
    /* ... other getters */
  const getUserDetails = () => {
    return Object.freeze({...user});
  }

  return Object.freeze({
   getName,
   setName,
   getUserDetails
 });
};

const isUserEligibleForLicense = (user: User) => {
    // write code here that checks if the user's age is >= 16
}

const isPremiumUser = (user: User) => {
    // write code here that checks if the user's plantype is premium;
}

const isProUser = (user: User) => {
    // write code here that checks if the user's plantype is 'pro';
}

const isFreeUser = (user: User) => {
    // write code here that checks if the user's plantype is 'free';
}
...

Here, I have written the functions isUserEligibleForLicense, isPremiumUser , isProUser which take in a User object and return various results based on the info present. (We could also make these instance methods of the User class. It doesn’t matter in this discussion)

If you have defined functions as illustrated, you now have more “words” in your vocabulary while programming. They are better abstraction primitives. For example, you can write code that looks like this:

const getUpgradePromoBanner = (user: User) => {

  if (isPremiumUser(user)) {
     return 'PREMIUM_TO_PRO';
  }
  if (isFree(user)) {
    return 'FREE_TO_PREMIUM';
  }
  ...
}

Here, you can use the functions you’ve defined already to make your code read almost like natural language. This is far better than code that looks like this:

const getUpgradePromoBanner = (user: User) => {

  if (user.getplanType() === 'premium') {
     return 'PREMIUM_TO_PRO';
  }
  if (user.getPlanType() === 'free') {
    return 'FREE_TO_PREMIUM';
  }
  ...
}

imo. I prefer the first style as it’s more expressive. You don’t have to dig up the property planType of the user object and do a comparison. The abstraction primitives (functions) you have defined remove that cognitive load from you (and others who read your code). The above point seems trivial, but it results in a dramatic amount of cognitive load reduction in large codebases when things are written in more expressive semantics. Given that code is read much more than it’s written. It’s worth taking the time to define new words and concepts we can use to make our lives simpler.

So there you go. Three ways of looking at a concept that I learned almost 15 years ago.