DEV Community

[Comment from a deleted post]
 
kspeakman profile image
Kasey Speakman • Edited

I don't use a lot of IDE tooling as far as refactoring code or inserting snippets. As I do refactoring (as I'm typing in F#), the compiler tells me other things I have broken. It's kindof a todo list of places to touch. But I do the fixes myself to ensure it makes sense. If it gets to be too much pain to do this, then rather than reaching for better tooling, I take it as a signal that my design should be re-examined or organized better.

I agree about standard typed OO languages. There ends up being a lot of effort spent on just the type system... perhaps more than is saved when doing proper SOLID principles. So many interfaces and classes. Note that type annotations are optional in Typescript... it's basically just JS with optional extras.

Haskell is an interesting one among the ML family. I would consider Haskell "extremely typed" when used in a way that is idiomatic for it. I tend to gravitate toward more moderate languages like F#, OCaml, or Elm. My specific experiences are with F# and Elm. The typing overhead seems heavier in Elm, mainly because of the MVU pattern it uses. But Elm taught me a very valuable lesson about the benefits of pure functions. Over time, we have literally gutted our Elm code base, removing hundreds of lines of code overall, and drastically reducing the possibility of state-based bugs. Since all Elm code is pure (no side effects), refactoring has little risk... because once it compiles, it won't crash. (Refactors still take effort though.) I confess that we don't even write tests for our Elm UI -- it just hasn't seemed necessary. We do sometimes discover bugs, but so far blocking bugs are found, fixed, tested, and deployed within an hour. We have had regressions before, but usually it is because we didn't "make bad states impossible"... taking some extra time to adjust the types can literally prevent the need for that test. F# is an impure language, so you have to practice some discipline to keep functions side-effect-free. But it carries the same benefits when you do. Pure functions are especially nice to use in domain logic... makes testing really easy too. We extensively test domain logic, verifying both success and failure scenarios behave as expected.

For perf, I was thinking more of code which is close to the metal, like device drivers or file systems. Perf for business purposes is great in any of them, and they have knobs you can turn for specific cases. For example, if you look at this challenge, I wrote entirely procedural F#. And the author's Haskell version used some impure libraries to get about the same perf as mine (maybe better if both were run on same hardware). Both of these performed better than a naive C implementation.