For languages like Ruby we had lot of books talking about design patterns applied, or what OOP looks like (excellent resources like Sandi Metz’s POODR)
However, outside of Crystal Programming one, we don’t have a lot of resources that talks about what a good design in Crystal looks like, right?
I’m wondering if some of you can provide references what are those that you see are good patterns or design decisions (outside of Crystal’s own stdlib codebase).
For me idiomatic crystal is add types everywhere, for every function header, because after some time you stop understands code. look at the example: crystal/src/compiler/crystal/codegen/call.cr at master · crystal-lang/crystal · GitHub you won’t understand what these arguments are or what their types are, and because of that, it’s also hard to understand what the function does. Also this is helps LLM better read code.
I don’t think there’s necessarily one form of idiomatic code. It depends on what you try to implement.
Taking the example for upcasing a file’s content from the previous comment.
I’d say that’s idiomatic. It’s a medium optimization improving efficiency for some forms of input data.
A single step conversion is also idiomatic: File.read("intput.txt").upcase.
That’s much more concise. But less optimized, of course. Optimization is not always useful. When the input is known to be small, this is entirely fine.
The impact of optimization also depends heavily on the use case. The one-shot might even be more efficient than iterating per line for some forms of input data because we only need exactly two string allocations of known size.
On the other end of the spectrum, an even more efficient implementation would read fixed-size chunks of bytes, instead of lines, and convert them. Another idiomatic form. This wouldn’t even need any intermediary allocations. But it’s much more complexity. So it only pays off if optimizing this code path has a significant effect.
I think one idiomatic treat of Crystal is to provide a good base implementation that works well for 80% of use cases and is easy to use.
If you need to go deeper because you need more performance, you can.
Crystal tries to avoid different way’s to do the same thing. As long as these ways are similar. But sometimes it’s good to have a choice between different behaviour characteristics. It’s usually a trade-off between code complexity and performance.
Try not to over optimize things. Instead: make it work, make it right, make it fast (if you need to).
Focus on making your idea to work. Understand it. And about the “make it right” part? There is no way to improve something that doesn’t exist.
Create first. Make it happen. Improve later.
not so tl;dr
Then, when you create, the experience grows. You will have “god classes” (bad, but works), then you will split them. Then you will not listen to the advice of not over optimizing. And will move things from the heap to the stack. Them you will understand that, many times, this is the wrong thing to do.
Design patterns? Learn them not as “laws”. The pain creates the necessity. Only apply a pattern when it actually makes sense.
Having the perfect code that doesn’t deliver any value to no one, means nothing.
Having a not perfect code (that you understand) but which delivers value for its users, means everything.
Solve real necessities. Software is not the end goal. It is a tool that helps achieving it.
And Crystal is really good to achieve goals in a productive and efficient way.
For me, “idiomatic” is pretty situational. For example, I think a lot of folks focusing on app development may not consider code that looks like this idiomatic, but they probably think the code that it lets them write is. And sometimes you need unidiomatic implementations, but you probably want to stick to idiomatic interfaces whenever feasible.
It’s like how idiomatic Rust code is often considered provably safe, but even the safest Rust code usually depends on Rust code that’s explicitly marked unsafe, even if not directly. It’s all about how you compartmentalize the code that deviates from established norms.
I’m not sure this really answers the question, tbh, but it’s something I keep in mind when writing Crystal code.