Hacker News new | past | comments | ask | show | jobs | submit login
Phoenix 1.3 is pure love for API development (swanros.com)
181 points by _ugfj on March 3, 2017 | hide | past | favorite | 53 comments



<3<3<3 for Elixir / Erlang / Phoenix

I learned about it about 20 days ago and have spent every day since then absorbing, reading and learning.

I LOVE this language / framework / OTP / methodology / community


I experienced the same. Once you start, it's hard to stop. An amazing community around this awesome language is hard to let go.


Do you find it's a lot of extra writing for the contexts? I'm new to Phoenix and my first thought is it seemed kind of verbose to define every single 'meeting point' across 'models'. For simple apps it seems OK but I have some complicated apps with many models and interactions


One of the things that is being stressed in this release of Phoenix is that, to paraphrase Chris, your entire application is a model of your ___domain -- that "models" are really just Ecto schemas, so data, and your web part is really just a shim over your larger application. I encourage you to watch this[0] talk.

[0] https://www.youtube.com/shared?ci=Muyt0zSUAbQ


Can anyone point me to a decent fully contained example of a smallish app in Phoenix on something like github? I've spent a couple of hours reading some of the Elixir docs and getting familiar with some concepts, but I still have no idea how an Elixir app or a Phoenix app would actually look.


Sure! Josh Adams of Daily Drip recently did a Kickstarter [1] to build an open source forum in Elm, Phoenix & Elixir. You can follow the progress either with Daily Drip or just by watching the repository [2] as all the work is happening in the open.

For other repos, Elixir and Phoenix both have "topics" on github now so you can filter a search by repos which are tagged with those topics and order them by stars (obviously this does not sort the wheat from the chaff or the libraries from the projects though). [3]

Welcome to the community :)

[1] https://www.kickstarter.com/projects/1003377429/firestorm-an...

[2] https://github.com/dailydrip/firestorm

[3] https://github.com/search?o=desc&q=topic%3Aelixir+topic%3Aph...


I have worked with Elixir quite a lot, and I think I have a fair idea of how the language works in general, and how OTP apps are structured.

Even so, I find myself having the exact same issue as you are having when using Phoenix and Ecto. The way I like learning something new is by looking at something someone else has made. This gives me a much better view of how things are done than just reading getting-started guides and docs. I have looked, but I haven't found a lot of examples that actually applies to the latest versions of Phoenix and Ecto.


Hi,

Take a look at the Building Umbrella Project from elxirconf: https://github.com/poteto/elixirconf-201620 by Wojtek Mach.

The source: https://github.com/wojtekmach/acme_bank22 is a great example of an umbrella app. For example, he uses two instances of phoenix (backoffice and bank_web). It's a working app, so you can experiment with it too.

The talk is here: https://www.youtube.com/watch?v=6NTmUQClHrU18 I found it very instructive.



For what it's worth, this is generally where the "build a blog" type examples come in handy.

It's a simple, repeatable process that gets you through the basics of the architecture across the board. You'll get a couple of routes, a couple of controllers, a little bit of database access, a bit through the view layers and by the time you're done you'll have a simple app that you can experiment with. It's generally my go-to "learn the language" process for anything.

I didn't open source it, but here's a little write up on mine.

http://www.brightball.com/articles/insanity-with-elixir-phoe...


I can't wait until May[0] for the new 'Programming Phoenix' book. I found the first edition to be a great learning experience.

[0] http://shop.oreilly.com/product/9781680502268.do


Thanks for the tip - just bought a Kindle copy. I was going to get the hardcopy, but Yikes, WHAT has happened to the cost of freight recently?!? It was going to cost minimum $45 to ship a $35 book to Australia!!


I can only assume continental drift...


Programming Phoenix is one of the few programming books I've read cover to cover. Not only does it cover the nuts and bolts of how to do things it also goes into why things are as they are because it was written by the same team responsible for Phoenix itself.


While action fallback sounds pretty useful, it feels like a framework solution to a language problem. The language just doesn't handle nested if statements very well.

Also writing elixir always feels like the lines of code are longer than the width of my IDE. It is so much text...

My Elixir (and phoenix) experience so far, has been far from amazing contrary to all the hype on hn all the time.


My first thought when I read this was that it sounds like you're struggling against the language because you're trying to use it in an imperative fashion. I'm well aware this may not be correct but it's what I'm going to assume for the rest of this comment, so please forgive me if it's incorrect.

Elixir is a functional language, and while it's not pure, nested if statements deliberately do not belong.

The if statement itself is actually just a macro to a case/switch, the only reason it is there is to reduce the verbosity in cases where only one conditional branch is needed, José felt the practicality of this outweighed the fact it may get abused.

To use Elixir properly is to try and move away from conditional constructs as much as is realistically possible. This means instead using pattern matching, guard classes and multiple clause functions (both named and anonymous).

Once you take advantage of these you'll find your code stays flatter while keeping the individual branches of your conditional logic tied up into small independently testable functions.

If anyone would like to read more, I found these [1] [2] to be helpful.

[1] http://culttt.com/2016/05/30/branching-conditionals-elixir/

[2] http://blog.lucidsimple.com/2016/01/24/pattern-matching-help...


Agreed, the fact that you're saying you can't nest if statements is a sign that you still don't get the grasp of functional programming.

cases, conds, with statements, recursion, pattern matching...


Elixir is essentially just erlang, with a more convenient syntax. Being a functional language, you shouldn't be doing nested if statements in the first place. The if statement in elixir is really just a case-switch macro.

Also writing elixir always feels like the lines of code are longer than the width of my IDE

This just seems weird to me. The pipe operator makes the code really compact in my experience, and I wish other languages had this feature.

I have a feeling you never bothered to really learn the language and just jumped on the new fancy framework, and tried to glue some code you didn't understand.

You struggling to write something isn't a language problem. I'm interested in seeing some examples that highlight your problems.


Coming from an imperative language background, it certainly took some time for me to start using and understanding functional constructs. But the nice thing is that once you pass that bump, it's a hell of a freeway to go down.


> The language just doesn't handle nested if statements very well

One of the advantages of this language I've found is that I specifically DO NOT need to nest "if" statements anymore! Hie thee to pattern-matching and private functions that take different states (which are normally handled by nested ifs).

Also, nested ifs are REALLY hard to test well, and breaking them out into separate functions with the same name but different clauses makes it MUCH easier to unit-test.


Why are you using if statements in a functional language geared towards pattern matching?


It is not an imperative language. You can't hit a nail with a screwdriver. Well, you can, but... ;)


Interesting. While Elixir syntax definitely requires writing more code, than for example Ruby which I use the most, I have not been struggling too much. Could you give some examples of these long, problematic snippets?

Also, what are the other things you did not like about Elixir/Phoenix? I am really curious to hear about them since, here on HN they are mostly praised and I wonder myself what are the cons (my experience has been great so far).


    $ irb
    [1, 2, 3, 4].
      select {|n| n % 2 == 0}.
      map {|n| n * n}.
      reduce {|sum, n| sum + n}
    # 20
vs

    $ iex
    [1, 2, 3, 4] \
    |> Enum.filter(fn(n) -> rem(n, 2) == 0 end) \
    |> Enum.map(fn(n) -> n * n end) \
    |> Enum.reduce(0, fn(n, sum) -> sum + n end)
That Ruby code is so compact that I split it on multiple lines only to make the comparison easier.

Even Python is more compact even if it's harder to read because of the reverse order (I'm sure that there is a nested comprehension for that but it's beyond my comprehension skills)

    from functools import reduce
    reduce(lambda x, sum: sum + x, \
    map(lambda x: x * x, \
    filter(lambda x: x % 2 == 0, \
    [1, 2, 3, 4])))
    # 20
The problem here is having to type Module.function(value) vs object.method

OO languages have a more compact notation because objects act as namespaces. Elixir has alias but it doesn't help much and must be used wisely. alias Enum, as: E would only confuse people and gain little.

Then Elixir has too many do end compared to Ruby (coming from Ruby I feel them unnecessary), but that's not a big deal.

Moving from syntax to programming patterns, having to define twice my functions in GenServer (internal API and public API) is too much boilerplate. I'd like to have time to study macros really well and end up with a DryGenServer that lets me def the external API with the internal implementation. Maybe I'll defmacro a defasync and a defsync, that generate the standard "double" GenServer functions.


I'd like to share some solutions. This is a compact notation for Elixir using the capture operator `&` and importing all Enum functions:

  import Enum

  [1,2,3,4] \
  |> filter(& rem(&1,2) == 0 ) \
  |> map(& &1*&1) \
  |> reduce(0, & &1 + &2)

Also ExActor (https://github.com/sasa1977/exactor) library does exactly what you want: generates "standard double" GenServer functions and significantly reduces GenServer boilerplate.

I've discovered that it's significantly easier for me to get in the flow state of mind while programming in Elixir. Comfortable and powerful. Definitely a worthwhile investment of time and effort.


I mean, if you want something really compact, why not APL?

      +/{(0=2|⍵)×⍵*2}⍳4
20

Edit: My point here, besides simply the joy of trolling, is that "compactness" isn't a great metric for analyzing a programming language, or a framework even. My APL example isn't very compact for APL, yet it's certainly /way/ more compact than the Python, Elixir, or Ruby versions.

Still, it's hardly comprehensible to me as a beginner APL programmer -- and I wrote it, just now. It's probably /completely/ incomprehensible to anyone who hasn't written APL before. Is APL still "better", going only by compactness?


The same in J:

    +/*~((0&=&(2&|))@:[#])(1+i.4)
    20
It's longer than APL version, but still cute! :)


Why not `import Enum` to get rid of the redundant `Enum.` part? As imports are lexically scoped there is no need to worry about name clashes (e.g. Enum vs. Stream):

        import Enum
        [1, 2, 3, 4]
        |> filter(fn(n) -> rem(n, 2) == 0 end)
        |> map(fn(n) -> n * n end)
        |> reduce(0, fn(n, sum) -> sum + n end)
While we're at it, why not use a `&` operator to shorten the lambdas:

        import Enum
        [1, 2, 3, 4]
        |> filter( &(rem(&1, 2) == 0) )
        |> map( &(&1 * &1) )
        |> reduce(0, &(&1 + &2))
...is it that much worse than Ruby then? It's a bit different - the syntaxes are different, after all - but it doesn't look much worse, I think.

Elixir, as well as Erlang, are peculiar languages. To achieve succinct code you need to phrase your code in a slightly different way, using pattern matching and guards, an occasional macro and alias/import commands (in Elixir's case).

And the separation between the external interface and an internal implementation of any GenSomething is a valuable thing! Even if in most cases the external interface does relatively little, it's important to have them separated. That is because the external interface functions execute in the caller process, while the implementation code executes in the OTP process. If the caller submits an invalid value to be called/cast to your server, do you want to crash the caller or the server? With the usual pattern, you get to make this choice. And let's be honest - in the simplest case, it's three one-line functions (start, call, cast) per GenServer - it's not that bad of an overhead. There's also Agent module for when you don't need a full GenServer, using it eliminates all the overhead (in terms of lines of code) you normally get when using GenServer.


Yeah, you have a point with the separation between client and server process. I guess the client must crash nearly every time. Still... I can't help feeling that there is something to improve in the syntax, to make it clear the context those functions run in.


I know you want to show it as parallel as possible, but: "pythonic" python (sum admittedly is a shortcut that only works for the special case +):

    sum(x * x for x in [1,2,3,4] if x % 2 == 0)


Works in Elixir, too...

    Enum.sum(for x <- [1, 2, 3, 4], rem(x, 2) == 0, do: x * x)
Edit: BTW: I'm not sure, but I think Erlang had list comprehensions even earlier than Python.


You can omit == 0 in Python because 0 is falsy like in C and add a not after the if. That gains another character

   sum(x * x for x in [1,2,3,4] if not x % 2)
and it matches this Ruby 2.4.0 (which added Array#sum)

   [1,2,3,4].map{|x| x.even? ? x * x : 0}.sum
You understood correctly that the point was not sheer compactness. I only wanted to provide a context to judge the Module.function syntax compared to the object.method one. The filter, map, reduce example was accidental and I'm sure there are clever ways to compact that example further in both languages.

So the pythonic way is be

    reduce_function(map_function(x) for x in input if filter_condition)
I prefer to write it in the order it runs (filter -> map -> reduce) but that's it. Thanks.


I was going to comment along these lines - while functools exists, where appropriate I'd say iterators and/or list comprehensions are more pythonic. Note that if one wants to work with simple reduce functions, there's the operator module to help:

  from functools import reduce
  from operator import add

  # I'd also say this makes for more readable code,
  # documenting intent - but many will probably say that
  # basic arithmetic should be clear enough:

  def is_even(n): return n % 2 == 0
  def square(n): return n*n

  # Don't do this for summing integers, just use "sum":
  reduce(add,
    (square(n) for n in
      range(1,5) if is_even(n)))
  > 20
Note the use of range() rather than literal list - if you have a list, that should probably be passed in by name.

Anyway, the point wasn't so much bikeshedding or code golfing - just expanding on what I think is "more pythonic" take on it.

And to be clear, I'd probably prefer:

  sum(x * x for x in range(1,5) if x % 2 == 0)
for this particular example. And for more complex "real world" cases, I'd probably prefer to define my "reduce" function directly ie:

  def my_sum(iterator): return reduce(add, iterator)
  int_sequence = range(1,5)

  my_sum(square(n)
    for n in int_sequence
      if is_even(n))
  > 20
Note that python has map and filter as built-ins, so it's also possible to do:

  sum(map(lambda x:x*x,filter(lambda n:n%2==0,range(1,5))))
  # Which I find rather unreadable, but gets a bit better
  # without lambdas:
  sum(map(square, filter(is_even,int_sequence)))

(Phew, please excuse my personal dive into new/old list-comprehension/functional python -- but at least now I'm more clear on why everyone keep adding "threading" macros/syntax to their functional languages :-)

[ed: And if one wants threading-like syntax along with some parallelization and lazy-ness in python, there's a module for that: https://github.com/EntilZha/PyFunctional

  from functional import seq
  (seq(range(1,5))
    .map(square)
    .filter(is_even)
    .reduce(add)
  )
Just FYI.]


Right, and then in perl5:

    sum map $_**2, grep !($_ % 2), 1, 2, 3, 4
I find the python and perl versions more aesthetically pleasing than the ruby one, tbh.


    scala>
    List(1,2,3,4).
      filter(_ % 2 == 0).
      map(x => x * x).
      reduce(_ + _)


I love elixir and phoenix, I have started learning it around a month ago and it has been a eye opener, the only thing that I struggle are the error messages, sometimes I spend more time than I should try to understand where I made a typo, I am not sure if it's just me because I am so new to this technology or many ppl have the same problem.


Raise an issue.

José pays a lot of attention to providing great error feedback. And yeah, we've all had incomprehensible messages en route when learning elixir. Things aren't perfect, but they'll only get better when folk point out the shortcomings.


I've wanted to use Phoenix a few times but every tutorial to get me started relied 100% on ecto which for something like a transformation API was frustrating. I didn't want the complexity of a database I wanted to learn the request lifecycle in it. Overall, it was challenging with too many resources being out of date or just so focused on a different type of API than I needed to build


If you don't want to deal with a database and ecto then you should just learn Plug first as Phoenix requests and responses are built upon it.

https://github.com/elixir-lang/plug

https://elixirschool.com/lessons/specifics/plug/


Really? I had a very different experience. I mostly just used the official Phoenix docs and never touched ecto. It was a strictly optional thing I didn't need, so I didn't touch it.

I did look at some other tutorials and I don't recall ever having issues because I wasn't using ecto. Either they weren't using it or I got what I needed anyway.

This was probably about a year ago or so. Maybe things changed at some point?


I'm working on a big Elixir project, and at first Ecto was frustrating (I was learning both Ecto and Elixir). However, upon spending a bit more time with it and learning the basic blocks that defines it, I can say it's a pleasure to work with.

I think this is the case whenever we encounter some new language or new framework/library. In the beginning it's frustrating because we don't master the basic building blocks and cannot write code fast enough. But once we have a solid foundation, a whole new world opens.


I've had the same experience. Coming from Express/Node.js and other micro-frameworks in other languages, I yearn for a `mix phoenix.new --no-ecto --no-brunch --no-view --no-controller ...` Basically the bare minimum to serve a piece of HTML or JSON down a pipe.

I ended up doing what Dangeranger said and used Plug, but now I'm finding that phoenix channels would be a lot more ergonomic for the project I'm working on...


Here's [0] a pretty good explanation of how Plug and Conn fit into the mix. Phoenix is just a pipeline of Plugs, which in tern are just modules with init/1 and call/2 functions. Controllers, routes, all of it is just Plugs under the hood. This should help you get your bearings.

[0] http://shankardevy.github.io/code/elixir-phoenix-conn-reques...


I did a short video series for Pragprog.com on Phoenix that does not use a database, but does get you started.

It looks like it'll all be out of date by the time 1.3 hits though. :(

Anyway, it's at https://pragprog.com/screencast/v-bhphnx/build-real-time-web... if you're interested.


If you want to learn Phoenix without Ecto. Checkout my screencasts: https://www.youtube.com/playlist?list=PL_uO5Hv34JImYx9zN73wD... It builds a simple Broken Links application which finds broken links in an input url


Is there an actual reason for shortening everything to "phx"? I would guess the same rules as naming variables would apply here.


action_fallback seems like a great addition. It is helpful to expose all your error codes in a single place.

However, I am not too convinced about having a context for each resource. I think that should be optional, meaning that I should be able to specify a context for some related resources but at the same time, I should be able to have resources outside of any context.


It is not a context per resource. The context should group all resources and responsibilities that belong to that context. For example, if you are building Github, you could have an IssuesTracker context where you would find the issues, labels and other all issues tracker related resources in there.


Best part for me is the new fallback controller plug. Really well done. That and the centralized error list. This is one of the things which is miss in Go (I think this is the same in Rust, but I am not an expert).


Be careful: that is a framework, not a language feature.


Is a language useful without useful frameworks around it?


I believe at least most of those lisp dialects are useful without any frameworks. You get view template with s-expr for free instead of erb/ejs/eex. And you are basically composing everything like plug in elixir very easily. However, the down side of not having a major framework is community does not have same context to discuss and share, which keeps almost every lisp dialect remains out of mainstream.


Most of the time not, but the comparison of a specific feature of phoenix and go as a programming language is not valid.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: