bglusman

bglusman

IndiffererentAccess - Adaptation of HashWithIndifferentAccess to Elixir Maps/Plug

So I’m looking for reactions and alternatives, and I know a lot of them will be negative/some of mine are too…

I just threw together this code in an hour or two after frustration with a bug in my project where one dev used a string-keyed map in some business logic where some existing code assumed it could use an atom… and we have a number of similar situations elsewhere, where we fail to do anything to the params at the Router or Controller level and they leak, unchanged, into deeper business logic modules.

Arguably that’s the problem itself, and I know a lot of you (probably correctly) will think this is a terrible idea. I think it might be a terrible idea. But I also wanted to see how hard it was to do and have a discussion here over alternatives, tradeoffs, and whether it’s fundamentally bad in any important way or just non-idiomatic, and if, as I’m sure many will think, it’s non-idiomatic, what’s the right alternative that tolerates mistakes and oversights and doesn’t have a lot of ceremony or boilerplate?

One obvious issue with this is the overhead… another is that Map’s are enumerable and if you use this on a Map you enumerate over, well, things will be a bit weird by default. What other issues come to mind? Anyone willing to say they like it/try it out in a personal project and see how they feel after using it a tiny bit/pointing to specific places it works well or badly, aside from the general concerns over being idiomatic?

I lean towards being pluralist and even if I don’t use this myself going forward, I wanted to see what it looked like when done/how hard it was, and the only comparable libraries I found were https://github.com/vic/indifferent and https://github.com/philosodad/morphix and neither seemed to do quite as simply what I had in mind… and it was easy enough I thought i should share the result and see if I learn anything, or if people surprise me and actually like it :upside_down_face:

Anyway here’s the github https://github.com/bglusman/indifferent_access and the hex package https://hex.pm/packages/indifferent_access

Most Liked

LostKobrakai

LostKobrakai

The bug is passing string keys into business logic and not that the business logic expected atom keys, but you’re trying to change the latter instead of the first problem. User input (most often string keyed maps) should only ever land at a place where it’s Ecto.Changeset.cast or otherwise converted to an internal data structure. I would even say modifying those maps (e.g. add something to it) before “casting” to be a smell.

The other place for string keyed maps is if you need keys, which are user defined. But then your whole business logic needs to work with just string keys, because you never want to convert arbitrary user provided keys to atoms.

NobbZ

NobbZ

Exactly.

When I get input from the user, its usually that I validate it and typeconvert to elixir types as necessary. Then its either used for funcalls directly as bare value, or to create some other data. For what I have done so far, this other data is about 90% some struct that existed already before I decided to get some user input.

And in case of projects using ecto, well, at least 50% of them have a schema and a changeset function which makes validation/normalization very easy and straightforward.

Of course, sometimes other data structures make sense, then use it. And of course, if there are user provided relationships, just don’t use atom keys. But either way, deeper in your codebase nothing should access a map by a literal string, as either you know upfront what you expect or you don’t. And if you use a literal string, it feels as if you knew what you should get, but forgot to normalize. (And as always, there are exceptions to this “rule”, case by case)

NobbZ

NobbZ

I’ve only skimmed the implementation, but it seems as if it builds on a one-time creation of stringified/atomized counterkey with same value.

This seems to make things even worse…

One part of code updates atom key, another reads string key.

And even if you remember to re-key… Which should win if both exist?

The only solution are proper conversion to structs. No more arguing about string vs. atoms, just structs. Part of the contract, checked by dialyzer.

Schultzer

Schultzer

It really sounds like you trying to solve the wrong problem here.

Maybe a better thing is to take a few steps back and, look at why a developer would end up in this situation, in the first place; I feel like all of this can be solve by good design, but that is sometime harder than said.

dimitarvp

dimitarvp

This is not about that. It’s about introducing a module/structure that has an implicit and thus potentially surprising behaviour.

That’s hard because none of us can point you at our own production code, it’s all company trade secrets, you know. The “abstract” piece of advice you receive, you seem to be not impressed with so you’ll have to discover your own truths along the way.


As an anecdotal evidence, I have 10+ times successfully introduced big refactorings in Elixir code bases where the PR shows, say, 1650 lines added / 1280 lines removed and I still introduced zero bugs. I am not special or particularly amazing at programming though; good test suite and coding style consistency can and do work wonders, even in dynamic languages like Elixir.

You are aiming at the wrong problem. You can rework your code base to not rely on such arcane surprising mechanics and your project will be better for it. I’d argue the energy spent on such refactoring will end up being less than the energy put into making sure your surprise mechanics work well.

It seems you are dead-set on actually going forward with this though, so maybe all of us here trying to convince you to give it up are barking up the wrong tree. :003:

Where Next?

Popular in Libraries Top

Jskalc
Hi! Today, after a couple weeks of development I’ve released v0.1 of LiveVue. It’s a seamless integration of Vue and Phoenix LiveView, i...
New
deadtrickster
I’ve just released stable versions of my Prometheus Elixir libs: Elixir client [docs]; Ecto collector [docs]; Plugs instrumenter/Export...
New
devonestes
Introducing assertions, the library that helps you write really great test assertions! GitHub: https://github.com/devonestes/assertions ...
New
riverrun
I’ve just released version 3 of Comeonin, a password hashing library. The following small changes have been made: changes to the NIF c...
New
asiniy
Hey there! I wrote a download elixir package which does exactly what its name about - an easy way to download files. I saw solutions ...
New
seancribbs
Today I released a new dialyzer Mix task as the dialyzex package! At the time we started writing this task, the existing dialyzer integra...
New
woylie
I released Doggo, a collection of unstyled Phoenix components. Features Unstyled Phoenix components. Storybook that can be added to...
New
archan937
It is a well-know topic within the Elixir community: “To mock or not to mock? :)” Every alchemist probably has his / her own opinion con...
New
tmbb
PhoenixWS - Websockets over Phoenix Channels Source code on Github here: https://github.com/tmbb/phoenix_ws Phoenix channels are a great...
New
bryanjos
Hi, I wanted share a small library we at Revelry Labs made for rendering react components from the server side. There are instructions fo...
New

Other popular topics Top

TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
273 38985 115
New
shahryarjb
Hello, I get Persian date from my client and convert it to normal calendar like this: def jalali_string_to_miladi_english_number(persi...
New
chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 30048 115
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 35421 110
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
vac
Hi, I'm quite new in Elixir and I'm trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and ...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
AstonJ
by Lance Halvorsen Elixir and Phoenix are generating tremendous excitement as an unbeatable platform for building modern web application...
460 27162 124
New

Sub Categories:

We're in Beta

About us Mission Statement