# GHC String Interpolation Survey - Final Results

**URL:** <https://discourse.haskell.org/t/ghc-string-interpolation-survey-final-results/11971>\
**Category:** Announcements\
**Created:** [May 1, 2025, 5:59am UTC](https://discourse.haskell.org/t/ghc-string-interpolation-survey-final-results/11971 "2025-05-01T05:59:28Z")\
**Posts on this page:** 20\
**Page:** 1

<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:** [May 1, 2025, 5:59am UTC](https://discourse.haskell.org/t/ghc-string-interpolation-survey-final-results/11971/1 "2025-05-01T05:59:28Z")

</div>

Apologies for the many, many posts, but this will be the last one. We made it.

Without further ado, results can be found here: [GHC String Interpolation Survey Results](https://brandonchinn178.github.io/ghc-string-interpolation-prototypes/results/). Scroll down to the “Commentary” section.

I’m really excited about the hybrid solution that came out of this process; I think this will truly be the best of all the worlds. A tremendous thank you to everyone who participated, I think we’ve definitely come out of this with a much stronger proposal than what I started with.

Feel free to post comments/questions in this thread, I will respond to what I can. But no matter what, I promise that there will be no more surveys 🙂 I will update [the proposal](https://github.com/ghc-proposals/ghc-proposals/pull/570#issuecomment-2820152690) when I can get to it, so subscribe there for further updates.

---

<div class="post-metadata">

**Author:** ![TeofilC](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/teofilc/32/3987_2.png) [@TeofilC](https://discourse.haskell.org/u/TeofilC)\
**Post date:** [May 1, 2025, 8:01am UTC](https://discourse.haskell.org/t/ghc-string-interpolation-survey-final-results/11971/2 "2025-05-01T08:01:03Z")

</div>

Thanks for all your work on this!

---

<div class="post-metadata">

**Author:** ![danidiaz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/danidiaz/32/92_2.png) [@danidiaz](https://discourse.haskell.org/u/danidiaz)\
**Post date:** [May 1, 2025, 8:13am UTC](https://discourse.haskell.org/t/ghc-string-interpolation-survey-final-results/11971/3 "2025-05-01T08:13:02Z")

</div>

I’ve often wanted something like `-XQualifiedLiterals` by itself! Although I would probably call it `-XQualifiedStringLiterals`.

---

<div class="post-metadata">

**Author:** ![Tikhon](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tikhon/32/3667_2.png) [@Tikhon](https://discourse.haskell.org/u/Tikhon)\
**Post date:** [May 1, 2025, 2:28pm UTC](https://discourse.haskell.org/t/ghc-string-interpolation-survey-final-results/11971/4 "2025-05-01T14:28:52Z")

</div>

Thanks for all your work on this!

The final suggestions you have with both `-XStringInterpolation` and `-XQualifiedLiterals` sounds great to me and I love the overall design. I found the SQL example intuitive at a glance, even before I read any of the explanation for it:

```haskell
SQL."select * from users where name = ${name}"

```

---

<div class="post-metadata">

**Author:** ![michaelpj](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/michaelpj/32/417_2.png) [@michaelpj](https://discourse.haskell.org/u/michaelpj)\
**Post date:** [May 1, 2025, 3:48pm UTC](https://discourse.haskell.org/t/ghc-string-interpolation-survey-final-results/11971/5 "2025-05-01T15:48:04Z")

</div>

Thanks for running this! I’m not terribly surprised to see that nobody except me likes `explicit`, but I’m glad we checked!

---

<div class="post-metadata">

**Author:** ![Vlix](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/vlix/32/1551_2.png) [@Vlix](https://discourse.haskell.org/u/Vlix)\
**Post date:** [May 1, 2025, 4:43pm UTC](https://discourse.haskell.org/t/ghc-string-interpolation-survey-final-results/11971/6 "2025-05-01T16:43:26Z")

</div>

I was the other person that liked `explicit` then, I guess 🙃  
But I’m not discontent with the results.

---

<div class="post-metadata">

**Author:** ![hasufell](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/hasufell/32/1250_2.png) [@hasufell](https://discourse.haskell.org/u/hasufell)\
**Post date:** [May 2, 2025, 6:22am UTC](https://discourse.haskell.org/t/ghc-string-interpolation-survey-final-results/11971/7 "2025-05-02T06:22:01Z")

</div>

Just a sidenote: the result also means that `OsPath` will not work with this, because it does not have an `IsString` instance: [GitHub · Where software is built](https://github.com/haskell/filepath/issues/236)

---

<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:** [May 2, 2025, 6:32am UTC](https://discourse.haskell.org/t/ghc-string-interpolation-survey-final-results/11971/8 "2025-05-02T06:32:47Z")

</div>

It should work with the new QualifiedLiterals extension proposed in the results?

```haskell
module System.OsPath.Interpolate where

fromParts :: [String] -> Maybe OsPath
fromParts = encodeUtf . concat

fromString :: String -> String
fromString = id

-- reusing String's Interpolate instances as an example;
-- the signature here could be anything
interpolate :: Interpolate a String => a -> String
interpolate = show

```

```haskell
{-# LANGUAGE QualifiedLiterals #-}

import System.OsPath.Interpolate qualified as OsPath

main = do
  let user = "testuser"
  print OsPath."/home/${user}/foo.txt"
  -- Just "/home/testuser/foo.txt"

```

---

<div class="post-metadata">

**Author:** ![TeofilC](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/teofilc/32/3987_2.png) [@TeofilC](https://discourse.haskell.org/u/TeofilC)\
**Post date:** [May 2, 2025, 7:41am UTC](https://discourse.haskell.org/t/ghc-string-interpolation-survey-final-results/11971/9 "2025-05-02T07:41:22Z")

</div>

Why does the `QualifiedLiterals` idea require qualifying things by modules? As far as I can tell something like `foo"..."` should work where `foo` is a record of the appropriate type. This is how Quasiquotes work

---

<div class="post-metadata">

**Author:** ![tomjaguarpaw](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tomjaguarpaw/32/1230_2.png) [@tomjaguarpaw](https://discourse.haskell.org/u/tomjaguarpaw)\
**Post date:** [May 2, 2025, 7:48am UTC](https://discourse.haskell.org/t/ghc-string-interpolation-survey-final-results/11971/10 "2025-05-02T07:48:11Z")

</div>

> [@TeofilC](#):
>
> Why does the `QualifiedLiterals` idea require qualifying things by modules?

Now I come to think of it, why does `QualifiedDo`? It could also technically work as `var.do`, where `var` is a variable whose type is a record containing the desired `Monad` operations. Perhaps the parser would have a hard time, especially when `OverloadedRecordDot` is in play.

---

<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:** [May 2, 2025, 7:49am UTC](https://discourse.haskell.org/t/ghc-string-interpolation-survey-final-results/11971/11 "2025-05-02T07:49:03Z")

</div>

Sure, that would work too. But the `QualifiedLiterals` approach has two advantages:

- It removes whitespace ambiguity (`Foo."..."` is invalid syntax right now, while `foo"..."` is a function application)
- `QualifiedLiterals` is extendable to numeric literals

Conceptually, I also like the consistency with QualifiedDo; `foo"..."` would be yet another syntactic structure added to the language.

---

<div class="post-metadata">

**Author:** ![tomjaguarpaw](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tomjaguarpaw/32/1230_2.png) [@tomjaguarpaw](https://discourse.haskell.org/u/tomjaguarpaw)\
**Post date:** [May 2, 2025, 7:53am UTC](https://discourse.haskell.org/t/ghc-string-interpolation-survey-final-results/11971/12 "2025-05-02T07:53:11Z")

</div>

It removes a possibility: you can’t determine the interpolation function at run time. I’m not sure that feature would be used in practice, but it seems unfortunate to close it off unless absolutely necessary.

---

<div class="post-metadata">

**Author:** ![danidiaz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/danidiaz/32/92_2.png) [@danidiaz](https://discourse.haskell.org/u/danidiaz)\
**Post date:** [May 2, 2025, 7:58am UTC](https://discourse.haskell.org/t/ghc-string-interpolation-survey-final-results/11971/13 "2025-05-02T07:58:27Z")

</div>

> [@tomjaguarpaw](#):
>
> It removes a possibility: you can’t determine the interpolation function at run time

That possibility seems little better than explicitly calling the interpolation function.

---

<div class="post-metadata">

**Author:** ![tomjaguarpaw](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tomjaguarpaw/32/1230_2.png) [@tomjaguarpaw](https://discourse.haskell.org/u/tomjaguarpaw)\
**Post date:** [May 2, 2025, 8:01am UTC](https://discourse.haskell.org/t/ghc-string-interpolation-survey-final-results/11971/14 "2025-05-02T08:01:17Z")

</div>

Why doesn’t that argument apply to statically-determined interpolation functions too?

---

<div class="post-metadata">

**Author:** ![danidiaz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/danidiaz/32/92_2.png) [@danidiaz](https://discourse.haskell.org/u/danidiaz)\
**Post date:** [May 2, 2025, 8:20am UTC](https://discourse.haskell.org/t/ghc-string-interpolation-survey-final-results/11971/15 "2025-05-02T08:20:25Z")

</div>

For my taste, using the module name would be more direct and understandable than trying to puzzle out the provenance of a value that guides interpolation. Worth the loss in flexibility, especially considering that the implementation would be simpler.

Also, IIUC, the interpolation should work for a potentially open set of value types, so I’m not sure how the record carrying the interpolation functions for each type would work.

And if I wanted to use `-XQualifiedLiterals` on its own as an alternative to `-XOverloadedStrings` (say, be able to write `T."foo"` to build a `Text`) the more flexible approach would be overkill.

---

<div class="post-metadata">

**Author:** ![romes](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/romes/32/2912_2.png) [@romes](https://discourse.haskell.org/u/romes)\
**Post date:** [May 2, 2025, 9:18am UTC](https://discourse.haskell.org/t/ghc-string-interpolation-survey-final-results/11971/16 "2025-05-02T09:18:55Z")

</div>

Im also happy with the simpler default interpolation put together with QualifiedLiterals. Good idea.

---

<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:** [May 2, 2025, 2:26pm UTC](https://discourse.haskell.org/t/ghc-string-interpolation-survey-final-results/11971/17 "2025-05-02T14:26:56Z")

</div>

Do you have a use-case in mind?

You could get similar behavior with a custom interpolator

```haskell
module MyInterpolate where

str1 :: [Either String Val] -> String
str2 :: [Either String Val] -> String

fromParts = id
fromString = Left
interpolate :: Val -> Either String Val
interpolate = Right

data Val = I Int | S String

```

```haskell
import MyInterpolate qualified as I

main = do
  let str = if ... then str1 else str2
  putStrLn $ str $ I."..."

```

---

<div class="post-metadata">

**Author:** ![tomjaguarpaw](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tomjaguarpaw/32/1230_2.png) [@tomjaguarpaw](https://discourse.haskell.org/u/tomjaguarpaw)\
**Post date:** [May 2, 2025, 2:52pm UTC](https://discourse.haskell.org/t/ghc-string-interpolation-survey-final-results/11971/18 "2025-05-02T14:52:44Z")

</div>

No , I don’t have a use case in mind, I just didn’t want to close off a possibility unnecessarily (Haskell has benefitted a lot historically from this kind of approach.)

Ah yes, OK, perhaps if you define an interpretation that’s “open” in some sense (or maybe “initial”?) then you can handle it however you want after the fact.

---

<div class="post-metadata">

**Author:** ![lukec](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/lukec/32/2368_2.png) [@lukec](https://discourse.haskell.org/u/lukec)\
**Post date:** [May 3, 2025, 6:14am UTC](https://discourse.haskell.org/t/ghc-string-interpolation-survey-final-results/11971/19 "2025-05-03T06:14:42Z")

</div>

Ok, I hate to toss this in so late, but, the `fromParts` is essentially a `Monoid` like interface. So, it could be broken up into a `mempty`/`<>` pair. If we did this, we would also have the option of allowing the `<>` type to change the type of the result as we go (no longer a true monoid).

My motivation here is to allow for the types to change as we construct the value, which has the benefits of allowing some interesting patterns (e.g. returning functions).

A simple demonstration, I’ve called the functions `%%` and `final`, as well as introducing an `extract` function:

```haskell
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE GADTs #-}

-- Data.Printf.Interpolate
-- defines `%%`, `extract` and `final`
import Data.Proxy

-- Represents a string literal, as opposed to a string that should be interpolated.
newtype Lit = Lit String

infixr 5 %%
class Interpolate a b c | a c -> b where
    (%%) :: a -> b -> c

data Printf a where
    PrintfEnd :: Printf String
    PrintfLit :: String -> Printf a -> Printf a
    PrintfArg :: Show b => Printf a -> Printf (b -> a)

final :: Printf String
final = PrintfEnd

instance Interpolate Lit (Printf a) (Printf a) where
    Lit s %% p = PrintfLit s p

instance Interpolate String (Printf a) (Printf a) where
    s %% p = PrintfLit s p

instance Interpolate (Proxy Int) (Printf a) (Printf (Int -> a)) where
    Proxy %% p = PrintfArg p

instance Interpolate (Proxy Float) (Printf a) (Printf (Float -> a)) where
    Proxy %% p = PrintfArg p

instance Interpolate Int (Printf a) (Printf a) where
    x %% p = PrintfLit (show x) p

evalPrintf' :: Printf a -> (String -> String) -> a
evalPrintf' PrintfEnd f = f ""
evalPrintf' (PrintfLit s p) f = evalPrintf' p ((++s) . f)
evalPrintf' (PrintfArg p) f = \x -> evalPrintf' p ((++ show x) . f)

extract :: Printf a -> a
extract p = evalPrintf' p id

int :: Proxy Int
int = Proxy

float :: Proxy Float
float = Proxy

x :: Int
x = 3

y :: Int
y = 5

z :: Float
z = 3.2

-- Usage:
-- Printf."Example ${x} ${int} ${float}" y z
-- desugars to
res = extract (Lit "Example " %% x %% Lit " " %% int %% Lit " " %% float %% final) y z
-- "Example 3 5 3.2"

-- Or consider possibilities like
-- getNameForPerson :: PersonId -> IO String
-- getNameForPerson = SQL."select name from people where id is ${personid}"

```

Note how the `Printf` example above supports both interpolating the `x` into the string directly, as well as leaving other parameters to be supplied as arguments to the resulting function.

I haven’t really considered any performance or type-error ergonomics issues this may present yet.

---

<div class="post-metadata">

**Author:** ![juhp](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/juhp/32/323_2.png) [@juhp](https://discourse.haskell.org/u/juhp)\
**Post date:** [May 3, 2025, 6:43am UTC](https://discourse.haskell.org/t/ghc-string-interpolation-survey-final-results/11971/20 "2025-05-03T06:43:52Z")

</div>

Goodness… @brandonchinn178: sorry to be that person, but I have literally no idea what the conclusion of the results is: could you pretty please add a TL;DR at the top or in this thread at least? 🙂

[Next page](https://discourse.haskell.org/t/ghc-string-interpolation-survey-final-results/11971.md?page=2)
