Having a team member create production code in a language nobody else on the team understands is a risky decision. That applies for anything, software, systems, infrastructure... if one developer is allowed to sit on something, what happens when the dev leaves? Or dies? And the thing he or she worked on had a non-trivial learning curve?
Choosing something like Haskell or anything else niche should be a group decision. Even when it comes to prototyping! "Guys, I'm going to prototype this .NET code in F#. Who wants to join?", instead of a developer sitting in a corner applicating his functors while others around him do something arguably simpler. It's incredibly short-sighted!
This is not aimed at you though, since I'm not sure if this was your case, I'm only guessing based on your post.
The principal data structure of Lisp is a list which is written like this: (a b c d). Like the article mentions, when you see Lisp code you're in fact reading the same list syntax, only that, when the list itself is not quoted, the "a" is evaluated as a function, which is why (+ 1 2) behaves the way it does. If you quote the list by saying "'(a b c d)" then it is a list of symbols, so "'(+ 1 2)" does nothing.
Hence, Lisp code is made of nested lists. When you think further about it, it means that Lisp metaprogramming facilities may do things just with plain list manipulation functions! So it's not about metaprogramming itself, it's that it's incredibly easy. This is what people refer as "code as data" and "homoiconicity" and so on.
"If you know you will never change the implementation or configuration of some dependency, there is no benefit in using dependency injection.".
It all boils down to this.
Here's the catch, though: with dependencies, it is really, really hard to know you will never change the implementations. In most languages, implementing dependency injection is so trivial that it is always worth it. The work associated with changing implementations that aren't built with some form of loose coupling is in most cases, non-trivial.
From what I've seen, Clojure has a strong type system, but is dynamic. These terms aren't orthogonal. If I understand correctly (+ 'a' 1) is not allowed. Immutability only strengthens it.
I ask this because I've been eager to get my hands dirty with Clojure. I have experience with other functional languages (Haskell, OCaml, F# and some Emacs Lisp), so the paradigm isn't new.
I've looked at core.typed, but doesn't seem as neat as the syntax of Typed Racket. It's something, though. Is it checked at compile time?
What irks me is that static typing and type inference can make code feel really robust, and for the lack of a better word, safe. In Clojure I see a lot of nice things but the lack of typing, though common for Lisps, always bothers me a bit. I don't like runtime errors that happen because the compiler wasn't able to tell me that this object doesn't have that method. JavaScript's undefined is not a function or its kin in Python are examples of this behaviour, which I don't like.
Though, I see that Clojure's answer to this is rapid REPL development--which is great!--and unit testing, and what I've recently discovered, pre- and post-conditions. But it still feels somehow inferior.
I am torn between learning Scala or Clojure. Knowing OCaml and F#, Scala doesn't look that interesting, messy and multi-paradigm. Clojure appeals to me because its a Lisp and has one paradigm, but on the other hand I'm scared by runtime exceptions.
> Should I just ignore my trepidations and proceed?
Yap give it a try.
From what I understand Core.typed while not as rigorous as static type system in Haskell or OCaml can let you gradually add typing to your application. They more you add the greater the benefit.
But also personally haven't used it.
I used Dialyzer in Erlang, which is a similar concept. You annotate your code with types and the more you do the more type errors it will find for you:
Code readability is the original intent behind striving for simplicity.
Though I find some of the design choices a bit frustrating, e.g., generics, lack of min/max; for each design choice I disagree with there are dozens of other choices I do agree with. Structural typing, built-in concurrency, a rich standard library, the syntax, the list goes on.
There is a certain brutal elegance to the way the Go standard library is designed, it is really easy to read and comprehend. It took me less than half an hour to make sense how the net/http server was implemented.
This doesn't only apply to the standard library: because the language style is standardized--there are no coding styles, there is just a coding style--other libraries or programs are very easy to understand. If the program architecture is easy to understand, it doesn't require any significant domain expertise to understand.
Languages aren't supposed to be cargo cults, though some culture can be nice, let's not kid ourselves: programming languages are means to to an end. Go is pragmatic, it gets things done, it is a tool, and most importantly, it doesn't get in the way of design or architecture. You are free to build programs in any style or way you want.
Ultimately, languages are just expressions of grander designs that are far more important than arguments for and against a particular syntax or language feature.
The Windows-first status of F# is changing. There have been major improvements in this recently, most notably, the whole compiler being open sourced and its development moved to github[1], and .NET is going to be available on Linux[2]. This is not reality yet, but it will be during this year.
Choosing something like Haskell or anything else niche should be a group decision. Even when it comes to prototyping! "Guys, I'm going to prototype this .NET code in F#. Who wants to join?", instead of a developer sitting in a corner applicating his functors while others around him do something arguably simpler. It's incredibly short-sighted!
This is not aimed at you though, since I'm not sure if this was your case, I'm only guessing based on your post.