Can We Have Form Objects in Elixir?
blog.lelonek.me
blog.lelonek.me
I was using Ruby myself for 10 years before using Elixir and the Form object pattern was one I used very often myself. A general principle of Elixir I have often seen that if you think something doesn't work, or is the wrong way you in fact need to unlearn some of the brain damage acquired from Ruby.
A great example of this is Wojtek Mach's github_ecto project (https://github.com/wojtekmach/github_ecto). His abstracting of the Github API behind a Ecto.Repo was mindblowing to me. Your app communicates with a HTTP API without knowing it's any different than your DB.
Elixir is very much a different way of thinking about problems than Ruby.
https://github.com/cieplak/chat/blob/master/src/http/forms.e...
https://github.com/cieplak/chat/blob/master/src/domain/types...
No data validation at the moment, but it would be nice to generate the form parsing and validation, as well as the getters and setters, from a declarative spec.
In the following code (from the linked site): What is embeds_many? Are :string and :map type information or Atoms? What happens to :changes, Change, primary_key? Does the code between do and end just call the field function twice?
embeds_many :changes, Change, primary_key: false do
field :field, :string
field :value, :map
endembeds_many is another such macro [2], that allows you embed another schema, in-line, into the current schema. This is contrast to your more common has_many relationship, which references another table entirely.
Here, :string, :map, etc are atoms that are being passed to the field macro [3], to define the schema for the table.
[0] Basically a super lightweight ActiveRecord
[1] https://github.com/elixir-ecto/ecto/blob/b030353d4b94ddd4216...
[2] https://github.com/elixir-ecto/ecto/blob/b030353d4b94ddd4216...
[3] https://github.com/elixir-ecto/ecto/blob/b030353d4b94ddd4216...
NOTE: I haven never used Ecto, but I have used Elixir for a few hobby projects.
1. `embeds_many` is a function name, described here: https://hexdocs.pm/ecto/Ecto.Schema.html#embeds_many/3
2. Anything that begins with a colon is an Atom in Elixir, including :string, :map, :changes, :field, and :value. These atoms are used by Ecto at runtime to identify certain things. `:changes` is a unique name given to the embedded schema, `:field` and `:value` are field names on that schema, and :string and :map are informing Ecto of how to treat certain values.
3. `Change` is the name of a Module which describes the Schema we are embedding
4. `primary_key: false` is a configuration option
5. Everything after `do...` is describing what the embedded schema would look like. So it has a `:field` field that is of type string, and a `:value` field that is of type map.
Hopefully that makes some sense... I'm not totally clear on it either (having not used Ecto before) but that's what I'm grokking.
In Elixir, the most basic function application looks fairly standard:
func_name(arg1, arg2, ...)
But the parens are optional, so you can write: func_name arg1, arg2, ...
as well.Next, in Erlang and Elixir function arity (number of arguments it takes) is always fixed (i.e. no varargs or Python's *args). Because of this you generally have to pack your arguments in a list; functions like `printf` work that way:
iex(2)> :io.format("fmt ~p foo: ~p bar: ~p", [1, 3, 4])
fmt 1 foo: 3 bar: 4
That takes care of (positional) varargs. There are some other functions, however, which would benefit from "keyword arguments", which also isn't directly supported. To get around this, you can take a list of pairs (keyword, value) as one of your arguments (usually last): some_func(arg1, [{:keyword1, value}, {:keyword2, value}])
Where `{}` creates a tuple and `:` creates an atom (called symbols in Lisp), which you can think of as a special[1] string value. This pattern is so common, that Elixir provides a syntactic sugar for it. The above is equivalent to: some_func(arg1, [keyword1: value, keyword2: value])
The brackets around keyword list are also optional, but only inside function call. The most sugar-y way to write this looks like this: some_func arg1, keyword1: value, keyword2: value
The last thing of note is that the `do ... end` is just a syntactic sugar for `[do: ...]`, where `...` is treated as a block of code.Normally, the block of code would be evaluated and only last value would be passed to the function:
some_fun arg1, kwarg1: 1, kwarg2: 3 do 1; 2; 3 end # notice no comma before `do`
would be equivalent to (after all the sugar being resolved): some_fun(arg1, [{:kwarg1, 1}, {:kwargs2, 2}], [{:do, 3}])
However, when writing a macro, the macro gets the unevaluated block, which then can be modified or executed. See http://elixir-lang.org/getting-started/meta/macros.html for more info on that.Edited to add: Capitalized names in Elixir are actually special atoms, mostly used for naming modules. It works like this:
iex(5)> :'Elixir.Change' == Change
true
There is more going on because of the fact you can alias module names, but mostly you can think about this as (another) special syntax for atoms.[1] Allocated once and cached. When working with normal strings:
a = "Asd"
b = "Asd"
you have no guarantee that `a` and `b` point to the same thing in memory, while with atoms you do have that guarantee. This makes comparing them efficient.In the newest Elixir I get a warning about a variable not existing when I omit the parenthesis, and it suggests to add them.
Or I'm missing something?