Bitwise and case expression

Allergies
Allergies in Elm
intToAllergen : Int -> Maybe Allergy
intToAllergen x =
  case x of
    1 ->
        Just Eggs
    2 ->
        Just Peanuts
    4 ->
        Just Shellfish
    8 ->
        Just Strawberries
    16 ->
        Just Tomatoes
    32 ->
        Just Chocolate
    64 ->
        Just Pollen
    128 ->
        Just Cats
    _ ->
        Nothing

toList : Int -> List Allergy
toList score =
  [ 1, 2, 4, 8, 16, 32, 64, 128 ]
  |> List.filterMap ( \allergenInt -> intToAllergen (Bitwise.and score allergenInt))

isAllergicTo : Allergy -> Int -> Bool
isAllergicTo allergy score =
  toList score |> List.member allergy

In this approach

This approach uses case expressions to represent a bit mask for the allergy.

A hard coded list of all the possible allergy values is created. List.filterMap is used to filter and map the list. It converts the allergy values to their matching Allergy or filters them out by returning Nothing.

You can see a variation of a Bitwise and case expression solution on Exercism.

When to use this approach?

This is not as concise as some other solutions, but the code is all very simple and easy to read. It is the fastest solution of the different approaches.

This approach is better at embracing the concept of using bit positions in the allergy score to represent a list of Allergy, using the bit masks.

The bit masks (1, 2, 4 ...) are duplicated once, which most people would say is Ok (people commonly refactor code when it is duplicated 3 times or more).

toList is fast, iterating the allergies just once, or O(n). You can add an inverse of the intToAllergen function and use it in isAllergicTo, which would make the function as fast as it can be (O(1)). However, this results in another duplication of the allergy values (1, 2, 4, ...). Optimising for speed usually means unoptimising something else.

The compiler does not guarantee that the intToAllergen function or the hard coded list of allergy values contains all the possible values.

This approach does not guarantee that all bit masks are valid, although you could use List.range and List.map to improve this.

This approach doesn't guarantee that all bit masks are sequential. This is potentially useful, and means that if there was ever a need for non sequential bit positions, we could support it, but it does also make it easier to make a mistake if the bit positions are required to be sequential.

13th Dec 2024 · Found it useful?