$30 off During Our Annual Pro Sale. View Details »

Imperative vs Declarative: Weathering the storm

Imperative vs Declarative: Weathering the storm

You’ve no doubt heard the terms “imperative” and “declarative” before, and you probably know that developers strongly prefer the latter whenever possible. But just how clear are you on what these really mean, when it comes to writing lines of code in your app? Most of us probably just wing it or feel “I know it when I see it.”

Debates have raged for decades on this topic. In this talk, I’ll drive straight into the eye of the storm and try to sift through what it all means for our apps, specifically for the readability and maintainability of our code bases. We’ll look at how these concepts play out in a variety of webdev technologies, from HTML, to CSS, to JS… and more along the way. I hope you’ve brought your raincoat!

Kyle Simpson
PRO

October 15, 2023
Tweet

More Decks by Kyle Simpson

Other Decks in Technology

Transcript

  1. Imperative vs Declarative

    View Slide

  2. View Slide

  3. Why?

    View Slide

  4. Code is for humans,
    not computers

    View Slide

  5. Code must first be read
    before it can be written

    View Slide

  6. As soon as you write some code,
    you can no longer accurately
    judge its readability…

    View Slide

  7. …unless you wait awhile until
    you’ve forgotten writing it.

    View Slide

  8. View Slide

  9. What’s IMPERATIVE?
    What’s DECLARATIVE?
    No
    Yes

    View Slide

  10. IMPERATIVE: how
    DECLARATIVE: what/why

    View Slide

  11. IMPERATIVE: instructions, flow control
    DECLARATIVE: non-conditional intent

    View Slide

  12. IMPERATIVE: explicit
    DECLARATIVE: implicit

    View Slide

  13. Hiding Implementation Details
    Hiding Implementation Details
    Abstractions?
    Separating Concerns
    Creating Mental Models

    View Slide

  14. IMPERATIVITY - DECLARATIVITY
    relative spectrum

    View Slide

  15. View Slide

  16. let age = 43
    console.log(`I am ${age} years old`)
    #include
    int main() {
    int age = 43;
    printf("I am %d years old\n", age);
    return 0;
    }

    View Slide

  17. .model small
    .stack 100h
    .data
    age db 43 ; Byte-sized variable named "age" initialized with 43
    msg db "I am %d years old", 0 ; Null-terminated string for printf
    .code
    mov ax, @data ; Load the data segment address into AX
    mov ds, ax ; Set DS to the data segment
    mov al, age ; Move the value of "age" into AL register
    mov ah, 0 ; Clear AH to prepare AX for printing
    lea dx, msg ; Load effective address of the message into DX
    mov ah, 09h ; Function 09h - Display string
    int 21h ; Call DOS interrupt
    mov ax, 4C00h ; DOS function to exit the program
    int 21h ; Call DOS interrupt
    end
    DISCLAIMER: CHATGPT

    View Slide

  18. DECLARATIVITY: establishes a
    “grammar” for describing
    intended outcomes

    View Slide

  19. printAmount(x + y)
    const total = x + y
    printAmount(total)

    View Slide

  20. const total = x + y
    const addTwoNums = (x, y) => x + y
    // ..
    const total = addTwoNums(x, y)

    View Slide

  21. DECLARATIVITY: not absolute

    View Slide

  22. Prompt Engineering

    View Slide

  23. Good design of DECLARATIVITY
    resists leaking implementation
    details.

    View Slide

  24. But, good design of DECLARATIVITY
    admits the need for IMPERATIVITY
    to refine outcomes.

    View Slide

  25. DECLARATIVITY
    requires discipline

    View Slide

  26. DECLARATIVITY
    requires balance

    View Slide

  27. View Slide

  28. SELECT
    id,
    (CASE
    WHEN firstName ISNULL THEN 'Customer'
    ELSE firstName
    END) AS 'customerName'
    FROM
    Customers
    WHERE
    enabled = 1

    View Slide







  29. This page works best with the new JScript 3.0 in IE4.

    View Slide

  30. hx-trigger="cancel"
    class="editing"
    hx-get="/contact/${contact.id}"
    >







    class="btn btn-danger"
    hx-get="/contact/${contact.id}"
    >
    Cancel

    class="btn btn-danger"
    hx-put="/contact/${contact.id}"
    hx-include="closest tr"
    >
    Save



    > htmx
    hx-trigger="click, keyup[altKey&&shiftKey&&key=='D'] from:body"
    hx-post="/doit"
    >
    Do It! (alt-shift-D)

    View Slide

  31. View Slide







  32. { customer.name ?? 'Customer' }

    View Slide

  33. FatalErrorBoundary(
    { page: FatalErrorPage },
    RedwoodProvider(
    Routes()
    )
    )
    // vs
    compose(
    partial(FatalErrorBoundary, { page: FatalErrorPage }),
    RedwoodProvider,
    Routes
    )()
    // vs
    Routes() |>
    RedwoodProvider(%) |>
    FatalErrorBoundary({ page: FatalErrorPage }, %)

    View Slide

  34. @media (orientation: portrait) and (prefers-color-scheme: dark) {
    .my-list {
    --base-font-size: min(max(3vw, 1.3rem), 2.2rem);
    font-size: var(--base-font-size);
    }
    }
    @supports (display: grid) {
    section h1 {
    background-color: green;
    color: white;
    }
    }
    input[type=radio]:checked {
    margin-left: 20px;
    }

    View Slide

  35. .bar {
    --value-abs: max(
    -1 * var(--cfg-value),
    var(--cfg-value)
    );
    --value-sign: calc( var(--cfg-value) / max(0.001, var(--value-abs)) );
    --bar-width: max( 1px, var(--bar-scale-factor) * var(--value-abs) );
    --bar-translate-x: min( 0px, var(--value-sign) * var(--bar-width) );
    --bar-left-rounding: calc(
    -1 * min( 0px, var(--value-sign) * var(--bar-rounding) )
    );
    --bar-right-rounding: max(
    0px, var(--value-sign) * var(--bar-rounding)
    );
    /* .. */
    }

    View Slide

  36. x == 3
    // vs
    x === 3 || x === '3'
    // vs
    Number(x) === 3
    // vs
    +x === 3

    View Slide

  37. switch (customer.type) {
    case 'guest': printCustomer('Guest'); break
    case 'subscriber': printSubscriber(customer.name); break
    case 'vendor': printVendor(customer.name)
    }
    // vs
    const actions = {
    guest: () => printCustomer('Guest'),
    subscriber: customer => printSubscriber(customer.name),
    vendor: customer => printVendor(customer.name)
    }
    actions[customer.type]?.(customer)

    View Slide

  38. let names = ''
    for (const record of records) {
    if (!!record.enabled) {
    const upperName = record.name.toUpperCase()
    names = (
    (names == '') ? upperName :
    `${names}, ${upperName }`
    )
    }
    }
    // vs
    const names = (
    records
    .filter(record => !!record.enabled)
    .map(record => record.name.toUpperCase())
    .join(', ')
    )

    View Slide

  39. View Slide

  40. View Slide

  41. View Slide

  42. function HomePage() {
    const [ locState, updateLocState ] = useState({
    reset: false,
    canceled: false
    })
    const locCanceled = locState.canceled
    const [ searchText, setSearchText ] = useState(null)
    const [ weatherCanceled, setWeatherCanceled ] = useState(false)
    const [ loc, locFound ] = useGetLocation({
    searchText,
    ...locState
    })
    const [ selectedLoc, setSelectedLoc ] = useState(null)
    const searchInputRef = useRef()
    locState.reset = locState.canceled = false
    return // ..
    }

    View Slide

  43. searchText={searchText}
    setSearchText={setSearchText}
    locFound={locFound}
    setSelectedLoc={setSelectedLoc}
    resetLocState={resetLocState}
    setWeatherCanceled={setWeatherCanceled}
    ref={searchInputRef}
    />

    View Slide

  44. function resetLocState() {
    setLocState({ reset: false, canceled: false })
    }
    function setLocState({
    reset = false,
    canceled = false
    } = {}) {
    // hack: new object inserted to change the
    // state-slot value and force a re-render
    updateLocState({
    reset,
    canceled
    })
    }

    View Slide

  45. https://weatheround.com

    View Slide

  46. (this spot intentionally left blank)

    View Slide