Feature request: String interpolation

Ah, that’s a problem. We will have to amend the instructions for publishing to Pursuit. Thanks for bringing this to my attention!

I’ve opened an issue for it here: https://github.com/purescript/pursuit/issues/402

Library has been published. See its installation instructions. PR to the official package set builds and is awaiting review. If you have an idea for building off of this, see how to refer to it in your bower.json file

Also, don’t use this library when doing a fold (e.g. foldl i "" arrayOfInts) until the inliner optimization is done. See this benchmark, which I hope isn’t naive.

Edit: the above benchmark was implemented incorrectly. See this comment for an accurate one.

2 Likes

So I saw the benchmark, the blue line indicates standard append and it blows up, while I guess the red should have, right?

I’ve run this benchmark with changed functions with one size = 10000.
Raw data results (because making a graph is beyond my skills :smile:)

  1. foldl (\a b -> show a <> show b) "" array
    causes JavaScript heap out of memory
  2. 0.001428 mean of foldl (\a b -> a <> show b) "" array
  3. 0.001227 mean of foldl i "" array
  4. 0.001106 mean of foldl interp "" array

In the original benchmark (1) is blue (the blow-up one) and (3) is red.

Remarks:

  • Running show on an accumulated string breaks my memory
  • Difference between (2),(3),(4) is negligible - using fold with interp is ok here

I think the performance problem is not here, but when you write a long expression with interp (or when it is generated with another typeclass generic magic)

Example

Simple usage of interp vs. append

fooI = i "a" 1 "b" 2 "c" 3
fooA = "a" <> show 1 <> "b" <> show 2 <> "c" <> show 3

And the compiled JS (without inlining)

var fooI = function (dictInterp) {
    return Data_Interpolate.i(
      Data_Interpolate.interpStringFunction(
        Data_Interpolate.interpIntFunction(
          Data_Interpolate.interpStringFunction(
            Data_Interpolate.interpIntFunction(
              Data_Interpolate.interpStringFunction(
                Data_Interpolate.interpIntFunction(
                  dictInterp)))))))("a")(1)("b")(2)("c")(3);
};

var fooA = "a" + (Data_Show.show(Data_Show.showInt)(1) + ("b" + (Data_Show.show(Data_Show.showInt)(2) + ("c" + Data_Show.show(Data_Show.showInt)(3)))));

You can convert the outputted json file into a graph by uploading it here and then exporting the result as an SVG or PNG file: http://harry.garrood.me/purescript-benchotron-svg-renderer/

1 Like

Looking at my original benchmark again, I realized I misread it. I thought the blue line was the i a b one, not the a <> b one…

So, the original benchmark I created used \acc next -> show acc <> show next when it should have been \acc next -> acc <> show next. @paluh pointed this out.

Here’s the correct benchmark, which better reflects my expectation:

1 Like

Did anyone check out purescript-template-strings or purescript-formatting?

While those work, they also feel boilerplate-y…

-- purescript-template-strings
"Hello, ${firstName} ${lastName}!" <~> { firstName: "Haskell", lastName: "Curry" }

"The answer: ${answer}" <~> { answer: 42 }

-- purescript-interpolate
i "Hello, " "Haskell" "Curry" "!"

i "The answer: "(show 42)

…and…

-- purescript-formatting
inbox :: forall r. Format String r (String -> Int -> r)
inbox = greeting <<< s " You have " <<< F.int <<< s " new messages."

message3 :: String
message3 = print inbox "Kris" 3
--> message3 == "Hello Kris! You have 3 new messages."

-- purescript-interpolate
inbox name amount = i "Hello " name "! You have "(show amount)" new messages."

message3 = inbox "Kris" 3
3 Likes