With do(:)?

I feel I should be able to write something like:

def xoxo(), do: with ..., ..., do ... ...
rather than

def xoxo() do

it’s a small difference. But ‘with do’ feels so very atomic, so it feels still within the realm of expressions, even though often impure.

Anyone have 2c to add?

Just curious.

Examples:

  #Works
  def one() do
    with  a <- 1
    do
          a
    end
  end
  #Doesn't work
  def one_(), do:
    with  a <- 1
    do
          a
    end
  #Works
  def two() do
    with  a <- 1,
          b <- 1
    do
          a + b
    end
  end
  #Doesn't work
  #-> undefined function def/3
  def two_(), do:
    with  a <- 1,
          b <- 1
    do
      a + b
    end

Could you please go into a little bit more detail what you are asking about?

You can use do-short-syntax with with the same way you could with def and everywhere else where you can use do.

Oops, huh, really? lemme double check… I could have sworn… (altho it was some time ago…= )

do ... end and do: are interchangable. This is done by the compiler, and both result in the same AST.

OK, I added a quick clarification. Thanks for the quick reply previously - really appreciate it.

This does probably not do what you expect it to do… This defines one_/0 to return 1 and some with which is dumped into the module and optimized away during compilation.

Here precedence rules are tricking you. This is basically parsed as (warning pseudo-ast!) def two_(), {:do, with a <- 1}, b <- 1 do a + b end, you will need to insert some parentheses, but I’m not able to say you how exactly. As soon as I have something that is more complicated than returning a constant value or a slightly modified parameter, I use full syntax with do ... end.

2 Likes

Aha, OK, this works:

  def one_(), do:
    (with  a <- 1
    do
          a
    end)

  def two_(), do:
    (with  a <- 1,
           b <- 1
    do
          a + b
    end)

Seems rather quirky, but OK. Thanks again.

As I said, in this cases you really should prefer to use do ... end, it will make the code much more readable. Also do ... end is recognized by most IDEs as markers for code-folding/hiding wheras parenthesis are not.

Code-folding/hiding is actually the mainreason why I do avoid short-form-do for anything that is more than a single function call in the body (rule of a thumb). As soon as I start to pipe, or to nest function calls I used do ... end, since it will end up on multiple lines anyway.

Another magical barrier I try not to breach is a line-width of 120 characters.

1 Like

Yeh all good points

  • I suppose I prefer the short form syntax in order to enforce fewer imperative statements and more expression-only functions…

But as with anything (and as you point out) perhaps this is an exceptional case and better not - ‘with do’ is perhaps more on the imperative statement side of things in any case…

I would say a lot of features implemented with macros tend to feel more on the imperative side.

That being said the with is a special form which to me feels more like a “code organization device” (a stripped down implementation of Either) rather than a “language feature”. Also none of your examples deal with the else explicitly (just because you don’t see it doesn’t mean it’s not there).

The “code organization nature” seems to be even more pronounced when it’s used with action_callback in a Phoenix controller.

1 Like

As has been talked about elsewhere, do is not imperative, it is merely sequential. Elixir does not support imperative programming. There’s plenty of precedent for this, haskell also uses do notation to notate sequential operations.

2 Likes

What’s the diff between imperative and sequential operations?

Perhaps I am mixed up.

Tbh, I would say Haskell’s ‘do’ is also imperative. Haskell is usually lazy and you need a special case ‘do’ syntax do do things eagerly / sequentially…

I am open to being educated here : )

Jose articulated this very well here: Why is the word do used to start blocks?

2 Likes

V good.

Elixir doesn’t do single assignment (sometimes makes things look like things are mutable which I am not a fan of, prefer Erlang) and you can sprinkle functions with side-effects.

So, in the strictest sense, I think he’s wrong. But in every day use and most of all - the spirit of the language - he is of course totally correct (I am a big fan of this language).

Upshot, I’ll still bias towards using ‘do:’ style functions where it makes sense, trying to reduce complexity and sequential code.

Thanks for the link - good food for thought.

1 Like

Which proposition is wrong? Nobody is arguing that do end excludes side effects. Rather we’re merely arguing that what do end means is sequential code. That code may have side effects, or it may be pure.

This is in contrast to say a C style for loop which by its very nature implies mutation. C style for loops are a classic example of imperative programming. do end has no such intrinsic characteristics.

2 Likes

This is actually an Erlang (practicality) thing - in a sense Elixir’s do..end simply replaces Erlang’s punctuation:

A clause body consists of a sequence of expressions separated by comma (,):

1 Like

Imperative programming is often associated to mutation and side-effects. You need to tell the computer where to store that value or to change its state.

i) Lack of single assignment, means sometimes when i look at Elixir code it looks imperative. I know behind the scene it is not, but I just don’t like looking at it.

ii) Side effects are a necessity in Elixir

I suppose it depends on what you’re used to. When you used to purer languages you think Elixir can sometimes look imperative. When you are used to Ruby you think it looks less imperative.

All in all, I like the balance struck by Elixir (altho hanker for single assignment ; )

Not the only one, it feels ‘wrong’ coming from Erlang. ^.^;

Exactly, the punctuation is a series, a single expression, a block does not feel like a single expression. That is one of the reasons I wish erlang had a scoped let, sure it does the same as , but it is more explicit about initialization and usage of a binding, easier to search and grok for. Something in Erlang like a=21,a*2 is just parsed like a normal operator, it is a single expression parsed like ((a=21),(a*2)) where the , operator just evaluates the left, uses the new output environment as the input environment when it then evaluates the right, returning the result of the right environment, it is clear and obvious. :slight_smile:

I think exactly this. It seems you and I both came from stronger languages than languages like Ruby (I came from over 10 years of erlang and a whole-lot of C++ for over 2 decades prior, of which I even tried to write functionally in it as well). To be honest, it is Mix and the Macro’s that keep me using Elixir, without those it does not seem to gain anything over Erlang (or lfe, which also has strong macro’s, even stronger perhaps than Elixir, however it does not have the ecosystem supporting it). ^.^;

I really like Erlang to be honest, even its little droppings of , and ; all over the place, they make sense to me. ^.^

3 Likes

:grinning:

Compare the code in Elixir in Action and Programming Elixir - I think you’ll be able to tell who’s coming from a Ruby background and who’s coming from an Erlang background (and which one feels “more functional”).

4 Likes

No love for protocols? Strings? Structs? :slight_smile: For some reason I rate protocols > macros.

1 Like