# Challenge: FeatureFlag pure functions

**URL:** <https://discourse.haskell.org/t/challenge-featureflag-pure-functions/9861>\
**Category:** Uncategorized\
**Created:** [July 2, 2024, 1:29am UTC](https://discourse.haskell.org/t/challenge-featureflag-pure-functions/9861 "2024-07-02T01:29:14Z")\
**Posts on this page:** 10\
**Page:** 2

<div class="post-metadata">

**Author:** ![brandonchinn178](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/brandonchinn178/32/5103_2.png) [@brandonchinn178](https://discourse.haskell.org/u/brandonchinn178)\
**Post date:** [July 2, 2024, 3:52pm UTC](https://discourse.haskell.org/t/challenge-featureflag-pure-functions/9861/21 "2024-07-02T15:52:17Z")

</div>

> [@atravers](#):
>
> Wouldn’t laziness mean that the subsequent calls to `flags` reuse the result of the first call?

What I mean is that presumably, the rest of the application also needs the database connection, and there wouldnt be a way to share the db connection between the flag-fetching and the rest of the app, youd have to initialize it twice

But this is also moot because for our specific use case, we have to take the db conn in as an argument to our main function, so we’d have to smuggle that into the global unsafePerformIO action anyway

---

<div class="post-metadata">

**Author:** ![atravers](https://avatars.discourse-cdn.com/v4/letter/a/45deac/32.png) [@atravers](https://discourse.haskell.org/u/atravers)\
**Post date:** [July 2, 2024, 3:55pm UTC](https://discourse.haskell.org/t/challenge-featureflag-pure-functions/9861/22 "2024-07-02T15:55:18Z")

</div>

Question: if the program can’t connect to the database, is it expected to keep running, or is it allowed to fail or end?

---

<div class="post-metadata">

**Author:** ![brandonchinn178](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/brandonchinn178/32/5103_2.png) [@brandonchinn178](https://discourse.haskell.org/u/brandonchinn178)\
**Post date:** [July 2, 2024, 4:50pm UTC](https://discourse.haskell.org/t/challenge-featureflag-pure-functions/9861/23 "2024-07-02T16:50:18Z")

</div>

> [@atravers](#):
>
> But more generally - if you really, _really_ think that _“top-level mutability”_ (even _“once off”_ ) is the solution, then look no further than:

I mean, yes I agree it’s generally a terrible idea. But I don’t see a better option here, other than making everything monadic or passing a constraint or argument everywhere.

> [@atravers](#):
>
> Question: if the program can’t connect to the database, is it expected to keep running, or is it allowed to fail or end?

For simplicity, assume it will never fail to connect. It’s not an issue for us, since we get a connection in the entrypoint (again, we have a really weird set up).

---

<div class="post-metadata">

**Author:** ![BurningWitness](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/burningwitness/32/3665_2.png) [@BurningWitness](https://discourse.haskell.org/u/BurningWitness)\
**Post date:** [July 2, 2024, 5:52pm UTC](https://discourse.haskell.org/t/challenge-featureflag-pure-functions/9861/24 "2024-07-02T17:52:55Z")

</div>

> [@tomjaguarpaw](#):
>
> isn’t the point that the flags are _immutable once they’re set_.

That makes me wonder what the application architecture is. For the dumb “parse a set of requests into a set of known functions” case the immutability doesn’t matter: you’d just have a feature flag blob that is woven into every function, then an external request could be used alter the flags.

---

<div class="post-metadata">

**Author:** ![Ambrose](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/ambrose/32/5672_2.png) [@Ambrose](https://discourse.haskell.org/u/Ambrose)\
**Post date:** [July 2, 2024, 5:57pm UTC](https://discourse.haskell.org/t/challenge-featureflag-pure-functions/9861/25 "2024-07-02T17:57:38Z")

</div>

I think the simplest solution is basically what you did - `-XImplicitParams` (no need for `reflection` tbh) + a way to fetch the flags through `IO`. It really isn’t a complicated problem…

The main complication I can see is if your feature flags type is a record that in turn imports a lot of internal modules for custom types. That is doomed to fail as the project grows - I’ve seen some pretty egregious compilation time waste due to this at scale. But you can use some sort of dynamic solution instead of a record that’d work just as well (e.g. `TypeRepMap` or one of its friends).

---

<div class="post-metadata">

**Author:** ![chrisdone](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/chrisdone/32/1408_2.png) [@chrisdone](https://discourse.haskell.org/u/chrisdone)\
**Post date:** [July 2, 2024, 8:19pm UTC](https://discourse.haskell.org/t/challenge-featureflag-pure-functions/9861/26 "2024-07-02T20:19:34Z")

</div>

If this is a **greenfield** project, then you could consider investing your efforts in lightning fast build and re-deploy.

That way, your feature flag toggling is changing a line in the source code, which is really what everyone wishes they could do instead of parsing annoying config files.

It’s mentally easier to think about immutable processes—where you tear down the existing thing and make a new fresh thing—rather than modifying a running process in-place.

It’s unlikely GHC Haskell will ever achieve this, though not impossible, but Unison might achieve it if not already; I haven’t used it for something real. That or another CAS-based language; only in that setting can you avoid recompiling and run only the tests that need to re-run. Achieving redeploy of a large service measured in seconds is barely imaginable in any other setting.

I personally think it’s a good mental exercise to consider many types of problems (such as this one) as symptomatic of deployment pain. Having to stick IO in our code to “cheat” when really what we want is faster updates is a bit sad, but practical.

---

<div class="post-metadata">

**Author:** ![brandonchinn178](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/brandonchinn178/32/5103_2.png) [@brandonchinn178](https://discourse.haskell.org/u/brandonchinn178)\
**Post date:** [July 2, 2024, 8:37pm UTC](https://discourse.haskell.org/t/challenge-featureflag-pure-functions/9861/27 "2024-07-02T20:37:21Z")

</div>

In theory, I’d agree. Except if your greenfield project is deployed as a feature in a legacy system that has a really long deploy cycle. Say, perhaps, the greenfield project is a binary that the legacy system calls to do a thing, and for security reasons, that binary has to be bundled with the legacy system instead of being able to be pulled from some repository, which can be updated out of band

---

<div class="post-metadata">

**Author:** ![atravers](https://avatars.discourse-cdn.com/v4/letter/a/45deac/32.png) [@atravers](https://discourse.haskell.org/u/atravers)\
**Post date:** [July 2, 2024, 10:16pm UTC](https://discourse.haskell.org/t/challenge-featureflag-pure-functions/9861/28 "2024-07-02T22:16:10Z")

</div>

> [@brandonchinn178](#):
>
> But I don’t see a better option here, other than making everything monadic or passing a constraint or argument everywhere.

It won’t be a _“better”_ option at all once this is implemented:

> <https://github.com/ghc-proposals/ghc-proposals/pull/240>
>
> The \[proposal\](https://github.com/ghc-proposals/ghc-proposals/blob/master/propos…als/0052-threaded-by-default.rst) has been accepted; the following discussion is mostly of historic interest.
> 
> \---
> 
> Compile with \`-threaded\` by default. For those in need of the non-threaded RTS, provide the new \`-single-threaded\` flag.
> 
> \[Rendered\](https://github.com/ulysses4ever/ghc-proposals/blob/master/proposals/0000-threaded-by-default.rst).
> 
> \[Reddit thread\](https://www.reddit.com/r/haskell/comments/byvq5w/ghc\_proposal\_compile\_with\_threaded\_rts\_by\_default/).

- with mutual exclusion, all threads will have to wait their turn to access the DB, so not much better that being `-single-threaded`;

- _without_ mutual exclusion - are you absolutely sure that DB is immutable? If not, joyous nondeterminism awaits…

* * *

> [@brandonchinn178](#):
>
> For simplicity, assume it will never fail to connect.

So assuming ( **!!!** ) the DB is immutable, and each access of it doesn’t have secondary effects that the rest of the program can observe…the DB _could_ be treated as one large implicit argument to the entire program, in the same way `$+` in Miranda™ was. But you only have to look at the GHC `DynFlags` experience to see how badly _that_ can go wrong.

The only other suggestion I can think of right now would be to look at:

- [Controlling Chaos: On Safe Side-Effects in Data-Parallel Operations](https://www.sac-home.org/_media/publications:pdf:2009_6.pdf) (2009)

…maybe you can reuse the technique it describes.

---

<div class="post-metadata">

**Author:** ![darkxero](https://avatars.discourse-cdn.com/v4/letter/d/f07891/32.png) [@darkxero](https://discourse.haskell.org/u/darkxero)\
**Post date:** [July 3, 2024, 12:48am UTC](https://discourse.haskell.org/t/challenge-featureflag-pure-functions/9861/29 "2024-07-03T00:48:42Z")

</div>

> [@brandonchinn178](#):
>
> cpp macros - cant modify at runtime

Main.hs:

```haskell
import System.IO.Unsafe

import Flags

experimental_f = 4

f :: Integer -> Integer
f x
    | flag = x * 2
    | otherwise = x * 4
    where
        flag = unsafePerformIO $ getFlag experimental_f

main :: IO ()
main = do
    print $ f 10
    setFlag experimental_f True
    print $ f 10
    setFlag experimental_f False
    print $ f 10
    print "done"

```

Flags.hs:

```haskell
{-# LANGUAGE CApiFFI #-}

module Flags (
    getFlag,
    setFlag
) where

import Foreign.C.Types

foreign import capi "libflags.h flags" c_flags :: CInt -> CInt -> CInt -> IO CInt

getFlag :: Integer -> IO Bool
getFlag flag = do
    v <- c_flags (CInt 0) (CInt (fromInteger flag)) (CInt 0)
    return $ if v == 0 then False else True

setFlag :: Integer -> Bool -> IO ()
setFlag flag value = do
    _ <- c_flags (CInt 1) (CInt (fromInteger flag)) (CInt (if value then 1 else 0))
    return ()

```

libflags.h:

```haskell
int flags(int mode, int flag, int value);

```

libflags.c:

```haskell
int flags(int mode, int flag, int value) {
    static int f[128] = {0};
    return mode ? f[flag] = value : f[flag];
}

```

GHC is the greatest C compiler, you get Haskell for free.

---

<div class="post-metadata">

**Author:** ![tcard](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tcard/32/4823_2.png) [@tcard](https://discourse.haskell.org/u/tcard)\
**Post date:** [July 3, 2024, 7:18am UTC](https://discourse.haskell.org/t/challenge-featureflag-pure-functions/9861/30 "2024-07-03T07:18:38Z")

</div>

The mention of pure functions in the title piqued my interest. I have been wanting to write a blog entry that includes use of the [`dependent-sum`](https://hackage.haskell.org/package/dependent-sum) and [`dependent-map`](https://hackage.haskell.org/package/dependent-map) packages, and this topic is a good one.

TLDR:

```haskell
data FeatureFlag :: Type -> Type where
  Fix_202407_SomeBug_42 :: FeatureFlag Bool
  Ref_202407_SomeBusinessLogic :: FeatureFlag Bool

deriveGEq ''FeatureFlag
deriveGCompare ''FeatureFlag
deriveGShow ''FeatureFlag

type FeatureFlagMap = DMap FeatureFlag Identity

newtype FeatureFlagConfig (tag :: FeatureFlag a)
  = FeatureFlagConfig
    { config :: Maybe a
    }
  deriving (Eq, Show)

lookup
  :: FeatureFlag a
  -> FeatureFlagMap
  -> FeatureFlagConfig (tag :: FeatureFlag a)
lookup ff = FeatureFlagConfig . fmap runIdentity . DMap.lookup ff

```

Details:

1. Problem definition [blog](https://www.extrema.is/blog/2024/07/03/haskell-feature-flag-demo-part-1)
2. Implementation using a global, without tagged configuration [blog](https://www.extrema.is/blog/2024/07/03/haskell-feature-flag-demo-part-2)
  - [Definition code](https://github.com/ExtremaIS/www.extrema.is-code/blob/main/blog/2024-07-03-haskell-feature-flag-demo/src/Demo1/FeatureFlag.hs)
  - [Demo app code](https://github.com/ExtremaIS/www.extrema.is-code/blob/main/blog/2024-07-03-haskell-feature-flag-demo/src/Demo1/IO.hs)

3. Implementation using a global, with tagged configuration [blog](https://www.extrema.is/blog/2024/07/03/haskell-feature-flag-demo-part-3)
  - [Definition code](https://github.com/ExtremaIS/www.extrema.is-code/blob/main/blog/2024-07-03-haskell-feature-flag-demo/src/Demo2/FeatureFlag.hs)
  - [Demo app code](https://github.com/ExtremaIS/www.extrema.is-code/blob/main/blog/2024-07-03-haskell-feature-flag-demo/src/Demo2/IO.hs)

4. Tests [blog](https://www.extrema.is/blog/2024/07/03/haskell-feature-flag-demo-part-4)
  - Without tagged configuration [tests](https://github.com/ExtremaIS/www.extrema.is-code/blob/main/blog/2024-07-03-haskell-feature-flag-demo/test/Demo1/IO/Test.hs)
  - With tagged configuration [tests](https://github.com/ExtremaIS/www.extrema.is-code/blob/main/blog/2024-07-03-haskell-feature-flag-demo/test/Demo2/IO/Test.hs)

[Previous page](https://discourse.haskell.org/t/challenge-featureflag-pure-functions/9861.md?page=1)
