OverloadedStringDefault proposal

What happens if the Text type isn’t in scope or the text package isn’t even available?

1 Like

I don’t think out of scope is a problem, is it? Numeric literals can default to Integer even if that type is not in scope:

Prelude> import Prelude (show)
Prelude> show 3
"3"

I guess it should raise an error. The same as what happen when you try to inline a function not in scope.

Imo the general idea of “you defined this literal here, but I can’t infer what its type is” is completely fine as is. Works the same as f = pure () giving you f :: Applicative f => f () as a type instead of IO ().

The more general way to tackle this issue would be user-defineable type defaults. Sure, you wouldn’t get the magical out-of-the-box-icity, but default IsString Data.Text.Text is short, obvious in what it does and can work exactly like a typeclass instance.

Also there’s a much bigger issue with literals, it’s the fact that they’re clunky. Conversions through runtime functions both mean you can’t throw compilation-time errors (see ByteString) and you can’t optimize anything (fromString ("naïve" :: Text) should just be packing "na\xC3\xAFve"# into a Text). I suppose quasiquoters are the answer, but writing [bs|thing|] would be a lot of overhead for something that seemingly should be supported out of the box.

2 Likes

I, too, would like "foo" : Text. Possibly even un-overloaded. My impression is that to do that properly, to have a nice consistent language overall in the end, we should also Text-ify base and Prelude. It is already hard to explain why we have String and Text and Lazy.Text (plus all the binary variants); I fear it becomes more of a mess as soon as "foo" : Text, but many basic functions still want String.

But such a change to base/Prelude is a huge undertaking, so I’m not very optimistic.

3 Likes

I don’t want Text by default. In fact I like String as they are (using Text because String is slow is actually a premature optimization), and the last thing that I want is a Textified prelude.
The main issue with a Textified Prelude is most functions from Text collides (in name) with List (e.g. length, drop, strip) etc …
The answer to that is the foldable/traverse proposal which is I think is a mistake (mainly because it introduce type classes to resolve name overloading). Moreover FTP is not enough for Text, as Text is a MonoFoldable.

What I want is to be able to use String when I can, and if I decide I need Text, use OverloadedString (in that required file) without having to add type signature everywhere.

Ideally I would have prefer String to be [Char] with Text optimization under the hood, but I understand is not possible.

1 Like

Isn’t this just OverloadedStrings + ExtendedDefaultRules?

default (Text)

x = "asdf" -- inferred as Text

Also related: Default polymorphic type parameters? · Issue #437 · ghc-proposals/ghc-proposals · GitHub

3 Likes

Basically. Is it implemented (or even accepted ?)

Not sure what you’re referring to. ExtendedDefaultRules has existed since GHC 6. The ghc-proposals link is just an issue, so it hasnt even been proposed officially yet.

Sorry I think I misunderstood your original comment. Is

this working at the moment ? since GHC 6 ?

If you turn on the ExtendedDefaultRules, it should! I started using it for a bit but started finding it too annoying to specify in every file

I didn’t know about ExtendeDefaultRules. Thanks!

I grant that String v. Text and strict v. lazy decisions are annoying, particularly for newcomers, but I don’t understand why people say this about the “binary variants”. Many languages correctly distinguish strings from arbitrary binary data.

4 Likes

Taking inspiration from QualifiedDo, which allows you to overload the meaning of do-notation on a case-by-case basis, perhaps we could have a QualifiedStrings extension which would allow you to write:

import Data.Text qualified as T

foo :: Text
foo:: T."sometext"
-- perhaps without dot? T"sometext"

The idea is that if SomeModule has a fromQualifiedString function defined, writing SomeModule."sometext" would desugar to fromQualifiedString "sometext".

3 Likes

Sorry, that’s what I meant with the parentheses: Those two are separate for a good, but here are again two variants (plus more with myriads of vectors and arrays). I agree that having separate types for text and bytes is the right thing to do.

2 Likes

Is that so much better than T.s "sometext" (for example) that we need a whole extension for it, rather than just a function?

1 Like

Perhaps not.

Another option could be (ab)using OverloadedRecordDot like this:

instance HasField "t" String Text where
    getField = Data.Text.pack

foo :: Text
foo = "sometext".t

But it feels gross.

6 Likes

I don’t understand what you mean by this; could you elaborate please?

1 Like

My bad, it should have been “Using Text because String is slow is a premature optimisation”.

What I mean is, as an abstraction seeing a string/text as a list of char is not only fine but reall nice.
Having String = [Char] allows you to do lots of things out of the box, iterating over, transforming, choping, filtering etc …
The Text abstraction doesn’t bring anything but disadvantages: name clashes, ambiguous types, back and forth conversion etc …

Strings have the reputation of being slow and inefficient. It is true in theory, but most of the time it is good enough and therefore better than Text. Switching to Text for performance reason is an optimisation.
Doing so before actually encountering performance issue is “premature optimisation”.

1 Like

Yes, it has a tighter binding. Compare

mkName (s "tom") (s "jaguar")

to

mkName T."tom" T."jaguar"

Also is there not any performance difference ? I assume that s "sometext" will actually create a (frowned upon String) and then convert it, instead of creating directly a Text.

1 Like