The full proposal to add string interpolation to Haskell can be found here: ghc-proposals #570. As one might imagine, there’s plenty to bike-shed here. This doc describes some options currently in discussion, which can be voted on from the survey linked below. After gathering survey results, this doc and the results will be synthesized into the proposal directly, for posterity.
Click here for the survey: GHC String Interpolation Survey. You might want to open this in a new tab to reference this doc when filling it out. To avoid over-complicating the survey, the options will not be completely exhaustive, so if you feel strongly about any options that aren’t covered, include them in the comments.
Thanks to Sebastian Graf and Michael Peyton Jones for their help with writing this survey!
<aside> 📣
UPDATE: A prototype of some options here and more options discussed on Discourse can be found here: https://github.com/brandonchinn178/string-syntax
</aside>
There are two primary questions on the table:
Options C and D are merely flavors of these main decisions, so focus mostly on Options A and B. To give you a general idea of the differences, we’ll first sketch out a couple examples here. Then the rest of the doc will separate the independent axes that can be voted on independently.
Options: A1, B1, C1, (D: N/A)
In this example, the answer to both of the primary questions above is “No”; the desugaring should be as minimal as possible, which makes the feature more conceptually simple and error messages less confusing. Unlike OverloadedStrings
, this option would not require any machinery like IsString
wired-in to the compiler.
The user would write code like:
-- String
s"Name: ${name}, Age: ${show age}"
-- Desugars to:
-- "Name: " <> name <> ", Age: " <> show age
-- SqlQuery, with -XOverloadedStrings
s"SELECT * FROM person \\
\\ WHERE name ILIKE ${toSql name} \\
\\ AND age = ${toSql age}"
-- Desugars to:
-- fromString "SELECT * FROM person WHERE name ILIKE "
-- <> toSql name
-- <> fromString " AND age = "
-- <> toSql age
class ToSql a where
toSql :: a -> SqlQuery
instance ToSql Int where
toSql x = _
instance ToSql String where
toSql s = _
In the String example, notice that the user is responsible for converting everything into a String, unlike what you might find in string interpolation in other languages (e.g. Python: f"Name: {name}, Age: {age}"
). But this could be seen as an advantage; Haskell generally avoids any implicit type conversions everywhere else (see: Num
).
In the SQL example, the user (or a library) might define a SqlQuery
type with IsString
and Monoid
instances, and the desugaring would Just Work, building on top of the existing OverloadedStrings
behavior.
Options: A2, B2, (C: N/A), D1
In this example, the answer to both of the primary questions above is “Yes”. We’ll add two MultiParam classes — Interpolate
+ Buildable
— and implicit add invocations of their methods in the desugaring.