Proposal: Add ~URI sigil

160 views
Skip to first unread message

Wojtek Mach

unread,
May 16, 2023, 4:38:01 AM5/16/23
to elixir-lang-core
Hi, I'd like to propose adding ~URI for constructing URI structs. Here's an example:

    iex> ~URI"https://elixir-lang.org"
    %URI{
      scheme: "https",
      authority: "elixir-lang.org",
      userinfo: nil,
      host: "elixir-lang.org",
      port: 443,
      path: nil,
      query: nil,
      fragment: nil
    }


I believe the advantage is we can make this a macro and thus parse the URI string at compile-time
so catch bugs because of incorrect format early (though these are pretty uncommon) and also be a
little bit more efficient at runtime.

If added, I'm not sure whether ~URI should use URI.parse or URI.new! under the hood so an advice
on that would be appreciated.

This is a separate but related discussion so while at it, I'd like to propose adding Inspect
implementation for URI that would return the sigil:

    iex> ~URI"https://elixir-lang.org"
    ~URI"https://elixir-lang.org"


I think more compact representation helps e.g. here. Before:

    iex> Req.new(url: "https://elixir-lang.org")
    %Req.Request{
      method: :get,
      url: %URI{
        scheme: "https",
        authority: "elixir-lang.org",
        userinfo: nil,
        host: "elixir-lang.org",
        port: 443,
        path: nil,
        query: nil,
        fragment: nil
      },
      headers: [],
      body: nil,
      options: %{},
      ...
    }


After:

    iex> Req.new(url: "https://elixir-lang.org")
    %Req.Request{
      method: :get,
      url: ~URI"https://elixir-lang.org",
      headers: [],
      body: nil,
      options: %{},
      ...
    }


On the other hand, seeing the internal representation right away is sometimes useful given the URI
format is somewhat subtle.

Before:

    iex> URI.parse("/foo")
    %URI{
      scheme: nil,
      userinfo: nil,
      host: nil,
      port: nil,
      path: "/foo",
      query: nil,
      fragment: nil
    }
    iex> URI.parse("//foo")
    %URI{
      scheme: nil,
      authority: "foo",
      userinfo: nil,
      host: "foo",
      port: nil,
      path: nil,
      query: nil,
      fragment: nil
    }


After:

    iex> URI.parse("/foo")
    ~URI"/foo"
    iex> URI.parse("//foo")
    ~URI"//foo"


I think this downside can be alleviated by adding `IEx.Info` implementation along these lines:

    iex> i URI.parse("/foo")
    Term
      ~URI"/foo"
    Data type
      URI
    Raw representation
      %URI{
        scheme: nil,
        userinfo: nil,
        host: nil,
        port: nil,
        path: "/foo",
        query: nil,
        fragment: nil
      }

Wojtek Mach

unread,
May 16, 2023, 4:45:42 AM5/16/23
to elixir-l...@googlegroups.com
I just realised another small benefit of showing the full URL in the sigil. Here’s a snippet from the proposal as rendered by my e-mail client:


The URL was automatically made clickable. Tools like Livebook could potentially do the same.

--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/9cc29c5e-ca64-42b3-83f8-84c60985efedn%40googlegroups.com.

Kip Cole

unread,
May 16, 2023, 7:51:21 PM5/16/23
to elixir-l...@googlegroups.com
I think this is a good proposal, as is the ~UTC one.  Both make the intent much clearer.

On 16 May 2023, at 6:45 pm, Wojtek Mach <woj...@wojtekmach.pl> wrote:

I just realised another small benefit of showing the full URL in the sigil. Here’s a snippet from the proposal as rendered by my e-mail client:

<PastedGraphic-1.png>

Christopher Keele

unread,
May 18, 2023, 1:37:37 AM5/18/23
to elixir-lang-core
I dislike +1 posts, but I feel very strongly about this—it'd be amazing to have.

I want booting an application to fail as fast and obviously as possible if someone gives it a bad phoenix host url, ecto database url, rabbitMQ url, redis url, mongodb url, etc. Preferably, before the connection is attempted; which, depending on the library in question consuming the url, often fails with a confusing network-failure-related error message.

Ben Wilson

unread,
May 18, 2023, 10:25:35 AM5/18/23
to elixir-lang-core
Question: Would this sigil support interpolation? Is that too clever?

~URI"https://#{path}?#{query_params}"

- Ben

José Valim

unread,
May 18, 2023, 10:33:10 AM5/18/23
to elixir-l...@googlegroups.com
Uppercase sigils do not supported interpolation, so that's a no-go.

Note you can also get the compile time behaviour today by doing @uri URI.new!("...").

Zach Allaun

unread,
Jun 1, 2024, 10:42:43 AMJun 1
to elixir-lang-core
I found this proposal while reading Wojtek's proposal for URI inspect format and wanted to resurrect it since it doesn't seem like anything had been explicitly decided on.

In my opinion, this proposal is in line with sigils D, N, U, T: they are not strictly necessary, but allow ergonomic creation of a built-in data type with compile-time validation as well as a recognizable inspection format.

One concern I initially had was that this might be a slippery slope that could lead to many additional sigils, but it looks like URI and Version may be the only remaining built-ins that don't have a "data literal" format (either through an explicit literal or a sigil).

José Valim

unread,
Jun 1, 2024, 11:07:55 AMJun 1
to elixir-l...@googlegroups.com
I don't think it is worth it. The main benefit listed here is building/parsing the URI at compile-time, but we have recently added a compiler pass that can inline constructs, so we would be able to get the benefit listed here for free, without introducing new language concepts.

Both this proposal and the inspect one are about hiding basic language features:

1. using sigils instead of functions for building data
2. inspecting structs using something special instead of %URI{...}

But I think it is important to have examples in the language of using basic data structures as is. You don't need to put everything behind a sigil. You don't need to customize the inspection of everything.

Perhaps there is an argument that we should not even have done those for calendar types. On the other hand, when it comes to inspection, everyone knows what are the inner parts of a Date or a Time, way fewer developers know by heart all the parts that make a URL.

Reply all
Reply to author
Forward
0 new messages