Pronouncing `<-`

Learn You Some Erlang: List Comprehensions uses “in” - and it does set up the association between ∈ and <- though it isn’t explicit about. Personally I’ve always pronounced ∈ as “element of”.

Designing for Scalability with Erlang/OTP p.27

The effect of this is to successively bind the variable X to the values 2, 3, 5, 7, and 11. In other words, it generates the elements from the list: the symbol <- is meant to suggest the “element of” symbol for sets, ∈.

Erlang Programming - A Concurrent Approach to Software Development p.198

Generators
A generator has the form Pattern <- List, where Pattern is a pattern that is matched with elements from the List expression. You can read the symbol <- as “comes from”; it’s also like the mathematical symbol ∈, meaning “is an element of”.

3 Likes

I use in or into. That does also works for with use of it.

“from” sounds like a great idea!

1 Like

“Comes from” makes sense… though it makes me think of the COMEFROM command (https://en.wikipedia.org/wiki/COMEFROM). :wink:

“in” makes some sense to me (especially as I’ve done Python before), but IMHO “into” seems to make the information flow backwards – it’s coming out of the “supply” (or whatever you call the thing on the right side). Also it could be conflated with the into keyword one can use (when putting the results into: a string or map or whatever).

As for with, I’m not very familiar with it (still scratching the surface of Elixir here, and as it’s a liquid it keep filling up the scratch ;-)), but I’ve just finally read up on that a bit. (Saw https://elixirschool.com/lessons/basics/control-structures/, as pointed to by https://www.dailydrip.com/topics/elixir/drips/elixir-weekly-drip-2-and-exercise-reverse-polish-notation-calculator.) This time it seems to me like “from” and “out of” would be good, or “coming from”, and “in” might do, but again “into” seems exactly backwards to me. Using their example, I’d pronounce:

with {:ok, first} <- Map.fetch(user, :first),
{:ok, last} <- Map.fetch(user, :last),
do: last <> ", " <> first

as “with ok first from Map.fetch user first, [and] ok last from Map.fetch user last, do last append comma append first”. By contrast, methinks someone hearing it would have a harder time grokking “with ok first into Map.fetch user first, [and] ok last into Map.fetch user last, do last append comma append first”.

Perhaps you don’t mean what I think you mean, or maybe I’m not yet understanding with?

I’m for the pragmatic and unambigous pronounciation: “left-arrow” or “lesser-than and minus”. This version of pronounciation can’t be confused with others and also one doesn’t need to know about the surrounding context.

1 Like

According the Urbit Hoon glyph and character specification, <- could be pronounced as galhep. :sweat_smile:

3 Likes

in is probably closer to what i mean. into come from my mathematical thinking, which means to me that the result on the left is a subset of what is on the right. But it may just be a bad translation on how it works in French Maths Jargon.

1 Like

Hehe, I look Hoon, even written a couple simple things in it. ^.^;

I think the main issue with <- is it is used in different ways in two different contexts, as an ‘for each element’ in for and a ‘match’ in with, which I find odd since = is already match. That is the major oddity source for me. ^.^;

Haskell uses “draw” or “draws” to reference <- in list comprehensions, which are a very similar syntactic construct to comprehensions in Elixir. I find myself using “from” sometimes as well, particularly when speaking about with expressions.

It might be useful to rule out language that references other operators, keywords, or common arguments. in, into, elem, match would be easy to confuse

I’ve just discovered a context, which may be somewhat common, where pronouncing it as “from” may be somewhat of a clash. I’ve just started reading the Elixir School page on OTP Concurrency (https://elixirschool.com/lessons/advanced/otp-concurrency/) and am wondering, is it a common idiom (or just theirs) for a GenServer or similar thing, to refer to the sender of a request as from?

Yeah, it’s common.

It’s a typespec (GenServer.from/1) for a parameter of the callback GenServer.handel_call/3.

Ironically it’s supposed to be opaque - Designing for Scalability with Erlang/OTP p. 89

Always use From as an opaque data type; don’t assume it is a tuple, as its representation might change in future releases.

I don’t think that’s true. It’s documented as {pid(), Tag} in erlang docs - there’s even this fragment:

From is a tuple {Pid,Tag}, where Pid is the pid of the client and Tag is a unique tag.

While I agree that any “opaque”-ness should be called out in the documentation, I suspect that Cesarini/Vinoski are after a different sort of “off-limits”-ness:

  • the From type is produced by OTP expressly for the purpose of consumption by OTP (e.g. gen_server:reply/2). So:
  • Don’t go off making your own {pid(),Tag} tuples and feed them to OTP. Just because you may be able to get that approach to work now - future OTP releases could break the code (e.g. some logic that previously ignored the tag may start making decisions based on it).
  • Don’t rely on any information inside the From type - e.g. if the processing logic needs a pid to reply to, do not get it from the From argument but include it explicitly in the request message for use by the processing logic.
  • Any instance of a From type only has a very limited window of usefulness because of the tag acting as a correlation identifier. So once the call request has been replied to, the From instance should be discarded.

These two contexts could be considered similar by noting that both are in fact monadic constructs. Although Elixir does not (yet… :smiling_imp:) expose general monadic coding possibilities to the users (there are some libraries that do though), I think this issm where the <--syntax comes from.

Hmm, that is true, though the language does not explicitly use monads anywhere it does use them very implicitly…

In case of comprehension it’s like “in” eg. for item <- [1, 2, 3] is basically for item in [1, 2, 3], however in case of with this arrow is more like from, eg. with {:ok, user} <- Repo.insert(changeset) you can read as with {:ok, user} from Repo.insert(changeset. Context of the arrow changes its meaning.

In general - then - from seems to be best fit for me. In every case we take an element or a result from a operation or collection.

Eeeeeek! Monads! Run away! Run away! :wink:

Someone explained them to me last year. I think I understood them for a few minutes…

1 Like

How about this explanation:

You know Elixir’s pipe |> operator I presume, it is basically defined like this (but as a larger macro/specialForm/etc…):

def value |> fun, do: fun(value) # Except as a macro, but conceptually it is this simple

Well a monad ‘bind’ operator (in Haskell the >>= operator by default) is basically just this when elixirized:

# An :ok/:error tuple monad
def {:ok, value} >>= fun, do: fun(value)
def {:error, _value}=err >>= _fun, do: err
# And whatever else you want, any kind of container (or in fact no container) are monads
# A map is a monad, a user struct is a monad, a list is a monad, etc...

Technically the Elixir normal pipe of |> is a monad ‘bind’ as well, but just for the simple unwrapped value (hence why it is strictly less useful than a monad pipe while a monad pipe can still do what the elixir pipe can do). :slight_smile:

Examples

Let’s demonstrate with some examples:

Normal piping (naked integer):

def add(a, b), do: a+b

2
|> add(2) # returns 4
|> add(3) # returns 7

Monad piping (let’s use, oh, the error/success tuple):

def good_add(a, b), do: {:ok, a+b}
def bad_add(a, b), do: {:error, "I borked!"}

good_add(2, 2) # returns {:ok, 4}
>>= good_add(3) # returns {:ok, 7}
>>= bad_add(4) # returns {:error, "I borked!"}
>>= good_add(5) # returns {:error, "I borked!"}

Basically the only difference between the Elixir pipe |> operator and a monad bind >>= operator (traditionally >>> or ~> in Elixir libraries due to syntax restrictions in Elixir) is that the monad bind ‘unwraps’ the value from it’s container before passing it to the function (then of course the function is ‘should’ build a new container for it and return it, but not really necessary in all cases, like for converting from one form to another, like there is a function traditionally called ‘return’ that just unwraps the value and returns it with no container).

Any more clear I hope? :slight_smile:

If you are curious, there are a whole host of libraries for Elixir that adds monads (and optionally many can also ‘enhance’ the traditional Elixir pipe to work as a monad bind, so they container to work as normal but can also work with, say, ok/error tuples and so forth). My favorite is @expede’s witchcraft, which is a full-fledge monad handling library with a lot more (it contains about everything you need, designed to work with the default monad ‘definitions’ in her other libraries like algae, and built on the very low level library also made by expede of quark while the new version is also built on the amazing library of type_class, also made by expede, that adds typeclasses to Elixir in a fascinating and succinct way). @expede also made the exceptional library, which is basically ‘just’ a monad handler ‘just’ for success/error typing (the number one fault that normal elixir piping does not handle), and it handles ok/error tuples, exceptions, and more, it let’s you turn stuff like:

try do
  2
  |> i_might_throw!()
  |> i_return_ok_or_error_tuple()
  |> case do
    {:error, _err}=error -> error
    {:ok, value} ->
      value
      |> do_something()
      |> i_also_return_ok_or_error_tuple()
      |> case do
        {:error, _err}=error -> error
        {:ok, value} ->
          value
          |> more_work_that_can_only_be()
          |> done_after_confirming_the_tuple()
          |> was_a_success_and_unwrapping_it()
      end
  end
rescue
  exc -> exc
end

Well, that is a bit of hell, but entirely expected code considering normal elixir and libraries and erlang libraries and all, it is a mess and the pipe operator is not up to the task. At this point for readability (and sanity) you’d break it up into intermediate variables or more function, which just makes the code huge. With exceptional it could instead just be:

2
|> safe(&i_might_throw!(&1)).()
~> i_return_ok_or_error_tuple()
~> do_something()
~> i_also_return_ok_or_error_tuple()
~> more_work_that_can_only_be()
~> done_after_confirming_the_tuple()
~> was_a_success_and_unwrapping_it()

Oh look, a normal piping chain but using ~> instead of |> where you want to unwrap the value or pass the error, and this is just the default mode to use it, you can have it enhance the pipe operator so it just becomes:

2
|> safe(&i_might_throw!(&1)).()
|> i_return_ok_or_error_tuple()
|> do_something()
|> i_also_return_ok_or_error_tuple()
|> more_work_that_can_only_be()
|> done_after_confirming_the_tuple()
|> was_a_success_and_unwrapping_it()

Though this is not the default mode or even recommended mode except in very tightly controlled areas as it does change the expectation of how the normal pipe operator works (I wish it worked like this, how often do you want to pass an ok/error tuple to a function instead of just it’s success value). And no, those last three it does not matter if they return raw values, ok/error tuples, return exceptions (though if they raise you need to wrap it with a ‘safe’), it all ‘just works’.

It also has things to setup alternate paths for error piping and such too as well as for raising an error condition as an exception or normalizing it to an ok/error tuple or as a naked value or exception. It is a really nice and useful library that makes piping into monad handling for errors, and by doing so it makes handling ok/error conditions so much easier and I wish something like it was built in to Elixir so people stopped doing error handling so crazy (with the exceptional library the ! functions become entirely useless as one example, I hate hate hate duplicate functions that only differ in how they handle error like that!).

4 Likes