3

I looked at some Elixir projects, and saw code like that:

value = Dict.get(options, :key)

Is there a way to make it shorter and leverage the dispatching/protocols? It seems like there are some statements like import, use, require in Elixir.

So it seem like it should be possible to write short code and let the compiler to figure out should it use Dict.get or String.get:

import Dict, String

# Getting :key from the Dict.
value = get options, :key

# Getting the second char from the String
char = get "some-string", 2

Does such approach works in Elixir? I.e. is it possible to write short and compact code instead of long fully prefixed names like A.B.C.do_something?

2
  • What you want is type-inference. Even in languages that support that (ML family languages mainly) type annotations are sometimes necessary. What I mean to say is even in a type-inferred language you may have to sometimes do something analogous to Dict.get or String.get. Also, while shorter code is a good goal, other developers being able to read your code is also an important goal. Forcing disambiguation is not entirely a bad thing from that point of view. Commented Nov 6, 2015 at 13:17
  • This is not always a good idea, it makes your code shorter, but when you come back to it 6 months later it makes it that much harder to figure out, because you run into the case of where the ???? was that function defined Commented Jun 17, 2020 at 16:41

2 Answers 2

4

You can definately write short,compact code using alias. Just make sure you dont confuse yourself. Check the offical documentation

iex(1)> alias Enum, as: E
nil
iex(2)> E.reduce [1,2,3,4], &(&1+&2)
10

As for the first part of your question. When you import modules, conflicts will show ambiguous error.For example

iex(1)> import Map, only: [delete: 2]
iex(5)> delete %{a: 4,b: 5}, :a
iex(6)> import List, only: [delete: 2]
iex(8)> delete %{a: 4,b: 5}, :a       
** (CompileError) iex:8: function delete/2 imported from both List and Map, call is ambiguous
    (elixir) src/elixir_dispatch.erl:111: :elixir_dispatch.expand_import/6
    (elixir) src/elixir_dispatch.erl:82: :elixir_dispatch.dispatch_import/5

So make sure you import only useful functions from a module.using the only keyword. Another good option would be to take advantage of lexical scoping in import. Where you can specify where you want to use the imports and only that part will be effected. Here is an example

defmodule Math do
  def some_function do
    import List, only: [duplicate: 2]
    duplicate(:ok, 10)
  end

  def other_function do
    duplicate(:ok, 10)#this will show error since import is only present inside some_function
  end
end

Alternatively protocol could be thing you are looking for.The docs will tell you what you need to know, i'l put up a short summary here.

defprotocol Get do
  @doc "Returns the data,for given key"
  def get(data,key)
end

You can then implement it for whatever type you require

defimpl Get, for: Map do
  def get(data,key), do: Map.get(data,key)
end

defimpl Get, for: Keyword do
  def get(data,key), do: Keyword.get(data,key)
end

defimpl Blank, for: Any do
  def blank?(data,key), do: raise(ArgumentError, message: "Give proper type for key")
end
Sign up to request clarification or add additional context in comments.

4 Comments

Sad, I was afraid it works that way. The alias doesn't solve the problem, I want to use Polymorphism (or the Method Dispatching / Protocols as it's called in FP). The important thing is that I would like to be free from remembering if I should get the "get" method from Enum (or E) or from Dict (or D).
i am kinda sure that wont be possible in elixir since its a dynamic type language so all types in Elixir are inferred by at the runtime. But there is stuff called protocols and it can work you. elixir-lang.org/getting-started/protocols.html
Thanks, I'll check it out. Although it seems strange that people don't use Protocols..., I've checked couple of repositories and it seems like all of them are using A.B.C.some_function expressions.
A.B.C.some_function is nesting of modules. elixir-lang.org/getting-started/…
2

You could also use a combination of import :only with import :except to get the behavior you're looking for. Check here for more details.


EDIT:

Another possible approach occurs to me. You could also create a shorter name via an anonymous function. Something like this:

dget = &(Dict.get/2)

sget = &(String.get/2)

Then your sample code would look like this:

value = dget.(options, :key)

char = sget.("some-string", 2)

While this would, of course, work I think it's probably still not what you're looking for. I add this to my answer solely for others who may run across this Q & A to help to give a more complete answer about possible alternatives.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.