• onlinepersona@programming.dev
    link
    fedilink
    English
    arrow-up
    2
    ·
    3 days ago
    data NonEmpty a = a :| [a]
    

    Note that NonEmpty a is really just a tuple of an a and an ordinary, possibly-empty [a]. This conveniently models a non-empty list by storing the first element of the list separately from the list’s tail: even if the [a] component is [], the a component must always be present.

    Wat? How can I “store the first element of the list separated from the lists tail” when the list is empty? Whether a list is empty or not is a runtime possibility, not a compile-time possibility.

    Someone care to explain this part? It does not compute at all for me.

    Anti Commercial-AI license

    • Ephera@lemmy.ml
      link
      fedilink
      English
      arrow-up
      2
      ·
      3 days ago

      During the parsing step, you check that the list has at least one element. If it does not, you report an error to the user and exit. If it does, you take the first element in the least and store it in the left side of your tuple, and then the remaining elements of the input list go into the right side of your tuple.

      So, for example: [1, 2, 3] → (1, [2, 3])
      Or also: [1] → (1, [])
      If the user gives you [], then you cannot represent that with your tuple, you necessarily have to error.

    • Corbin@programming.dev
      link
      fedilink
      English
      arrow-up
      3
      ·
      3 days ago

      A list can store zero or more elements. A NonEmpty can store one or more element. That’s all.

      This overall strategy – representing the top of a list as a dedicated value – shows up elsewhere, notably in Forths, where it is called “top of stack” and often stored in a dedicated CPU register.

    • Kache@lemm.ee
      link
      fedilink
      arrow-up
      2
      ·
      3 days ago

      You cannot, and that’s why that type declaration models a NonEmpty that a type checker can enforce