4 ms·
I've seen a few (as in 2 ;) ) examples of this myself. And this is what "scares" me the most for elixir's future/potential. I think it's a great language/tech
by dudul 9y ago
I've seen a few (as in 2 ;) ) examples of this myself. And this is what "scares" me the most for elixir's future/potential. I think it's a great language/tech but ruby enthusiasts rushing into it thinking that it's "just like ruby but faster" may have a very bad influence on where the language goes. They may bring a lot of ruby best practices that don't transpose at all in elixir/erlang world.
- bsaul 9y agoThat was indeed my underlying point. I see elixir more as "erlang with easier syntax", than "ruby with a more powerful runtime". aka : you should get to understand erlang design decisions prior to jumping into the tech. Which probably means having more experience than building the typical low load website made with RoR.
- sotojuan 9y agoWe already had a ton of Rails devs who didn't know Ruby or OOP well. If Elixir becomes very popular, we will have Elixir devs that don't know OTP, functional programming (outside of pure functions/immutability), or Erlang. That's the way it goes.
- pmontra 9y agoI'm in the fourth month of a Phoenix project, which shields me from almost all of OTP, and I agree with your point. Maybe even "Erlang with a Ruby syntax" would be too much. Similarities with Ruby don't go any further than "def" "do" "end" and many library modules and functions deliberately named to mimic corresponding modules and methods in Ruby. Everything else is different, both syntax and semantic. After 10 years of Rails, all things considered Elixir and Phoenix are pretty good. The best feature of the language so far is pattern matching. It can be used in the arguments of function definitions. It makes all the "if"s go away. It seems strange after 30 years of programming, but a program without conditionals is easier to read. Not having to use an external background job framework is great news too. Just spawn your processes to send a mail, process something asynchronously and store the result in the database. No Sidekiq (or Celery if you're into Python.) On the awful side, Ecto is too low level and it feels like a self inflicted pain. I understand the reasons behind its design decisions, but the typical web application or mobile backend don't need all of that. Actually the team working with me on this project wrote a lot of code to build queries and wrap them into another layer to return {:ok, result} / {:error, reason} tuples. We're calling them from controllers more or less as Model.get_something(args). I checked it now and we have zero occurrences of " from " in controller code. All the queries are confined inside a model. If we were using Rails ActiveRecord would have written all that code for us. Instead we had to use a more verbose query syntax, write our functions and tests. This is a net loss of productivity. There are some modules that can be mounted on the top of Ecto to make it look like ActiveRecord or other ORMs for other languages. I'm looking forward to using Ecto.Rut in my next project. I hope I'll never have to use Ecto directly. https://github.com/sheharyarn/ecto_rut https://github.com/sheharyarn/ecto_rut We almost didn't use the supervisor layer of the language (somewhat like systemd/init for who's not familiar with it.) I used it in smaller projects and I feel like it's a little too complicated. Same for defining a GenServer, more complex and verbose than exposing the same functionality with methods from a object. I know we have to split the code between what runs in the client process and what runs on the server process, but I feel there should be a vanilla GenServer that handles the most straightfoward case inside the behavior, which is no code in the client with the exception of passing the arguments to the server. Maybe use the name, sigils or module attributes to declare if there will be a synchronous return value. Anyway, Elixir is still young and there is time to smooth the rough edges. So far it's an acceptable contender and definitely better than the popular scripting languages when concurrency is important.
- karmajunkie 9y agoDon't take this the wrong way, but it sounds like you're approaching this from the perspective that Rails' way is the "correct" way when in fact almost everything about Rails' doctrine will cause you problems over the life of a project. > We're calling them from controllers more or less as Model.get_something(args). I checked it now and we have zero occurrences of " from " in controller code. All the queries are confined inside a model. Yeah, that's almost a best practice, except you're regarding the "model" (the terminology is 'schema' in Ecto) as primary instead of building it as a separate context or application (depending on your preferred approach to modularity within Elixir/Phoenix). For that matter, I fail to see the advantages of `Model.get` over `Model |> Repo.get(id)`. > If we were using Rails ActiveRecord would have written all that code for us. There's not a substantial difference between the kind of AR usage that's on the happy path and the equivalent query in Ecto. > I know we have to split the code between what runs in the client process and what runs on the server process, but I feel there should be a vanilla GenServer that handles the most straightfoward case inside the behavior, which is no code in the client with the exception of passing the arguments to the server You mean like an Agent[1]? [1] https://hexdocs.pm/elixir/Agent.html https://hexdocs.pm/elixir/Agent.html
- pmontra 9y agoFirst of all thanks for the detailed answer. It contains many interesting points. I don't think that AR is correct, I think that AR is more convenient than Ecto in many cases we run into in web development. With AR I don't have to write my own queries and encapsulate them into functions (but it's kind of what we do with scopes) and it has a more compact syntax. Ecto is more flexible, but that flexibility is not needed in most cases. Anyway when we must really be flexible we write queries in SQL and manually unmarshal resultsets. ORMs/Data Mappers are for simple and medium use cases. I still didn't run into problems with the AR approach. Maybe it's because all of my projects were medium or small sized. I found AR to be perfect for them to the point that I want to disguise Ecto as AR using Ecto.Rut. > you're regarding the "model" (the terminology is 'schema' in Ecto) as primary instead of building it as a separate context or application This sounds interesting but I fail to understand what you mean. Would you mind explaining or posting a link? Thanks. > I fail to see the advantages of `Model.get` over `Model |> Repo.get(id)` It's shorter but not by much. One reason is that Repo doesn't mean much to me, so it could be hidden. What I care about is Model. But getting values out of the db is not such a pain. Inserting and updating is, because they are more verbose. I quote Ecto.Rut for the insert Post.insert(title: "Awesome Post", slug: "awesome-post", category_id: 3) # instead of: # changeset = Post.changeset(%Post{}, %{title: "Awesome Post", slug: "awesome-post", category_id: 3}) # YourApp.Repo.insert(changeset) If all of those extra characters are for extra flexibility (maybe for using more repositories in future?) then it smells of premature optimization. I'll happily do without it. Regardless of this discussion, IMHO a thing that Ecto should fix is requiring developers to write both the migration and the schema. It's either the AR way, migration first and auto generated model, or the Python way, model first and auto generated migration. We got some bugs because we didn't write the same things inside the migration and the schema. Mistakes happens and the tools we use should help us not the make them. Ecto is not DRY but it should. Btw, the compactness argument applies more or less to all of Ruby vs Elixir because of the object.oriented.notation.is.shorter than the Module1.functional |> Module2.way |> Module3.of |> Module4.composing. Aliases help to some degree but they add clutter to the top of the file. It's a little nuisance but pattern matching more than evens it. I have to look more into Agent (thanks) but it seems exactly the opposite of what I want: write in the module only the code that should run in the server. Probably what I'm looking for is a macro that writes a vanilla GenServer for me hiding all the functions run on the client.
- quaunaut 9y agoIt's worth pointing out Jose Valim both created and maintains the language, and he's a core team member of Rails. He knows what to take and what to leave behind from Elixir, and has been incredibly thoughtful so far. I'm very confident in his stewardship.