Hi,
I think it would be cool to be able to require a certain type for my fields when I create structs. Like specifying one field should be a integer and another a list. So I am not talking about Ecto schemas but regular structs with type checking. What do you think?
Thanks for reading!
I use https://github.com/ejpcmac/typed_struct for all of my structs. The built-in dialyzer integration provided by the Elixir LS editor plugin does the checking.
Hello, thanks for the link, that library is cool and I am going to use it. However I thought that maybe something like this could be nice in the standard library.
One of the reason why Ecto has been split is to separate Sql from casting and validation.
It’s totally possible to define Ecto schema without table, for example a Contact Form, which will have casting and validation included, without ever touching the database.
Otherwise, You can define @type for the struct, and get dialyzer checking, or use the lib indicated by @tme_317
You might want it in the standard lib, but You have this in Ecto already
I am making something small and I found it to be redundant to use Ecto for it when I dont need it for anything else. I think it would be more better to have this in elixir and not ecto because then people could then validate their regular structs to too, not just their schemas. Without pulling in a extra dependency.
This has been an irrelevant problem ever since the Apple IIc computers stopped being mainstream. What are you worried about? A several dozen of kilobytes extra on your drive?
Doesn’t seem like a valid objection.
I like keeping my things as slick and small as possible. If nobody agrees with me on this then I will do things by myself in my code. For this I will be using the TypedStruct dependency because that does exactly what I want, not more.
For a hobby project that’s completely fine.
In a professional programming setting however, you will not be given this luxury.
I am going to follow the rules my employer gives me. If I get a job in the Elixir field.
What are you worried about? A several dozen of kilobytes extra on your drive?
Maybe about
- the need for keeping track of updates of that dependency
- potential conflicts delaying updates of other dependencies
- introducing potential vulnerabilities
- extended compilation time
- waking up when a dozen kilobytes turns into hundreds of megabytes, as in case of NPM and node_modules
So definitely having as little unused stuff in your deps as possible is a good approach
Dependencies can be pinned to a version that doesn’t break. Security for small dependencies is a non-issue.
Compilation times for become problematic if you pull 50 dependencies, that’s true.
I agree dependencies can balloon to hundreds of megabytes but I’ve only seen that in the JS and Rust ecosystems. Never in Elixir.
I like the purist and minimalistic approach, mind you. But I found that it can be very unproductive in a professional setting. For hobby projects you obviously can dive as deep as you like.
With the time, they’re going to lost compatibility with the dependencies you need to have up-to-date.
Wouldn’t be sure about that, remember this case? Each package can be altered in a malicious way, no matter if big or small.
I agree that professional setting requires some tradeoffs, but I believe using Ecto just to write schema instead of @type and defstruct is not one of them
Having as few dependencies as possible is a valid requirement and also the philosophy of Erlang and Elixir as far as I know: “A little copying is better than a little dependency.”
I don’t think that there is any point you can make to invalidate this reasonable demand. Including Ecto for just the type checking seems overkill.
On that I agree. I was saying that the general sentiment for it is rather overblown.
True, and I do that in hobby projects all the time. Although to be fair to both sides, having several good unit tests that make sure that your dependencies together with your code continue to do what you expect them to do has saved my neck from breaking changes many times. So choosing between little copy-paste and including a dependency in your hobby project is honestly a toss up and is mostly a personal preference.
I can and I did: it saves time and if you take a little special care (one-off effort to add several automatic tests) then you will be quite safe.
This is not at all related to the topic at hand. What you linked to is a problem of how an external entity is governing their code hosting and has zero bearing on “is it safe to include literally 10-15 lines of JS in my project via a dependency?” Yes, yes it is. The problem was the hosting.
What specific operations would this provide? “Type checking” can mean a lot of things in Elixir:
-
compile-time success-type checking, as provided by Dialyzer
-
runtime pattern matching and guards, like:
def some_function_that_wants_a_list(x) when is_list(x) do
- field enforcement on structs; a mostly compile-time guarantee that specified keys will be in a map tagged with the
__struct__
key. For instance, this will fail if the target struct doesn’t have afoo
key already:%{target | foo: "bar"}
typed_struct
(recommended in this thread) is ultimately just a shorthand for writing a Dialyzer typespec and defstruct
; the compiler ultimately sees the same code.
To return to my original question, what kind of “type checking” behavior are you looking for? For concreteness, let’s imagine we have a “typed struct” called Blog
that expects its tags
field to be an array of strings. What should the following do:
%User{tags: 1234}
- wrong type%User{tags: ["a", 1234])
- mixed types in a field%{some_user | tags: 1234}
- update with wrong type%{some_user | tags: ["a", 1234]}
- update with mixed types%{some_user | tags: value_that_might_be_a_list_at_runtime}
- compiler has no informationMap.put(some_user, :tags, 1234)
- structs are maps too
Dialyzer will catch some of these, when it has enough information to prove things are the wrong types - but it’s going to struggle with things like the value_that_might_be_a_list_at_runtime
case and give up ENTIRELY on the Map.put
one.
Communicating typing failures out of these constructs is going to be difficult - they don’t normally “fail”, so the only available option is to crash the process.
Also note that all of these constructs are used extensively in Elixir’s implementation - so any overhead added will be significantly amplified. That’s one reason to prefer an opt-in library like Ecto.Schema
, which layers the additional functionality onto bare structs instead of adding more complexity directly to them.
I’ve using this library to do data validation, it’s similar to what you can do with Ecto but the plus for me is that also have the option to generate valid data.
It’s totally a valid objection, you should select your dependencies carefully. Otherwise you end up in a ridiculous dependency hell.
This is one of the reasons the js ecosystem is such a tire fire, node_modules full of thousands of tiny modules is provably slow and a security risk as you cannot possibly audit them all.
I didn’t argue against that.
And I already agreed with that.
Not sure why you are replying to me in particular since I made the distinction that the left-pad
Node.JS fiasco was a problem due to how the hosting company handled it – the dependency itself was quite safe to include.
Whether or not your workplace is going to allow you to be that cautious with dependencies is a separate topic entirely.
I’m replying to you because of:
This has been an irrelevant problem ever since the Apple IIc computers stopped being mainstream. What are you worried about? A several dozen of kilobytes extra on your drive?
Doesn’t seem like a valid objection.
I wished to point out its a valid objection.