List of what Haskell doesn't have?

Yes :slight_smile: I think it’s really that much better personally, having used Scala. Also in reality it would be:

log $ "Running " <> show operation <> " at " <> show time <> " for " <> show timelength

Just now I wrote such a log statement and I think it’s completely unreadable. This feature would:

  1. Be pervasive through any production codebase
  2. Matches the standard of many other languages
  3. Not be that high an impact on GHC (this is my intuition; not fact - I would be surprised to hear this change would not be localised)

Put into perspective: if the “cases” proposal was accepted (which I think is fine), a format string syntax seems orders of magnitudes more important with similar costs.

3 Likes

Not if you pay the low, low cost of defining and importing a single library function.

The great strength of Haskell is that the language is kept sufficiently simple[1], firstly because, and secondly in order that, solutions to common problems can be implemented at the library level. A syntactic feature like do notation has such a vast scope of use that it pulls far more than its weight. Format strings, on the other hand, yeah they’re convenient, but I really don’t see that they pull their weight.

See also: osa1 - Some arguments against small syntax extensions in GHC

[1] Where by “simple” I mean something like “generic”

3 Likes

Perhaps looking at the Scala docs could motivate a little better than I can:

https://docs.scala-lang.org/overviews/core/string-interpolation.html

Note that the different interpolator heralds are library level. I can’t say for sure how the implementation would look in Haskell yet, but I would imagine it would share the spirit of an instance declaration.

Also, a compelling argument to counter yours is that in the languages that have format strings, they are used ubiquitously _and extensively in my experience. Even though we can achieve a similar pattern in Haskell via quasi quoters, we do not see the same usage. I think this is because the simplicity/readibility barrier to entry is the thing that makes them useful, not because Haskell code is so different from Scala.

I empathize with the feeling that we shouldn’t add small language extensions. For example, I think BlockArguments is overall a bad idea (though I could be convinced). However, I feel strongly that format strings are far past the line when compared to the recently accepted cases proposal.

I think a good comparison is OverloadedStrings. OverloadedStrings probably save fewer characters than a StringInterpolation extension would. I’m genuinely curious: would you also remove OverloadedStrings? The two seem pretty comparable to me.

1 Like

Another interesting one is multi-line strings… I often wish I had those. Pulling in and learning an eccentric TH library never seems worth it to me :confused:

2 Likes

Why don’t you consider Template Haskell as part of the core language? Isn’t a better solution just to fix the shortcomings of Template Haskell?

Edit: In particular, the flaws I’m thinking about are that Template Haskell expressions can perform arbitrary effects at compile time and that you need a lightweight Haskell parser to be able to parse the parts between the { } brackets.

2 Likes

Well I think the difference of a few characters/library selection/module import and subsequent readability make a world of difference:

.cabal:
   format-strings

{-# LANGUAGE TemplateHaskell #-}
import Text.Format
foo = log [s|Running $(operation) at $(time) for $(timeLength)]

versus

.cabal:
  StringInterpolators

foo = log s"Running $(operation) at $(time) for $(timeLength)"

I know it seems silly; but coming from other languages, people seem to enjoy the feature. I can’t think of a single reason why Haskellers would not use the feature if available (I certainly would). I would imagine that if we had support for format strings, Haskellers would use it, just as developers in other languages use their equivalents. Perhaps your codebases do use some TH interpolators that I’m not aware of? I have not seen this pattern out in the Haskell wild, even though I’m aware of 2/3 libraries that have the feature.

Perhaps the s" syntax could become sugar for [s|, and then we add s to the standard library. Doesn’t seem like a bad middle ground…

2 Likes

That change doesn’t seem worth saving two keystrokes to me. I feel like the rest of your concerns could be solved by using an alternative prelude (I don’t know if one that includes these format strings already exists).

Personally, I’m content with writing:

myLog ["Running", show operation, "at", show time, "for", show timeLength]

(where myLog = log . unwords)

Or if I really want to be fancy I would make a separate data type with a pretty printing instance:

data LogMessage = Running Operation Time TimeLength

instance Pretty LogMessage where
  pretty (Running op t tl) = unwords ["Running", pretty op, "at", pretty t, "for", pretty tl]

foo = log (Running operation time timeLength)
2 Likes

Relevant: https://www.reddit.com/r/haskell/comments/i3f3ip/state_of_string_interpolation_in_haskell/

I see @tomjaguarpaw commented there also :wink:

A good point mentioned there is dependency footprint.

I feel like the rest of your concerns could be solved by using an alternative prelude

I think this mentality is what causes Haskell’s ecosystem to get so out of sync. String formatting is something 90% of production codebases do and we should have one way to do it imo. It’s not something particularly controversial like transformers: it’s something everyone does and it’s weird to come to Haskell and not have it there.

Imagine you are learning Haskell coming from [Scala, Python, Rust…] and you search “format string” or “multi-line”. What you get is a few different libraries that are seldom used and an old reddit thread. I think it’s a bad look. And I think it’s symptomatic of the fact that sometimes Haskell doesn’t do the small things that make devs’ lives easier in code you write for a day-job.

Anyway: money where my mouth is. I think this is one for GHC proposals. Let me know if anyone wants to help me write it over the weekend…

3 Likes

Regarding that post by osa1: well spotted, @tomjaguarpaw!

Alas, Haskell 2010 already “bent that rule” (namely the inclusion of pattern guards) so it isn’t all that surprising to see the various other extensions being promoted (and accepted) at this time, including (ahem) some of my own.

With the advent of the HF, maybe an effort can be established to bring all these small syntactic changes together into something “more harmonious”.

2 Likes

What I like about the \cases proposals is that it allows us to (in principle) completely remove the feature of separate clauses for function definitions (which always seemed too sugary to me). This will never happen, of course, but I think Haskell would be better if we’d never had separate clauses and just used \cases from the beginning. That is, I would always prefer to write

foo = \cases
  _ (y:ys) -> ...
  (x:xs) _ -> ...

than

foo _ (y:ys) = ...
foo (x:xs) _ = ...

And yes, although I wouldn’t remove OverloadedStrings (since that would make many people very unhappy) in a hypothetical language that I invent I doubt that I would add it. I think the cost of (fs "Some string here") (or even a quasi-quoter) is not sufficiently high to warrant special syntax.

3 Likes

Rather than out of sync, I’d say the ecosystem is simply not yet mature. We first need to try out many different options and later we can see which one wins out and becomes the standard. Edit: But perhaps it is time now to propose a standard. That can always be attempted.

While it is common, I’d say it is also controversial. Every language implements it in a slightly different way with different tradeoffs (some languages even have multiple different implementations!).

4 Likes

Oh wow, I don’t remember that at all and I’m quite surprised how consistent I am over a two-year period!

2 Likes

Haha I was thinking the same thing :grinning_face_with_smiling_eyes:

I also appreciate the OverloadedStrings take - I think it’s less common that someone thinks that way. The extension missed GHC2010 by one vote iirc, in spite of the worsened errors (I’m very relieved it wasn’t added).

While it is common, I’d say it is also controversial.

@jaror While I agree, I don’t expect that the controversy will be too great in the same way as something like braces vs indent. I expect that as long as the syntax is reasonable, most people will just be happy to have it. I could be wrong of course! Perhaps others could comment on community opinions in other languages.

But perhaps it is time now to propose a standard.

A standard is sometimes better than no standard, even if that comes with controversy. A less-united ecosystem carries its own silent costs as well.

1 Like

I think this discussion is crystallising thoughts in my mind which have remained implicit until now. Specifically, whilst I strongly believe we should make Haskell more accessible by smoothing off rough edges[1] we shouldn’t be doing so in special purpose ways, even if those ways are widely used. Special purpose ways include pattern guards and comprehension syntax (which unless I am much mistaken can both be subsumed into do notation), as well as OverloadedStrings and string interpolation syntax.

By contrast, the monadic m >>= \x -> ... was a rough edge that was smoothed off in a wonderfully generic way by allowing it to be expressed with do x <- m; ....

I believe that Haskell should really be looking to find solutions of the second form, because searching for and implementing them is the Haskell community’s great strength. Honestly, any community can add small syntax extensions. It takes a community with the ingenuity of Haskell’s to find big general solutions.

(Side note: this discussion is getting far off the original topic. I think it’s great to keep having it but Discourse’s lack of threading mean that it is polluting the experience of everyone who isn’t interested in it!)

[1]: and indeed I’m contributing a great deal of my free time doing just that

6 Likes

Maybe there could be a merger of the two styles:

foo _      (y:ys) -> ...
    (x:xs) _      -> ...

…though it’s more likely to end up as just another nitpick.


That’s curious, considering the overloading of list comprehensions was removed from Haskell '98 because of the confusion caused by errors (also IIRC).

Regarding overloaded strings: how IsStr prevailed over e.g. Str (there’s no IsNum or IsOrd!) is a mystery to me…

Problem is, once people have used these options in production code, they will say: “I need that, for my existing project / library”. Then you can’t standardise it away.

The Alexandria library in Common Lisp is like that. It has some serious technical flaws with namespace pollution, but it cannot be removed, because it is used in too many places, and I understand that fixing it is hard too.

Or take, for example, the traditional head function in Prelude. It has some serious problems, and nobody would write it that way again. And yet, it seems that it must remain…

1 Like

Not necessarily. Most of the breaking changes in the last couple of years were rather useless, e.g. all that Semigroup/Monoid stuff.

People are exhausted by useless changes, not by significant API improvements. Otherwise they wouldn’t go to such great lengths as to use alternative Preludes.

E.g. the aeson breaking change was perceived well, because there’s a good reason.

Additionally, nothing stops us from maintaining older APIs. That’s the entire point of PVP. That would require further work, e.g. decoupling base and GHC better.

Not a fan, because in order to get something into stackage, maintainers often have to push themselves. I don’t see the use in that. I’m much more interested in Maintainership standards · Issue #7 · haskellfoundation/stability · GitHub

I also don’t believe that these ecosystem quality issues are so common that we should just accept it. I see a couple of action points:

  1. Better collaboration for boot packages (my experience with e.g. a simple containers patch spans across a couple years and was eventually left to rot. I have similar experience with patches to unix)
  2. Maintainers should be more proactive wrt future/backup maintainers. I believe edk does this very well. Other maintainers just left the community with the only option being package takeovers with no transition period.
  3. Lack of organized funding. E.g. afaik streamly could solve a lot of our problems, but it’s a huge project that simply needs more funding.
  4. The maintainership standards proposal I just linked. Ultimately with a filter function on hackage, if you don’t wanna bother with abandoned projects.
3 Likes

What is a multi-line string? I think if I check “Success and Failure in Haskell” by Moronuki, you can use \ within a string to have the compiler stop parsing the content of a string until \ is re-added.

1 Like