heartofgold
Joined 7 years ago
Joined 7 years ago
This is so unfortunate. Lisp has so many good things to teach us.
For the most expensive tier: 40 cents per million API requests for plain SQS vs 50 cents per million API requests for FIFO SQS.
Like many AWS Services, it started off pretty basic or limited, but gradually and incrementally improved over time.
I started off pretty skeptical, but over time I've grown to be impressed with SQS and SQS FIFO.
Anyway I am glad to see these gaps and caveats have been closed.
I do find languages like Scala and Haskell slightly appealing, but I find many of the deep concepts and rigid type are orthogonal to the problem I am dealing with at hand. I realize for some working thru the type system helps them solve the problem at hand, but for me, it often seems to get in my way.
Now consider that COVID-19 is much more contagious than the flu and it has a significantly higher case fatality rate than the flu.
Furthermore, people can and do protect themselves against the flu with a vaccine. There is no vaccine for COVID-19. And still further, no one is immune because the disease is new to humans.
So yes, while covid-19 has a 98% survival rate, a widespread outbreak could kill hundreds of thousands of people.
But having that option is better than not having the option at all IMHO.
Here's a few examples stolen from the doc page for the next.jdbc library (https://github.com/seancorfield/next-jdbc):
> clj
Clojure 1.10.1
user=> (require '[next.jdbc :as jdbc])
nil
user=> (def db {:dbtype "h2" :dbname "example"})
#'user/db
user=> (def ds (jdbc/get-datasource db))
#'user/ds
user=> (jdbc/execute! ds ["
create table address (
id int auto_increment primary key,
name varchar(32),
email varchar(255)
)"])
[#:next.jdbc{:update-count 0}]
user=> (jdbc/execute! ds ["
insert into address(name,email)
values('John Smith','[email protected]')"])
[#:next.jdbc{:update-count 1}]
user=> (jdbc/execute! ds ["select * from address"])
[#:ADDRESS{:ID 1, :NAME "John Smith", :EMAIL "[email protected]"}]
That is an example REPL session to setup the library, setup a datasource, create a table, insert a row into the table, and then select from the table. The "user=>" part is the REPL command prompt. Essentially it's one statement for each SQL query executed, and 1 or 2 statements to setup the JDBC datasource. This is tight.You can see the return from the select contains clojure native data-structures. It returns a list of rows, where each row is a map of fieldName to fieldValue. You can now go to town and manipulate lists and maps to your hearts content, convert the payload to HTML or JSON or whatever you like.
(client/get "http://example.com/resources/3" {:accept :json :query-params {"q" "foo, bar"}})
With the response, you can examine response headers, the body, etc.Stolen from the README page for a clojure CSV parsing library (https://github.com/clojure/data.csv)
(defn read-column [reader column-index]
(let [data (read-csv reader)]
(map #(nth % column-index) data)))
(defn sum-second-column [filename]
(with-open [reader (io/reader filename)] ; Read in the CSV file (streaming / lazily)
(->> (read-column reader 1) ; Convert to just the first column of data.
(drop 1) ; Drop the first row (the CSV header)
(map #(Double/parseDouble %)) ; convert each string in this column into double
(reduce + 0)))) ; sum the result
Because it's lazy, this code should work regardless of how large the CSV file.- Immutable data-structures with concise literals for lists, vectors, maps, and sets. Having pure functions and immutable data-structures makes code easier to reason about, easier to test, and thread-safe.
(But clojure doesn't "force" you to be pure. The idea is that you write as much of your code in pure functions as you can, and push the IO and impure parts to the extremities. It's very pragmatic in this way, and you can get real work done.)
- Simple syntax:
While the syntax may be intimidating at first and arithmetic looks weird to untrained eye, once you realize that everything is a function (or a special form that also looks just like a function), the very light syntax and consistency feels amazing/refreshing.
- Macro system:
This is tied to the previous point. Since all your code is technically a "list", and you have something akin to a pre-processor where you have the full library of clojure functions to manipulate data. But in this pre-processing phase, your data is the code. (the code is a list). Now you can dynamically re-arrange or re-write code. Lookup homoiconicty and learn about the kinds of things you can do in macros that can't be done in other languages.
- Capabilitis for general programming, abstractions, code re-use etc.
- Performance is quite good. It's often nearly as fast as java, yet your codebase might be 10x smaller because the language is so expressive.
- Concise/expressiveness. Clojure comes with a nice built-in library with generic functions that you re-use again and again. Give a programmer a few dozen of these functions and it's amazing what can be composed to solve many problems succinctly and elegantly.
- There are surely other benefits, but the last one I'll leave with is hard to explain unless you have felt it before. It's REPL driven development.
Clojure comes with some seriously awesome REPLs (i.e. a shell for interactive tinkering with the language). Other languages may have some form of REPL, but no other language in my experience has come close to the feel you get in a clojure REPL. I can best explain it as a freedom of very light-weight experimentation that you use to write your code. It's a playground for writing functions with very quick feedback to see if your code will work or not. One factor that makes the clojure repl experience so nice ties back again to the succinctness and expressiveness of the language. Typing commands in the repl is painless because it's concise, not a lot to type, and then the feedback is so instant.
Somehow when I write code in python, java, javascript, or other languages, I just don't use their REPL or shell as often. It's just not quite the same as the clojure repl experience.
However, race conditions are still quite possible with goroutines, and many experienced go developers seem to eschew goroutines. It seems like for certain niches go works wonderfully, but I'm not convinced it will become THE next enterprise programming language.
In fact, we may not have another language dominate the enterprise landscape the way java did in the 2000s. It likely will end up being a mix of go, rust, python, java, .net, and node, among others.
That doesn't change the fact that he's produced a great car and seriously innovated in many areas.
I am rooting for them to succeed because I think what they are doing is important and good for humanity. I am hoping Musk does the right thing, and doesn't get in further trouble with the SEC, etc. And I am hoping that the company is able to survive the tumult of the markets -- that is, the costs associated with electric cars and the supply/demand of the market. Sometimes the best technology doesn't win. It happens often. It would be a huge loss if it didn't work out.
Java upgraded to native threads, and then you could have N java threads bound to N kernel threads. This was way better, but had downsides: You're limited on how many threads you can spawn, you need a threadpool to help manage, and any long-running tasks could effectively deplete your thread-pool.
With Loom, now you have M green threads mapped to N kernel threads. These green threads are way cheaper to spawn, so you could have thousands (millions even?) of green threads. Blocking calls won't tie up a kernel thread. So if you have many long-running IO tasks, they aren't going to waste a kernel thread and have it sit around idle waiting on IO. This is similar to async libraries, but without the mental overhead. You should be able to just code synchronously and the JVM will take care of the rest.