beginners

doojin 2026-04-03T09:30:24.815099Z

I think I will read the pragmatic programmer by david thomas and be done with learning "general" software engineering from books. The book mentions learning a programming language every year, but I personally wouldn't learn a new programming langauge every year just to learn new things. If you are going to specialize in emacs, then it makes sense to learn elisp. If I'm going to learn a language, I better learn a language that I'm going to use very often. I don't advocate learning a language you are not going to use. Learning a language you know that you are not going to use regularly is a waste of time.

seancorfield 2026-04-03T13:36:25.860209Z

The reason they recommend that is that different languages teach you different things, different approaches to solving problems, different ways to think about problems, etc. There's not much point in learning similar languages, but there's a lot of value in learning very different languages. I think "every year" is a very aggressive ideal. I think "every couple of years" is reasonable. It's why I've learned Standard ML, Ruby, Python, Elm, Kotlin, and a few others, over the same period of time I've been using Clojure in production (and some Scala early on). It's also why I recommend the "Seven Languages in Seven Weeks" book: it's a great way to get a tour of some very different ideas about solving problems.

👍 1
👀 1
2026-04-03T13:40:35.344489Z

I learned clojure and don't use it for work, but my thinking and design is better for it. It taught me a lot about programming and basically rewired my brain because of how different it is from the traditional oop that I learned in school and getting all the popular books

2026-04-03T13:41:31.768779Z

Not learning it because I don't use it regularly would have been doing myself a huge disservice

👍 1
👍🏻 1
👍🏼 1
💯 3
doojin 2026-04-04T05:04:45.887749Z

I regret having learned haskell because its type system couples nominal types with functions and promotes place-oriented programming at the type level.

data Type a b c d e f = Constructor1 (Int a) | Constructor2 (String b) | ...
This is place-oriented programming at the type level. Haskell type system is also needlessly complex. In general, I want to focus on simple languages that avoid place-oriented programming and avoid coupling between (nominal) types and functions. I want to be efficient with my time by learning from the best. Static typing with robust type inference, structural types, polymorphism, and other features may be able to decouple (nominal) types from functions. Also, if you are already busy learning languages that you are going to use regularly, then don't sacrifice recovery and other aspects of life for education by learning a separate educational language. I also want to be efficient with time. Human lifespan is short. Personally, because I will be busy learning languages that I will use regularly, I don't want to sacrifice my life by adding an educational programming language on top of everything.

doojin 2026-04-04T07:03:50.559649Z

I don't recommend learning educational programming languages at the expense of other aspects of your life. If you have spare time, then it's okay to learn an educational programming language. Personally, I have seen enough programming paradigms that learning a new programming language that I'm not going to use doesn't feel like a good use of my time. If I'm going to learn something just for education, it better have a substantial return on investment, considering my short human lifespan.

2026-04-04T13:01:31.638629Z

If you've been exposed to a variety of programming paradigms, I think you've already followed the advice. The point is breadth, and to encourage developers not to call it "done" because they've learned a single programming language. Even within the same paradigm, different languages have different philosophies and tradeoffs. When you expand to other paradigms, you've added new thinking tools to your mental toolbox. You've done this and evaluated what you've learned. Sounds like a "pragmatic" approach to me

Anthony Franco 2026-04-03T19:02:07.737189Z

I know fp style prefers plain return values, so when designing API's, what's a good way to deal with errors if not exceptions?

Ramin Soltanzadeh 2026-04-03T19:07:57.968639Z

I'm not sure that's true. Clojurists probably prefer simpler types, but a lot of FPers would probably advocate for something like a Result type (Result monad, even).

Anthony Franco 2026-04-03T19:10:40.461839Z

At that point though, shouldn't I just use the exceptions system?

Ramin Soltanzadeh 2026-04-03T19:11:14.965529Z

I'm not sure I have a good answer. I'm curious to see what others think.

seancorfield 2026-04-03T19:12:34.506969Z

Exceptions are fine for "exceptional" failures, i.e., unexpected situations. But not for flow of control stuff.

seancorfield 2026-04-03T19:13:02.782079Z

And you can't avoid them when doing interop.

seancorfield 2026-04-03T19:14:53.022629Z

For errors in general it depends what failure information you need to return

Ramin Soltanzadeh 2026-04-03T19:15:51.780209Z

Having built integrations for a handful of third party systems, I'd say unexpected situations are to be expected! 😉

seancorfield 2026-04-03T19:16:06.376019Z

A generic failure (or success), maybe returning nil is sufficient. Clojure's nil-punning really helps here.

Anthony Franco 2026-04-03T19:17:18.639729Z

An HTTP 500 failure, would we consider that "exceptional?" I lean toward yes

seancorfield 2026-04-03T19:17:48.073979Z

If you need more details from a failure, or where the success value can be nil, return a map with either :error or :value can be a good compromise

seancorfield 2026-04-03T19:18:53.508049Z

Sure, 500 might be unexpected. But for some 50x you might want to retry with backoff

seancorfield 2026-04-03T19:20:03.454279Z

So, "it depends" in most cases

seancorfield 2026-04-03T19:37:37.092269Z

We don't generally use monads in Clojure because monads really need a static type system to make sense. There's a Contrib library algo.monads but it is not used much. Monadic code without type-based dispatch is a bit painful to write -- and monadic patterns tend to spread into surrounding code which can be ugly in Clojure. The map with :error / :value is about as close as we get there but it fits well with Clojure's nil-punning:

(let [{:keys [error value]} (call-the-fn arg1 :arg2 "arg3")]
  (if error
    ... handle the error ...
    ... handle the value ...))
Similarly, if you have a simple nil (error) / non-`nil` (value) return:
(if-let [result (call-the-fn arg1 :arg2 "arg3")]
  ... handle the result ...
  ... handle generic failure ...)

seancorfield 2026-04-03T19:39:12.393549Z

And those combine well with exceptions since you can wrap the call with (try ... (catch Exception e ... report it ...)) and either return nil from the catch or some :error (ex-message e) map value etc.

2026-04-03T19:58:08.168259Z

For an API, however you want to model it, I like to group things into: 1. Retryable errors The call is valid but some issue you know can succeed on retry occurred. Clients should retry and only alert yourself if multiple retries fail. 2. NonRetryable errors The call is valid, but some issue you know can't succeed on retry occurred. Immediately alert yourself and clients should not retry as that's futile. 3. Client caused errors The call was invalid, or permissions are missing, or some client managed configuration is incorrect, etc. The client isn't expected to retry, but it's on them to fix. You don't alert yourself on those, but you can check on them weekly.

technomancy 2026-04-03T20:14:38.899859Z

monadic failure types don't really need static typing, but they do need pattern matching to be ergonomic. (they're used very effectively in Erlang without static types)

technomancy 2026-04-03T20:15:09.023969Z

since we don't have pattern matching in the core language, you can't expect that the users of your library will have it, so you can't really design your API around failure types without making it a tremendous pain in the ass to use

2026-04-03T22:37:37.591219Z

"railway-oriented programming" (or is it "railroad-oriented..."?)

seancorfield 2026-04-03T23:13:15.721879Z

See https://fsharpforfunandprofit.com/posts/recipe-part2/ but also https://fsharpforfunandprofit.com/posts/against-railway-oriented-programming/ (linked from the first as a follow-up)

Harold 2026-04-04T04:05:46.182139Z

I have also seen this: https://github.com/cognitect-labs/anomalies --- perhaps interesting to consider; I don't think I'd advocate for it in many situations, but it's another line of thinking in this area, for sure.

Ben Sless 2026-04-04T06:49:14.347489Z

You can do what ring async does and cps transform your code with callbacks, the continuation and the error continuation Then you don't raise an exception, just call the error continuation withsome value to return to the top level caller

daveliepmann 2026-04-04T07:07:50.573539Z

it's "it depends" all the way down https://www.daveliepmann.com/articles/idiomatic-clojure-errors.html

😁 1