Simple Elixir web page - use Phoenix or not?

Guard clauses are introduced with case.
But they work the same way with function heads (which is why I consider them part of the pattern in those scenarios).

I like this succinct quote

Pattern matching is a conditional construct and destructuring isn’t.

I’d consider if and unless “covenience”/“familiarity” macros. case is driven by the power of pattern matching, while cond is about “truthiness” - if there are alternatives then there must be at least two separate expressions (i.e. not statements).

1 Like

How does the above code translate to English? It does something when the value v parses as an integer what does the

    {i, ""} -> i
    _ -> v

sequence actually mean? What does v get assigned to when it parses as an integer?

Have a look at Integer.parse/2. It returns a tuple and that is what case is matching on.

{i,""} -> i
So if v happens to be a string that only contains an integer then the result from Integer.parse/2 will match {i,""} - i being the integer that was parsed - the value that this clause evaluates to.

_ -> v
this clause is the “otherwise” or “default” clause - it simply evaluates to the value v that Integer.parse/2 was trying to parse but turned out to be something else other than an “integer string”.

You have to realize that @OvermindDL1 worked in Erlang before Elixir - in Erlang you live and breathe pattern matching, so he will justifiably leverage pattern matching in Elixir to the full extent possible. Coming from an imperative background this may look a bit strange at first but more often than not, it is a very concise and declarative way of tackling (data) problems.

2 Likes

Basically:
For each param in the params, take the key and value, parse out the value as an integer, if the parsing is successful {i, ""} then return it, else return the original string v, then return the modified tuple {k, v} to a keyword list. :slight_smile:

All part of how case’s work, I’m just matching if it fully successfully parsed or if it failed to parse as an integer. :slight_smile:

I’ve even built a template-driven pattern matching framework in C++ before, I love pattern matching, succinct and powerful! :smiley:

1 Like
params =
  params
  |> Enum.map(fn {k, v} ->
    v =
      case Integer.parse(v) do
        {i, ""} -> i
        _ -> v
      end
    {k, v}
  end)

When it comes to the params at the top of the loop can it be replaced by another variable, e.g. params2, or any other variable? I assume that the immutable nature of Elixir means that although params is used at the top, they are different values as the outer cannot be mutated by the inner one.

Yep, params2 is fine, bindings (they are not variables) cannot be changed once set, what it is doing is binding a new name of params that is shadowing the old one.

1 Like

Which begs the question - what is a binding, is it the Elixir/functional equivalent of a variable (which it actually isn’t)?

What does this statement mean what it is doing is binding a new name of params that is shadowing the old one?

Does this mean that if the params at the top is changed to params2, summed would have to be computed with params2?

So reading it right the outer params gets assigned to the result of the inner one after the pipeline completes, and I assume the Enum.map function gets automatically bound to the inner params to iterate over its values.

With the result of the map as {k v} does it change the values of the inner params in situ which then gets “assigned” to the outer one, or does the {k v} get added to the outer params?

What is the relationship between the first pipe symbol and the variable that precedes it? Does it receive the value of the last function of the pipeline or is it simply the input of the function to the right of the pipeline symbol?

In the Pascel/C/C++/etc… world a variable is a set of memory, it may or may not be changeable, but it is it a bit of memory able to be referenced and sized and all. Optimization passes in the compile may elide them sometimes, but for all intents and purposes you know it is a bit of memory, usually on the stack.

In the (more pure) functional world, like (mostly) on the EVM/BEAM, variables do not exist, instead they have bindings. If you set a binding to something like blah = 42, you do not have a bit of memory that blah is that contains 42, rather there is just ‘42’ (in this case in the constants table), and if you do blorp = blah then same thing, blah does not exist either, there is still only a single 42. If you do something like vweep = blorp * 2 then vweep is basically just 42 * 2, you may not even consider it already calculated since it might not yet be, it is just a binding to ‘something’, however if you pass it to a function, like zwhoo(vweep) then the 42 * 2 is calculated to 84 on the stack and passed to the function. If it is passed multiple times to multiple areas, like after the above call you then call twoob(vweep, vweep), the response of 84 is not recalculated but just held on the stack and that same bit is passed to the function, only calculated once. (Internally the BEAM does greedy calculate things, but not all functional languages do, like haskell does not). What this means is extra memory is not used, not even pointers, so if you destructure a map like %{vwoop: v} = %{vwoop: 42} then the 42 in-place is used everywhere as needed, it is not necessarily copied into new memory, literally used in-place (internally of course optimizations may change this). But you can see how this style require immutability, but allows for a lot of benefits.

I’m using shadowing in the normal Pascal/C sense, the old binding still ‘exists’, but any later uses of the new name reference the new binding.

Indeed yep.

Not assigned, rather it just gets bound to the operation, hence why it is called a binding, not an assignment as there may be no memory actually being allocated anywhere, even on the stack, for this. Unsure what you mean about the Enum.map getting automatically bound, the only automatic binding really is function arguments in a function head…

Nothing changes, ever, remember immutability, once something is set it never changes. It returns a new one (internally optimized of course).

In all cases a blah |> vwoop(dreep, bloop) always becomes vwoop(blah, dreep, bloop). The pipe just takes the thing before it, no matter what it is, and just puts it in the first argument position of the function, it is a very simple transformation macro. In essence this:

42
|> blah(16)
|> blorp()
|> vreep("bloop")

Is this:

vreep(blorp(blah(42, 16)), "bloop")

Just much more readable. The pipe operator is popular in a huge amount of languages, mostly functional languages but it has been coming in mainstream languages for a while now too.

1 Like

Would it be right to say that functional programming is like an algebra exercise where you don’t have to compute the result but simply derive the formula that would compute the result, but only give the result in numbers if the exercise asks for it?

That would mean that lazy/delayed evaluation is what might considered to be the normal thing and that Erlang or Elixir’s immediate evaluation when a function is applied is simply a means of speeding things up?

That is quite accurate actually, yes. :slight_smile:

Precisely that. Haskell, in comparison, is entirely lazy/delayed, but it has a lot of optimization passes (which makes for a very slow compiler, like C++ kind of slow) to work around it, but it makes for a fairly blazing fast language if done well. Although OCaml is like Haskell except immediate (if you want lazy you have to decorate it yourself, which I prefer), and it compiles massive projects even faster than Elixir compiles Elixir (much faster actually), yet is fully typed and makes code within a single order of magnitude of C/C++ speed.

If you want to learn functional programming ‘as it is meant to be’, I recommend Haskell. For getting non-EVM/BEAM oriented work done, I prefer OCaml. But the EVM/BEAM and what it excels at is unmatched by anything else, especially web work. :slight_smile:

1 Like

There was recently a whole topic around the bind thing.

The other thing about functional programming is thinking of code as expressions rather than statements.

If you look carefully at the unless example you’ll notice that the shell actually shows the evaluated value as nil. So while unless looks like a statement, it’s just a macro that either evaluates to the result of the supplied body or (implicitly) to nil, i.e. there always is an invisible alternative because an expression has to evaluate to something - even if that something happens to be nil.

3 Likes

http://hotpyn.com/2018/02/04/php-elixir/

Hello, I wrote a blog post showing how to build a simple website using Plug. Github demo is available below.