Stream: beginners

Topic: What is the point on knowing if a function is pure or not?


view this post on Zulip fenugurod (Aug 21 2026 at 21:33):

I do understand, from a conceptual point of view, but on real world applications, most of the functions will be impure, so, what is the point? Let's say we have a web API that has the transport layer, impure to communicate over the socket, that will call a service layer, impure because it will do calls at the persistence layer, and the persistence layer, that, well, will do calls to databases. I do expect that the majority of the functions will be impure, then there are logs and instrumentation.

view this post on Zulip Aurélien Geron (Aug 21 2026 at 21:39):

You can usually organize your app to have 95% pure functions and 5% impure, and this has huge benefits for testing, reliability, reasoning about your code, making it more reusable, etc.

Instead of main=>f=>g=>h=>database, you could reorganize this so that the functions f, g, and h are pure, and only take pure data in/data out, and they don't call the database, and the main function takes care of calling the impure stuff.

view this post on Zulip fenugurod (Aug 21 2026 at 21:47):

Ok, makes sense. I'm just too new to this kind of programming that I'll need to play a little with it to see if it fits my mental model. FP in general is ok, I've been playing with Gleam lately, and I have some experience in Elixir.

For example, to be able to instrument the code with something like OpenTelemetry. Yes, I could move the instrumentation to an upper layer that is performing side effects, but then I would have just a single layer of monitoring, or I would need to structure my application very very flat.

view this post on Zulip Jasper Woudenberg (Aug 21 2026 at 22:03):

I'm a big fan of OpenTelemetry for observability. One experience I have with with instrumenting purely functional code, is that if you put the instrumentation around all the impure "primitives" around the boundary of your application (your database queries, network requests, incoming requests, etc), you typically have great visibility into your code. This relatives to Aurélien's point: if you know what the impure bits of your code are doing, then the pure bits between them typically don't pose much of a mystery.

view this post on Zulip Aurélien Geron (Aug 21 2026 at 22:12):

There's also the pattern where a pure function can act as if it was performing a sequence of effects, but it's actually just creating a pure data structure containing the list of desired effects. The outer layer of your app can then take this data structure and actually execute the effects. Hope this makes sense.

view this post on Zulip fenugurod (Aug 21 2026 at 22:37):

Ah ok, I got it now. I was using this pattern this week at Gleam in a library called nibble (parse combinator).

  let parser = {
    use _ <- do(nibble.token(LParen))
    use x <- do(int_parser)
    use _ <- do(nibble.token(Comma))
    use y <- do(int_parser)
    use _ <- do(nibble.token(RParen))

    return(Point(x, y))
  }

  let assert Ok(tokens) = lexer.run("(1, 2)", lexer)
  let assert Ok(point) = nibble.run(tokens, parser)

You define a parser and then you run it with the tokens you have generated from the lexer.

view this post on Zulip Luke Boswell (Aug 21 2026 at 22:39):

Nibble is a cool name for a parser :smile:

view this post on Zulip Johannes Rubenz (Aug 22 2026 at 09:21):

Good question, @fenugurod ! :slight_smile:

The visual indicators of pure/impure functions (! and ->/=> in Roc) help me immediately see whether I'm living up to my architectural goal of a functional core and an imperative shell (G. Bernhardt).

I want to see ! at the boundary of my app with the “outer” world, and I don't want to see any ! in the realm of my pure business logic.

I fell in love with Elm years ago, and The Elm Architecture gave me a very concrete example of this idea: there is one (very appealing) layer that simply describes what I want the “world” to look like in pure data. Then there is another layer (the Elm runtime) that deals with the mess of the “outer” world and executes those commands, turning the results back into pure (tagged) data that my application can handle.

Your Nibble example is a perfect illustration of the same idea: the parser combinators describe what should happen, and run is what actually interprets that description.

view this post on Zulip Steve Howell (Aug 23 2026 at 19:00):

Somewhat related, Gary Bernhardt coined or made famous a good term that I like: Functional Core, Imperative Shell

https://www.destroyallsoftware.com/screencasts/catalog/functional-core-imperative-shell


Last updated: Sep 03 2026 at 15:16 UTC