The Untouched Goldmine of F#
rm4n0s.github.io
rm4n0s.github.io
One of the main reasons why companies move from monoliths to microservices is to promote ownership and accountability in large codebases.
In a monolith where everyone owns the code, developers can break each other's code.
With microservices, each team becomes responsible for one part, and (as long as they keep their SLAs) they can't break each other's code.
When something fails it's easier to identify who needs to fix what.
Microservices don't make much sense for small teams if they don't need or don't have the headcount to split responsibilities.
There are co-ordination costs to microservices. But there are also independence benefits.
Now sometimes folks (the Windows team famously did this) build systems which identify which tests to run based on changes that occur in the codebase, but that's _not_ easy.
If you're interested in iterative development and continuous deployment, which of course I am, it is a natural fit. It is productive in that case.
When done well, microservices work as a unit of deployment. The team that owns the service code deploys it on their own pace. Sure there is process to follow, but it involves 10 people in that microservice team not 100 people for the monolithic app. This allow for smaller batches and independence of development.
Defining a clear public contract in most existing programming languages that cannot be violated by a lazy programmer is tricky. Java's package-private should be the answer but is too blunt and ends up requiring a team to make things public so they can use them. C# has a bit more control that might make clear contracts possible (I haven't tried), but not everyone wants to use it because Microsoft. And most of the popular languages at the time of the rise of microservices had basically no mechanism for defining public versus private interfaces.
Microservices allow you to do that in any language— your HTTP API is your public interface. Anything inside of that is physically impossible to access without adding an explicit build-time dependency, which would be trivial to catch in code review.
Yes, code review could theoretically solve the same problems, but in practice no organization is that disciplined. You need tooling, and those tools didn't exist yet when microservices came to be. We're seeing them develop now (Rust's visibility modifiers are very powerful, and monorepo tools are starting to develop lints that can impose module boundaries on languages that lack them natively), and it's not a coincidence that around the same time we're seeing a lot of people moving back to monoliths.
Well, they still can and do break someone else's code. It is just breaking someones code with more steps in between. And who is at fault isn't necessarily as clear cut.
That is the weirdest and most crazy thing I've read in years.
I think the TSTs should also contain the stacktrace. Not or X or Y, but both X and Y.
badvalue = dobadthing(); dostuffwith(badvalue);
Depending on the indirection between dobadthing() and the place that chokes on badvalue the stack trace can be completely worthless. It is one of the reasons people hated the original Spring. Claimed it decoupled stuff but all it really did was obfuscate.
I have seen similar claims made about Clojure. Bad data coming from somewhere, but no way to trace it through the system. Sure you can filter bad data in to the garbage can, but that just masks the issue not fix it.
Glad to see more folks finding and appreciating FP languages like F#. It's good stuff. Scott Wlachin has a great site to discover more F# goodness[1].
[0]: https://hackage.haskell.org/package/monad-validate-1.3.0.0/d...
Which is fun to see, really, because it is interesting watching someone rediscover FP principles from first practice.
The good old missing sum type story.
Don't Rust, Haskell, Elm, Kotlin (with sealed classes), etc also have them?
1. Absurd claim that file names aren’t important in a stack trace 2. Claims they’re sharing a feature of F# that is never used, then later reveals they’ve been studying F# for 3 months 3. Then this statement about tagged unions
But I felt similarly when I picked up Haskell coming from Ruby and Java, so I can understand the attitude. :D
Go does not. Java did not (maybe now with sealed types and exhausitvity checks on switch statement, but not sure if they've already landed).
Try this:
sealed interface Shape permits Circle, Rectangle { }
record Circle(double radius) implements Shape { }
record Rectangle(double length, double width) implements Shape { }
void test() {
Shape shape = new Circle(5);
switch (shape) {
case Circle _ -> System.out.println("Circle");
case Rectangle _ -> System.out.println("Rectangle"); // Comment out this line to get an error.
}
}These are useless information?
https://github.com/dotnet/csharplang/blob/main/proposals/Typ...
I came from the other side: I own a huge monolithic web behemoth which is almost unbearable to maintain now. It was built using a now outdated technology, but: it serves customers, it runs the business, it brings profits. Nevertheless, it is a huge pain to even try to change something in it. The project is at its dead end now, it is a one-trick pony who has become too old.
Nowadays I build newer parts using separate well-defined services. It is not textbook microservices, I would call it just services. Imagine building a mini-product that is not publicly available and only used internally. This kind of productized services work well enough to never look back at the fragile monolithic approach.
Stacktraces show me call-stack to the point the error happened. Something TST does not always show: different call-stacks can result in the same (or very similar) TST-stacks.
It is possible to return the stacktrace as part of the TST error (not just an error message but also a stacktrace).
ORMs are a bad idea, even when using OO langs: they make the simple queries slightly simpler (`Users.getById(id: Long)`), they do not help you for hard queries (ORM-using codebases of size usually have hard-SQL-queries "in strings").
Most users of FP langs know this and hence will not even try to implement ORMs.
Look into jOOQ, LINQ-method-syntax (or whatever it is called, without the funny SQLish syntactic sugar), SQLDelight or sqlx for non-ORM options that improve embedding SQL in general purpose langs.
Also ORM for simple row selects is a straw-man - their use case is deserializing, updating relationship graphs where they hide away a bunch of code. Should that code be hidden is a different question - but pretending it's there to save you from typing SELECT * FROM foo is kind of disingenuous. Editing graph data structures in most FP languages is just not compatible with OOP approach hence no ORM.
The book of Hibernate is thicker than the book for SQL.
The only thing that works better in ORMs is "save". Just call save on an ORM managed object and it is saved, quite a bit harder in SQL.
But I also like stack traces, as they show me call-stack to the point the error happened. Something TST does not always show: different call-stacks can result in the same (or very similar) TST-stack.
People have been banging on with type systems and similarly "better" capabilities for at least 2 decades [0] and Enterprise continues with a stark preference for language "practicality" and low barrier of entry.
IMO it's because it's best to keep engineers superficially interchangeable rather than having a highly stable system (perhaps stable over spec) and a costly workforce with lots of negotiation leverage. But I digress.