propensive / contextual

Statically-checked string interpolation in Scala
https://soundness.dev/contextual/
251 stars 23 forks source link

Standard collection of interpolators? #10

Closed nrinaudo closed 3 years ago

nrinaudo commented 7 years ago

Scala and Java both have lots of types that are initialised from a string, where an illegal string will compile but fail at runtime. It'd be great if Contextual could provide standard modules for such types.

I've often felt the need for such a tool for:

I'm sure there are a lot commonly used types out there that would benefit from a literal syntax.

Happy to lend a hand or even write most of these, but if you feel this is a good idea, the structure needs to come from you - do you want that to be a completely separate library that depends on Contextual? Standard Contextual modules that live in this repository?

nrinaudo commented 7 years ago

Just to make this clear, here's how I'd imagine the syntax to work:

val jodaFormat: org.joda.time.format.DateTimeFormatter = joda"yyyy-MM-dd'T'HH:mm:ssV"
val java8Format: java.time.format.DateTimeFormatter = date"yyyy-MM-dd'T'HH:mm:ssV"
val url: java.net.URL = url"https://github.com"
val uri: java.net.URI = uri"https://github.com"
nrinaudo commented 7 years ago

Mentioned on twitter:

ShaneDelmore commented 7 years ago

These would be handy to have, but it would also be nice to have a non-side-effecting URI/URL. The java.net one makes a dns call on instantiation. Rapture version may be preferred.

propensive commented 7 years ago

It might be worthwhile having Contextual provide just the data representations of these things, with no interesting methods implemented, and have the actual interesting stuff (e.g. an HTTP POST method on a URL) provided as extension methods in other libraries.

nrinaudo commented 7 years ago

@ShaneDelmore is that true though? I thought it was during comparison, since two URLs are expected to be equal if they resolve to the same canonical IP / path, does it really also happen at creation time?

nrinaudo commented 7 years ago

@ShaneDelmore after looking around a bit, it does appear to only be when calling either equals or hashCode, and only in the case of URL - URI is relatively sane.

I would vote against dedicated types, at least in this context. The idea behind this issue was to provide standard "literal" syntax for common types - URL and URI are common types that people often have to interact with.

A sane URL implementation is of course always a good idea, but I don't think it's contextual's job.

propensive commented 7 years ago

Not really the point of the discussion, but I'm pretty sure two URLs whose domains resolve to the same host IP should not be considered equal, as HTTP Host parameter gets passed with the request, and allows the same server to process it entirely differently for each case...

propensive commented 7 years ago

Anyway, how about we create a separate project called, say contextual-representations (unless anyone can suggest a better name), that acts as a library of typed representations of various datatypes, constructed from interpolated strings.

I would imagine these would be simple and numerous. Anything complex, like XML, would quite quickly get moved out into its own project.

Any objections?

nrinaudo commented 7 years ago

@propensive regarding the URL issue: you're right, it shouldn't. Unfortunately, it is. See this stackovervlow answer, for example.

Regarding contextual-representations, I'm fine with the general concept, but you'll need more than just one library, right? Or at least multiple modules that can be imported independently. Take the example of java8 DateTimeFormatter - this cannot be in the "main" representations module, since it has a hard dependency on java8, which is not yet an assumption you can safely make.

rintcius commented 7 years ago

I was thinking along the same lines as @nrinaudo - maybe good if these representations were organized by the dependencies they need?

nrinaudo commented 7 years ago

@rintcius Precisely what I had in mind:

propensive commented 7 years ago

@nrinaudo I didn't even follow your SO link... I believe it, and it was too depressing! ;)

Anyway, regarding the main question: maybe, maybe not... You're definitely right that we wouldn't want to have people get, say, time dependencies on their classpath if they only wanted to parse an IP address, though you equally don't want to have to add six tiny dependencies to your build just to support six different datatypes you happen to be using. So @rintcius's suggestion (just this second seconded by @nrinaudo!) sounds reasonable.

That said, I'm not sure we need Java8 or JodaTime dependencies. If we parse a date, we can represent it by day, month and year (let's assume Gregorian only, for now), as a triple of Ints. I don't think we need an external parser to do the parsing (it certainly wouldn't be the most complex parser we would have). If someone wants to use the contextual-representations version of Date, then they write a converter to their favourite time library, and that becomes the new dependency...

Though I'm not adamant about any of this yet. Given that we're starting almost from scratch, I think we can begin by putting everything we do into a single contextual-representations library, with the view to splitting it up when it needs it, prior to a "first release" version.

propensive commented 7 years ago

There have been some suggestions already, but maybe we could start brainstorming all the different things we might want to represent?

nrinaudo commented 7 years ago

@propensive I'm wondering if we're talking about the same thing here - both are good and fit specific use cases, but my intention with this issue was not for Contextual to create new data types, but to provide literal syntax for existing ones.

Let me take the example of dates. I work with a lot of client data, which is always timestamped. I use either joda (if java < 8) or java date time (if java 8+) to deal with them, but always end up having to define custom formats - normal people don't apparently care much for ISO 8601. What I'd love to do is declare my formats in a way that would be checked at compile time:

val customFormat: DateTimeFormatter = joda"yyyy-MM-dd'T'mm:hh:ssZ"

In this context, a custom representation of date or times doesn't interest me - I already have access to a pretty good one. I just want literal syntax for types that already exist.

Having bespoke types for common concepts, and Contextual "connectors" for them, is also a good idea, but a completely different one. Maybe we should split this issue in two and make it clear which is which, since there appears to be some confusion (at least on my end)?

rintcius commented 7 years ago

W.r.t. ideas for representations, https://github.com/fthomas/refined (and https://github.com/fthomas/refined/wiki/Built-with-refined) may be a good source.

gabrieljones commented 7 years ago

Is joda still preferred over threetenbp? Incidentally all of them were written by the same guy.

nrinaudo commented 7 years ago

I had never even heard of threetenbp - thanks!

gabrieljones commented 7 years ago

https://github.com/ThreeTen/threetenbp

gabrieljones commented 7 years ago

Can we cross compile the date format interpolator so that it includes threetenbp on Java<8. The difference in package naming probably makes that non trivial

nrinaudo commented 7 years ago

@gabrieljones well I mean right now there appears to be confusion about whether we want to write our own types or just literal syntax for existing types (which is what you seem to be talking about), so this seems a bit early :)

But yeah, it might require some SBT-fu, but it certainly seems achievable.

gabrieljones commented 7 years ago

@nrinaudo Something would have to be there so we can abort the compile if the format string is invalid.

gabrieljones commented 7 years ago

https://github.com/gabrieljones/DateTimeFormatInterpolator

Here is my initial attempt.

propensive commented 7 years ago

Sorry I've not had time to contribute to this thread as much as I'd like... I'll be back at full pace once this week's over, though. :)

fommil commented 6 years ago

out-of-the-box parser for stdlib data types would be awesome! Support for Instant would be :100:

I will probably put one together, you're welcome to it, but I'm sure you're aware how trivial it is.