My idea comes from qualified keywords from Clojure, at least after I got in contact with that in Clojure I felt Elixir was missing something like that. So just a small explanation of what is a qualified keyword in Clojure that I got from this blog post:
Brief: the five common forms
:foo, which is just your plain old keyword
::foo, which is a namespaced keyword for the current namespace
:my.ns/name, which is a namespaced keyword for a valid namespace
::my/name, which uses the :as alias to achieve the same as form #3
:something/foo, which is commonly used with Datomic and doesnât actually map to a valid namespace
I think 4 can be ignored for now but to understand 2 and 4:
(ns some.namespace)
(def foo {::bar 1})
outside of the some.namespace::bar gonna be :some.namespace/bar.
You still can have 5, that is :something/foo that doesnât actually relate to any existing namespace.
The idea is that we can have some context aware keyword/atom. Iâm not suggesting binding atoms to module namespaces or something like that, but I guess we can initially do something like 5 at least for now.
Now going effectively to Elixir stuff.
We can have @ inside of an atom that doesnât break the atom syntax, either in Elixir or Erlang. We could use that to standardize in the community an approach for namespaced atoms, something like :foo@something. The idea is that we can read that as âfoo at somethingâ.
Why use that instead of just old snake case? Exactly because that a simple snake case doesnât communicate any intetion of namespacing or context related stuff to an atom.
Just as an example, letâs say want I to store api tokens for facebook and twitter I could use %{twitter_api_token: twitter_token, facebook_api_token: fb_token} I could have something like %{api_token@twitter: twitter_token, api_token@facebook: fb_token}. I think this even help avoid unneccesarry nesting of structures.
Elixir modules are namespaced atoms. Theyâre :"Elixir.Module.Nameâ aka Module.Name in short. Though the syntax sugar included in elixir doesnât really make that reusable.
I donât think use of bare maps is a problem, if their contents are still well defined/understood. E.g. optional keys cannot be modeled with structs, which can be limiting depending on the use case.
Yep, Iâve thought of that too, the biggest issue I see with that is that there is no indication of an end of the namespace⊠so in the token example Iâd had to do MyApp.Twitter.Token and MyApp.Facebook.Token there is no indication of where the namespacing ends where there is an identifier for the key.
I strongly disagree with that. As @LostKobrakai said, optional keys are not possible with structs, which usually makes you end up with a lot of different structs or bloated structs with a bunch of optional keys, both end up being way more harmful to codebases in the long term, at least in my experience.
Besides that, structs impose a strongly defined shape for the data, the idea of namespaced atoms is to give contextual meaning to the data without defining a strong shape to it. This allows you to use the data across several different use cases without having to deal with enforced shapes to the data.
I thought of that too⊠I think the @ example has some sort of the same meaning without disrupting the usage of the approach. Just in your example, you were forced to use the => rocket notation to define the map, by using the @ you can still have the shortened syntax for just atoms.
The only issue I see is that it may break syntax highlight tools. Other point that it may lack is filtering data, with the tuple approach itâs way easier with the current tools.
The idea is to give contextual meaning to the keys. Have you ever dealt with rdf, datomic or even rss/atom feeds? the idea is that you avoid nesting structures building context on namespaces.
Just an example, if you look at the Elixir Outlaws rss feed you gonna see a bunch of nodes that start with the itunes namespace, those are nodes that donât have any meaning to rss/atom feeds but are used by a platform to build information from that data, for example:
<itunes:type>episodic</itunes:type>
<itunes:subtitle>The hallway track of the Elixir community</itunes:subtitle>
<itunes:author>Elixir Outlaws</itunes:author>
<itunes:summary>Elixir Outlaws is an informal discussion about interesting things happening in Elixir. Our goal is to capture the spirit of a conference hallway discussion in a podcast. </itunes:summary>
<itunes:image href="https://assets.fireside.fm/file/fireside-images/podcasts/images/4/4b3f9fe7-b118-4f29-b6ee-7baf2571b03c/cover.jpg?v=4"/>
<itunes:explicit>no</itunes:explicit>
<itunes:keywords>elixir, erlang, BEAM</itunes:keywords>
This allows podcasts to integrate their information with iTunes directly from their feed without having to compromise their data related to the format(rss/atom) with the information that they want in the platform.
This is just an example of where namespaced keys can be useful, in Datomic for example this allows you to aggregate information whatever the way you want without losing context for that information. One of the problems of nesting structures for context is that once you go inside the nesting part it loses all context that you built.
At that point you might be much better off served by tagged tuples â potentially on several levels, not just one, e.g. {:api_token, :twitter, "abcdef} â than doing dances with your own naming convention of module-like atom names. Because theyâre just that: conventions. You have make all your code abide to them, whereas with complex tuples you can utilize pattern matching and eliminate bugs by the mere virtue of missing function head definitions [for invalid state].
Added bonus is that thereâs already a fantastic library for that: Domo.