Trying new programming languages helped me grow as a software engineer
cichocinski.dev
cichocinski.dev
From having built trading systems in c++, I ended up building, over about a year or so:
- A django (python / js) website. It integrated with both Android (java) and iOS (objective-C) apps that could read QR codes and ask the server stuff. It also made me interact with the app stores for the first time. It was also the first time I did anything on the web, including figuring out how pages are laid out. Bought a MacBook for the first time too.
- A consumer iOS app written in Swift. New language again. Went native, ran into a lot of constraint errors that eventually taught me a lot.
- A trading system in c++ that didn't use STL.
I remember at the beginning of each new language/framework thinking "OMG how do I do this". And yet it's not that bad once you've done it once or twice. I reckon it's a lot easier than learning a new natural language (German, Mandarin).
The thing it really gives you is a low opportunity cost. If you have some task, and the evidence points towards some specific framework/language being a good choice, hopefully you won't resist it due to internal anxiety about starting over again.
I knew various people who needed an MVP made for their startup, and with a little bit of familiarity people will let you do things you haven't done before.
I know people I'd bring in to help no matter what the framework is because i trust them. And some people would do the same with me if they needed help.
Some things that are taken as best practices are bad practices in other langauges and vice versa.
For example, lots of Java devs are against early and multiple returns to the point of absurdity, while in functional languages this is idiomatic and no problem at all.
Using different languages lets you see how different approaches work out in practice, so you can get a better view about which approaches work and which approaches are just cargo-cult nonsense.
We see this in C#. Has a ridiculous amount of features outside default OOP and continues to get more baseline, even has great interop with several other languages. But culture changes very, very slowly. Large scale adoption tends to happen only when some major framework insists on using a certain paradigm.
C# has been trending towards introducing more things from the functional paradigm in particular, but just getting away from typical OOP patterns and replacing them with something more on the spot or functional isn't amazingly well received. The main thing which seems very well adopted is LINQ, and that's primarily due to Entity Framework and Microsoft pushing LINQ-adoption hard. Most places still program in C# as if it were Java with Lombok built-in.
And that's where I'm getting at mostly. New paradigms and languages are cool, but historically it's been difficult for them to get mass adoption unless a big name is pushing for it hard. Convergence has been the name of the game for a while now, rather than divergence.
Works fine on non-Windows systems (well, Linux and macOS at least) IME and isn't several years behind.
Also I think you are giving nullable ref types too much credit
Having the type system be able to tell you that you may have a null reference exception in a complicated code base is magic. I've inherited bad code bases and refactored them to use nullable references and watched lots of bugs fall out.
That said, I get where you are coming from - .net framework is pretty much just windows, and there a large number of libraries/apps that are Windows first, other platforms second.
If you do end up needing to do Windows dev, though, I'd recommend it, same as if I were doing Apple Dev I'd recommend Objective C/Swift, or recommend you brush up on bash/zsh/fish, C and maybe Xorg/Systemd for linux.
You can use other languages on all these platforms but the 'native' toolkit tends to enjoy better interop and have better support.
Edit: The other reason Unity developers are careful around new language features is avoiding memory allocations. When you write C# like it was Java it's more straightforward to see where allocations happen but it's all too easy to accidentally write allocations in your main loop if you use everything the language has to offer.
That aside. Your point around ecosystems is interesting to me. As a wannabee coder I'm always trying new things. Most of my code was in PowerShell although I did do a bit of C++ and Delphi in school.
I have been dabbling with dotnet core (C#) and Python the last couple of years.
I tried to get into Java a bit, but to be frank the learning curve to go from start to consuming a REST service was just way too hard. I couldn't understand the difference between when I should be using JavaEE, JavaSE, spring boot, spring framework etc. I gave up after two days.
In contrast Python is just so easy. And if I want something more robust I'll rather go to dotnet.
Go is on my list as it seems to offer a lot of what I want, and although there are some apparently weird things in it I think i can be productive with it for some basic business logic within a couple of days.
Google library you need "java http client"
https://openjdk.org/groups/net/httpclient/intro.html - it doesn't look complicated.
First example is using "reactive-streams", you don't have to use that syntax if you aren't familiar (same as in Python there are new features, which beginners may learn later), other examples strait forward.
If library you need doesn't exist in standard library, download jar, add it your `classpath` and use.
Skip any tutorials which are using Spring (unless you want to learn Spring). If you want to write more advanced Java code, learn about [Maven](https://maven.apache.org/)
Same thing about JavaEE/JavaSE, if you google about it, you will find that you may use any, there are different libraries included in installation, it doesn't mean you can't add them later.
In Python a library like requests is a thin abstraction over http calls it is very easy to understand what is going on and get started.
In Java, there is a tendency to wrap everything so it fits with a framework and is super generic and configurable. For example look at the Feign library.
Each approach has its pro's and cons, but knowing both of them good as it allows you to pick the right solution for different use cases.
About getting into java: i'd recommend using Spring Boot. I provides a low abstraction http client (RestTemplate) that allows for straightforward calls to REST services.
Which functional language are you talking about? Of the two functional languages I know, Elm can only have a single return statement. Scala encourages a single return.
To make an example the following expression can be considered to have two returns:
max x y =
if x > y
then x
else y
The Java equivalent is int max(int x,int y) {
if (x>y)
return x;
else
return y;
}
Some people abhor the idea of having multiple returns in a method, and say that you should write it like int max(int x, int y) {
int result;
if (x>y)
result = x;
else
result = y;
return result;
}
(Disclaimer: Contrived and buggy example as I am on mobile) int max(int x,int y) {
if (x>y)
return x;
else
return y;
}
I just looks completely fine to me, but colleagues would complain about it in reviews and I just don't understant the problem at all.
The variant with the extra result variable looks just wierd to me, and I have seen much uglier code written by people desperately avoiding early/multiple returns.In imperative programming, a bunch of nested conditionals can easily have incomplete coverage of possible program states, and it can be easy to overlook problems if you have a mixture of side-effect branches and early returns. I think some people struggle with this more than others, and it can flummox them almost like goto-laden spaghetti code.
Of course, there are other areas where similar errors can occur in different languages, i.e. in exception-handling or pattern-matching constructs. There are many different coding styles which can make these control-flow structures easier or harder to debug. But, I think there can also be a lot of "cargo culting" where zombie rules of thumb continue beyond when they were really particularly helpful.
The fact everything is context-dependent is the basis of all these discussions to begin with. We severely lack evidence and most rules are hearsay taken as gospel.
I initially fell in love with the Rust convention of simply bubbling up errors throughout a function but I've gradually realized that leads to a poor API. [1]
I'm currently learning c++ for a personal project. I want to use a library with an API that's sufficiently templated it'll be easier to write a c++ shim than directly generating bindings. So far I'm at the stage where I'm constantly thinking Rust does things better. Hopefully later on I'll realize why the different tradeoffs c++ makes make sense. (One example would be how generic code is typechecked).
[1]: If you want to be able to simply propagate up errors with the `?` operator you need to implement automatic conversations from your dependencies' errors to your error type. But that means your API will have a single error type for every dependency error type - say MyError::Io(io::Error) - which is the wrong level of specificity. I now think you should either expose only a string message your user can just show their end user or an error that's much more specific so they can write code that intelligently responds to it. Something like MyError::ReadConfigFailed(io::Error)
[1]: https://doc.rust-lang.org/rust-by-example/error/multiple_err...
> I now think you should either expose only a string message your user can just show their end user or an error that's much more specific so they can write code that intelligently responds to it.
a boxed trait object is the main way to implement an error that merely conveys a string message.
Edit: Realized it might not have been clear that "boxed error" and "all you get are a string message" refer to the same thing. While you can try and convert a trait object to a specific type at runtime it's a crude and ugly approach that suggests that trait object should have been something else to start with. The main thing you do with a Box<dyn Error> is ask it for a Display message.
Learn concepts, obtain foundational computer science knowledge, write complex projects
not just get familiar with somebody's interpretations that they implemented as a language.
Languages that I used besides my job language barely gave me anything, in most cases they made me appreciate more for sane environment (package managers, IDEs, debuggers, strong standard library)
edit. just to be clear:
I don't see value in learning C#+Java+PHP, but C#+Erlang? yea
I do see value in learning Rust after C
but still - concepts are more important, you don't have to learn langs in order to be familiar with concepts
Learn software engineering - it's broad as hell and you can easily do that using one language
I'm saying that programming languages aren't they only way to get familiar with concepts
Or Prolog and logic-based declarative programming.
Or any GC'd language.
Or a Lisp and its metaprogramming.
I daresay these ideas would be very difficult to see the practical use of, or "impractical" beauty of, without touching the languages.
Well yeah learning a wide variety of languages is a good way to do that. People write new languages because they want to implement new concepts.
Well, depends what concepts are you talking about.
Computer science is huge as hell, concepts that you may learn from programming languages are just subset.
There's a lot of interesting and hard stuff that does not touch those (I'm not focusing on some domain-specific-languages that nobody know about)
Want to learn concurrency? Try a concurrent language.
Parallelism? Learn a parallel language.
Data flow? Learn a data flow language.
Pattern matching? Learn a language with pattern matching.
Etc for a thousand useful subjects to learn.
These are great ways to learn so many software engineering concepts. Use other techniques to learn other things sure.
eg async await
You are just learning tool like Excel
[1]: https://pilabor.com/blog/2021/05/learn-concepts-not-framewor...
You kinda do. Concepts are often exclusive to languages. And often concepts can't be internalized without practice of that lang.
Your own examples in your edit literally contradict what you say as you chose langs with extremely divergent concepts. C# and Erlang, Rust and C. You missed haskell and lisp.
How so?
You can learn and understand concepts implemented in Rust without learning Rust
If the only tool available is rust, then one must learn rust to truly learn the concepts related to it.
When the compiler stops yelling at you all the time, you'll get your feedback that you've actually learned those lessons.
https://www.semanticscholar.org/paper/Functional-C-Hartel-Mu...
It's hard to write a declarative querying library in C. SQL is not an ideal language, but as a way of expressing intent rather than a physical query plan, it's a long way away from C.
It's hard to appreciate the power of interactive debugger REPLs like binding.pry or JS's debugger without experiencing them. A C++ debugger, even a Java debugger, is a pale imitation, and it's not easy to simulate the experience without building a dynamic language inside the static language.
If all you've ever known is statically typed languages, you can suffer from a myopic parochialism with respect to dynamic typing. IMO the most popular statically typed languages only work well because they have runtime polymorphism holes in their type systems. More powerful statically typed programming languages tend to grow ever more esoteric typing constructs to enable static types to follow dynamic control and data flow - there's an inherent tension there and if language complexity isn't carefully managed, it becomes harder to express intent and error messages get more vague and cryptic.
Algebraic data types and pattern matching tends to be under-appreciated if one is schooled in object-oriented languages. Object orientation has a big hammer for switching control flow based on data values, dynamic dispatch, and architectures tend to leverage dynamic dispatch where they would be much better off with switch statements. If the set of variants is known up front, you're probably much better off with sum types and pattern matching than with interfaces, abstract classes and virtual methods.
This probably more than anything else comes down the programmer style. I had to use Jupiter notebook to help my wife learn programming Python and it was horrific. I could never imagine that real work was done in this way, but I know many good programmers that swear by it. I suppose that when I need something like a REPL, I tend to use my debugger instead.
1. When you need to explore behavior.
For example, C# has slicing syntax now. MyString[..15]. What happens when the string is only 8 characters long? Lets hit the REPL and find out.
2. Poking at systems using the same code as the rest of the codebase. For example, RoR. Being able to load up the RoR framework and poke at the ActiveRecord objects can be hugely helpful for tracking down an issue.
REPLs without the debugger bit aren't nearly as good. That's why I mentioned debugger for JS and binding.pry (implicitly, for Ruby).
binding.pry pops you into a terminal REPL with the local binding in scope, just like the eval function of your debugger; except you can start writing loops, define new variables, and more rarely, new functions, right there.
When writing code with a test-first model, you set up the data fixture then pop a binding.pry in the implementation, right where you expect to start implementing it, and run the test. You can then implement the logic in the middle of the test execution - in the middle of your debugger session - and because the REPL is so expressive, you can do a lot. You then copy & paste out those lines and save them in the implementation logic, and shuffle the binding.pry on to the next spot.
Or in a hobby project, you can fly by the seat of your pants, and write code live on the server. There's a remote version of Pry if multiple workers sharing a terminal start fighting each other to own keystrokes.
I have a few reasons to work in (not just learn) close languages. The languages might use roughly similar concepts and syntax and solve similar problems (think, C++, C#, PHP, Java, Go), yet their community, practices, tooling, frameworks are wildly different.
If as a C++ developer in 2005 you never experienced how smooth debugging and profiling could be in Java, how fast things could compile, how easy it was to drop a jar and deploy to your server farm, you were missing out.
Similarly, if as a Go developer you don’t know how quickly you can install WordPress, write a 200 line PHP plugin, and get an entire business off the ground, you are missing out.
Most transformational for me was to be a Common Lisp developer, and find the language that allowed the ideas and concepts in my brain to be turned into prototype at the speed of thought. It’s a challenging language to use in the real world, but it really brought the point home that a language is just a vessel for ideas.
Languages for me are just tools, like screwdrivers. I use / used many and do not get hung up. And no I do not learn languages just for the fuck of it. Only if I see some positive ROI.
Charlie Watts wasn't just into jazz, he was into all music. George Harrison understood that jazz could deepen him as a musician, but found Indian music.
If one wants to write a language that is truly original, that might actually influence the future, not just smearing around familiar weightings for features found useful "in the trades", then one wants to have some grasp of languages representative of every language in use. Then, only proceed with a compelling vision of what's missing.
For the rest of us, being a language junkie can be a problem. Without being judgmental, it is possible for a person to be too promiscuous. Same with languages. I wish I could just stick to Haskell and get work done.
Not hobby level, but client work level.
They did not really help me to become a better software engineer.
What did?
Being able to understand the business of the customer and create software that helped them do that easier.
That’s why today I tell junior programmers that almost everything we do these days could be accomplished with Bash, text files to hold the data and static html files.
Don’t focus on the language, focus on the business case.
Sure some languages are more efficient than others but all those do is help you build a solution faster. And languages are similar for the most part - so that helps you learn new languages faster.
You need to learn languages outside of this family to see the benefit. But the benefit won't be in your ability to do "business". It will be exclusively improvements in programming unrelated to "business programming"
>That’s why today I tell junior programmers that almost everything we do these days could be accomplished with Bash, text files to hold the data and static html files.
Why? Junior programmers are aware of this already. And get this: junior programmers are even aware that everything can be accomplished with assembly language. They are also aware why things aren't done in assembly, typically.
What makes you so confident of that?
I took a look at eric4smith's website. His current language of choice is Elixir, which is not in fact a member of "the Algol family of languages with OOP".
Maybe a brief bit of research before throwing stones next time?
Second of all, no stones were thrown. I made an educated guess. Don't assume I'm "throwing stones", this is not an attack. Just a statement of my thoughts.
I would say the "throwing stones" comment was waay more accusatory and presumptuous then mine, and would indeed require verification before pointing a finger. But to each his own. Yes... I certainly admit I'm making an assumption about the 9 programming languages he learned.
I don't consider that software 'engineering' necessarily.
Yes to be a more useful employee or more successful freelancer etc understanding the business case is absolutely an essential skill. But the 'engineering' begins once the problem has been defined; how do you actually implement the vision.
Imagine it's civil engineering, and we're talking about building a bridge.
To what extend does a bridge engineer need to understand regional trading patterns, and what bridge location and size would give maximum economic benefit?
To me that's a separate discipline to building actual bridges, and no amount of practice with different bridge designs or methodologies is going to be relevant to that, and vice versa.
You should absolutely understand what the purpose of the bridge is, what sort of things will be transported across it (trains? pipelines? weights? hazardous materials?), what sort of volume it will handle, what are the goals you're looking to solve with the bridge, what are acceptable and unacceptable tradeoffs? where are OK connection points, and where won't work (and how does it tie into the broader regional/municipal transit story)? I could go on...
you give most software engineers a bridge building problem and they'll build the bay bridge when all they needed was a simple suspension bridge for foot traffic.
Care to give me an example in the physical world a case where overnight that foot bridge overnight suddenly becomes a highway off-ramp? Oh and suddenly we’ve realized that our highway motorists all need a place to park within walking distance of the hotel right nearby that we built last night? Also, there’s going to be spies looking for weaknesses in your bridge, robbers and invading armies looking to loot and pillage on a daily basis.
Of course engineers need to understand all the requirements for the bridge.
But coming up with those requirements, ie designing the economic/business case, is a separate discipline. As someone said, some of that may also be engineering; economic engineering, traffic engineering, social, political etc, but it is not bridge engineering.
> "Being able to understand the business of the customer and create software that helped them do that easier." I don't consider that software 'engineering' necessarily.
Economical and efficient processes and systems to help someone figure out how to do their business would seem to fall under that definition, no?. I’m curious if you did attend engineering school because part of my curriculum in being part of an accredited engineering curriculum was classes that explored what it’s like being an engineer. You have to prioritize public safety first, integrity of being an engineer second, making sure the business succeeds and providing your advice third.
I disagree with OP in that learning languages does help you be a better engineer. It broadens your exposures to different ideas and cost effective ways to write code to solve those problems so that you can make better recommendations. That’s equally as important (perhaps even more in the beginning of a career) to being a well rounded engineer as is understanding business needs and matters enough that dismissing it as “I could do the same thing in Bash” kind of misses the forest through the trees because technology choices to impact cost to build, test, maintenance, ability to hire talent, cost to train people, etc etc.
More to the point though, software is not like a bridge. With a bridge, the high-level requirements are easily defined in a way that any layman can understand (weight limits, lane count, etc). Failure of a bridge is also viscerally obvious. Obviously there may be extreme engineering challenges in actually building a given bridge, but at the end of the day no one involved ever runs a serious risk of losing their handle on the big picture.
Software, by contrast, is arbitrary logic full of leaky abstractions and unknown touchpoints. Even if we narrow down to the most vanilla business CRUD app, precise requirements can be frustratingly elusive. No matter how well thought out a project, there are always discoveries at build time of any non-trivial project which significantly impact the requirements.
For this reason, I consider it a core responsibility for senior engineers to help define requirements and not just be a passive recipient of them. Martin Fowler captures my viewpoint more eloquently: https://www.youtube.com/watch?v=4E3xfR6IBII
- both C and C++
- Some form of lisp. I could see julia taking this spot.
- Haskell
- SQL
There are obviously more that are worth learning for industrial use. Learning the above will give a solid understanding of how every other language works though, even if there's different semantics for a particular language.
When I was really into learning new languages, I think it was because I didn't have a good sense of how to do more challenging things.
At some point, I settled down and got more into projects that really require more domain knowledge. Writing an external merge sort, interpreter & compiler, a gameboy emulator, a disk persisted b+tree, ray tracing & physically based rendering, etc.
Those types of problems are what really pushed my boundaries and made me think more about how to solve problems. In the case of ray tracing & pbr, it forced me to learn some calculus, linear algebra, and statistics, which has been an incredibly rewarding experience.
It can be fun to imagine what solving a problem looks like in a language, because it may have some qualities that play to the strengths of a solution. At the end of the day, the languages are just tools people have come up with. Learning the quirks of C++ becomes less fun when you start to realize that it is the way it is because of how history unfolded rather than because of some interesting concept.
(edit: hn list formatting)
Choosing a language to learn depends on, among many more aspects:
* What style do you prefer. Return codes vs exceptions, small lambdas vs elaborate functions...
* What paradigms you find useful (procedural, functional)
* how do you think, rather mathematical, more in a state/execution way or more like cooking (recipes),...
* what applications do you like? Frontend to a store, embedded/industrial, mission critical,...
* simply taste. I hate ruby and love python. For a lot of people it is the other way around.
* do you love optimizing? For what memory, speed?
* Do you prioritize or advocate readability, maintainability, re-use?
In the end you have to choose your battles, you can't master all paradigms and styles in one lifetime.
Without this, there’s a risk that the “learning” is just tunneling in on your own pre-existing prejudices (insert hammer-nail metaphor here).
I did a linguistics degree as an undergrad, and one of the requirements was coursework/proficiency in two different foreign languages. Unless you planned on actually using both of them, you were strongly encouraged to choose very different languages, rather than (say) Spanish and Italian.
Having learnt a Romance language, I took a year of intensive Japanese (the tones in Chinese were a bridge too far). It was a nightmarish amount of memorization and I juste eked out a passing grade, but the exposure to totally different writing, grammatical, and honorific systems was also really fascinating.
"Worth learning" is a subjective phrase, so it'll naturally be different for everyone. My list above was to give a list of languages that would cover as much of the spectrum as possible.
> would you also make such a list for normal languages? English, Arabic and Chinese?
No, for starters because I'm terrible with spoken languages and rather ignorant. But to make it a better comparison, I would say "learn one romance language" (or maybe just latin), or "learn one language written right-to-left".
If you were taking a painting class, it would be reasonable for the teacher to select some techniques to teach over others, or to showcase some select master painters to try and cover as much ground as possible in the course.
The idea being that you have a finite amount of time. Do you want to spend it getting a wealth of very similar experiences, or a broad appreciation.
Using your python vs ruby example: I think the more you learn, the smaller the differences between python and ruby are. There may be a profound difference on the experience of using either based on the users preferences and choices the languages made. But from a conceptual point of view, they're very close to each other.
That's a language any person can learn :p
Why? Short answer - Because it doesn't have for loops.
It forces you to think about data in another way. It is like origami for programming. You can't brute force it, you have to fold your way to the result.
Knowing 27 languages but only having worked on say small green-field web apps is still a very limited experience.
To those here who would like to have some guidance diving into other languages I can recommend these two books:
Bruce Tate: Seven Languages in Seven Weeks https://www.amazon.com/Seven-Languages-Weeks-Programming-Pro...
Bruce Tate: Seven More Languages in Seven Weeks https://www.amazon.com/Seven-More-Languages-Weeks-Shaping/dp...
You can work through them in <4 months, and then we can talk again whether you found it useful (I did).
C having better support for pointers makes it nearer to how the processor works, compared to other languages.
Java is almost entirely pointers to heap allocations, yet I don't think anyone would argue that Java is close to how the processor works.
I also don't think that the C virtual machine is all that close to how machines actually work any more.
C semantics do not work "directly on the hardware" but instead on an abstract machine that is then converted to the actual hardware.
It most often comes up when talking about undefined behavior and pointer behavior.
Some assorted reading, mostly in the context of Rust and C:
https://blog.regehr.org/archives/213
https://raphlinus.github.io/programming/rust/2018/08/17/unde...
https://www.ralfj.de/blog/2018/07/24/pointers-and-bytes.html
https://www.ralfj.de/blog/2017/06/06/MIR-semantics.html
[1]: https://stackoverflow.com/questions/53100198/what-is-the-pre...
Assembly once worked directly on the hardware. On modern machines it doesn't.
Means I can at least dig through some old codebases and understand what's going on.
Lots of higher level languages also have C bindings, so at least a couple of times in my career I've ended up writing short C modules for speed critical bits of code.
The pain of manual memory management also gave me much more of an appreciation of its pitfalls and alternatives.
If I had to pick one language to code in for the rest of my life, it would be C.
The Rust people are working hard to support everything that C does, so in the future C and asm will probably not have a monopoly there. But this is still not our reality.
1 - Of course, not a PC software stack, and not on any ARM available today.
TypeError: Cannot read properties of undefined (reading 'reduce')
at getTweets (/myapp/build/index.js:898:22)
at runMicrotasks (<anonymous>)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at loader5 (/myapp/build/index.js:991:48)
at Object.callRouteLoader (/myapp/node_modules/@remix-run/server runtime/dist/data.js:77:14)I've learned Elixir, Scala, F#, and Common Lisp on the side in the past year or so. I like Elixir and CL to the point where they're typically my go-to for personal projects. I liked Scala and F# a lot but I don't reach for them so often; it'd be nice to work with them.
The worst part about learning (and liking!) new languages is that at work, we're pretty firmly in Go. IMO, while Go is nice from a minimalism and resource utilization standpoint, it's simply not fun to write after using much more expressive languages.
It feels like the language was designed to be as good as any other language you've learned.
It's definitely a blessing and a curse. What I think I'd say about Go is that it's a simple "day 1" language and a complicated "day 2" language. Speaking strictly from an expressivity and language semantics standpoint, as its runtime and standard toolset are simply great, it's just filled with so many head scratchers when you dig into what's going on and why.
Like the go modules import compatibility rules forcing you to create new vX versions of modules. It's not that it's unsound, it's just unlike everything else out there, and so you can't take your knowledge from other packages managers and apply them to Go. Maybe that expands the mind a bit, but in my experience it just confuses teams and forces them to sometimes go back and revert a major version bump because it's easier to just do that.
This resonates. I very much dislike Go's error handling model. Its overly simple and leads to exorbitant
if err != nil
checks all over the place. It clutters the actual business code and remains a clunky, leaky abstraction.I think Go is a language designed for engineers who don't care to learn about more elegant solutions that come with more intricate semantics (Option, Either, Try etc).
It's get the job done inspiration is sloppy and poorly conceived.
I really don't like hopping into a codebase and seeing a 27-line function (uncommented, of course) with 21 of the lines being `if err != nil` checks
Have a parameter for an array of users? Call it ‘u’. Want to have a variable for a config object? Obviously call it ‘c’.
‘Idiomatic’ go code is bad code.
I'm mostly saying this because every company I worked for implemented similar antipatterns. It's like the human brain subconsciously leads people to make similar bad decisions regardless of the programming language.
On the other hand, learning different languages makes you more gritty. If you can troubleshoot code in half a dozen languages, chances are you can figure out the 7th.
In the last few years I’ve interviewed over 100 graduating Computer Science students from very good schools.
One of the questions I asked was, “Did you learn your first programming language as part of your college curriculum or before?”
Almost all of them answered they learned their first programming language in college.
I can’t even fathom it. I learned BASIC when I was 10. Logo when I was 12. Assembly when I was 13. C when I was 16 and C++ when I was 17.
This was a long time ago of course - I can’t believe how much things have changed with a population self-selected for being interested in computers.
Other languages might feel more like a "single language" because they are more domain-specific, and there are established basic libraries that are widely used, so moving from one code base to another is more fluid.
Not to mention that learning different languages can be quite valuable for one’s career, as it is another skill one can employ for practical purposes and for distinguishing oneself among peers.
I mean, I didn’t learn that much from learning FP that didn’t already know from reading Uncle Bob’s clean code and style guides. In fact I think for awhile it just made me a pretentious clown who wouldn’t stop calling things monads.
Furthermore, haven’t most languages today pretty much converged around the “imperative++” feature set:
Language-level async/await, object-oriented, garbage-collected, w/ a package manager for other people’s code. Whatever nuance between how Kotlin does abstract classes vs Typescript I can certainly learn on the job, for instance.
I don’t disagree that there is value in learning two or three languages but beyond that the marginal utility drops off quite sharply in my experience.
> Not to mention that learning different languages can be quite valuable for one’s career, as it is another skill one can employ for practical purposes and for distinguishing oneself among peers.
I mean if it comes right down to it, a decision between two otherwise identical candidates, hire the person w/ language experience. But we probably agree that theory knowledge precedes language familiarly in importance, always. Which is largely my argument for why the title thesis is probably inaccurate.
If anything, functional programming has absolutely won.
Support for first-class and higher order functions, anonymous functions and so in are absolute must-haves for any modern language.
These features have become so bread and butter that people will not even think about them as functional programming but these were the main features that functional programming languages pioneered.
Now, PURE functional programming like in Haskell is not mainstream. It is a testament to the success of functional programming that these days we mostly think about these extreme examples when we talk about functional programming.
If anything, it is object oriented features that are becoming optional. At least the class based approach is starting to decline for good with many new languages explicitly not implementing them.
Also, I strongly disagree that all programming languages are starting to converge. It seems you just picked languages that are very similar which I agree, will not expand your mind much.
Learning languages like Common Lisp, APL, Forth, Prolog, Haskell and so on though will will greatly expand your understanding of programming.
No no, I picked languages that have jobs waiting in industry. 3 Billion devices run on a language that literally requires everything to be an object ;) I haven’t seen too many elm/elixir job postings, but rest assured when they start cropping up with the frequency of say JavaScript or C#, I’ll be dusting off my notes from previous side projects. I’ve also done Prolog, I’ve also done Haskell, I’ve also done Scala. Far more important than being able to write Haskellish code for my typescript backend is the ability to write idiomatic Typescript code with good patterns. And the whole discussion here is not whether you should check out FP (you should) but whether learning extra languages is more important than learning patterns in software design (ie canonical Martin Fowler, GoF, Uncle Bob). There are absolutely useful models and perspectives to be found in FP, but I haven’t found them to be stuff that I use every day. If every imperative language is shipping with first-class functions (which I personally don’t think is as dazzling a notion as it sounds—even C has shipped with function pointers since the days of old) and all the jobs are in imperative languages, it seems pretty practical to focus on learning patterns before learning FP. Respectfully.
For instance using more functional language convinced me of the vertus of immutability.
Pattern won’t do that
I’m pretty sure Clean Code by Uncle Bob has a section on why you should use const to achieve immutable variables. In any case, good style guides inevitably are preaching this as well, as are good senior engineers.
All in one’s first language (human), without needing to understand lambda calculus, or monads to run a hello world.
Doesn't go far enough.
Firstly, it's just advice. I might like it, but it doesn't mean I can get my teammates to do it.
Secondly, if I want to do it, I have to get it right, any mistakes I make are on me.
Thirdly, const is not enough. You'll probably end up with immutable pointers to mutable data, rather than any kind of referential transparency.
A language which helps you get immutability right is a world of difference. It's like the difference between a memory-safe language vs just using C (and a C textbook which recommends that you write memory-safe code)
I mean I understood how to get immutability right before I ever picked up an FP language, but I can see how this is a flexible point though. The virtues of immutability really don’t take a long time to extol, and they’re a concept you’re introduced to immediately in MyFirstFPLang™.
Having no way to mutate my variable in List or Elm or scala … and seeing first hand that a lot whole class of bug disappeared, is different.
But we need both ! Absolutely both. And I will probably never code again in scala or lisp. That was fun. I learned a few things ( scala taught me how functional codebase can be unreadable as well )
Well, it certainly does not live up to the standards of languages, which those people would call "real programming languages". So while they are doing a true scotchman thing there, there is also some truth at the core of it. If we remember JS how it was a decade or more ago, and what JS today still keeps downwards-compatibility with, it is easy to see, how the very foundations are flawed, to say the least. Self-respecting computer scientists, professors or not, will most likely not accept those shaky foundations as something worthy.
Anyway, it is not the pure count of programming languages, but rather the count of programming language families one got to know, that makes the difference. If all one ever touches are languages out of the algol family, then it will have far far less benefit, compared to trying out languages from various families. For example one will learn much more, trying out SQL, a lisp, an APL, Prolog and a Smalltalk dialect, a language with stytic typing and a langauge with dynamic typing, than from trying out Java, C++ and C# and the like.
This is because the various different families do things vastly differently and require you to switch your brain into different modes, while languages of the same family will mostly only make you learn a new syntax and new built-in function names.
You're never going to build your next major project in one of them (unless you happen to be a COBOL developer or happen to maintain OpenFirmware boot code written in Forth) but seeing different ways of doing things is both interesting and instructive.
- Some sort of assembly
- C
- Java/Kotlin/C#/similar
- Python
- Javascript
- Some Lisp flavor
- Possibly Erlang
- Possibly Rust
This covers a lot of layers of the stack, along with some unique paradigms. I'd use it somewhat seriously for a while (months to year), not just as a toy to learn a language in a week. For a toy, JS and Java are close enough that you might not appreciate that once you get past the syntax, they're radically different.
Because general purpose language only solves a tip of iceberg, you will find out that your system begins to resemble more and more of a language as your system evolves. (Although it's usually best to avoid this situation IMHO, but if more than thousands of users depend on it then it's inevitable!) At this moment, some knowledge on language design and implementation could be helpful to avoid pitfalls and make the system more consistent and easy to the users even if it doesn't take a form of a textual language with its own unique syntax.
To this date I feel that using Python and SQL are amongst the best decisions in my developer life. Domain specific, without any helper libraries nor macros. Eye opener for using abstractions instead of doing everything the hard way using any low level languages.
- Pony’s lifetimes and destructive reading [1]
- Mercury’s overloading of dataflow direction and determinism [2]
were mentioned in [3].
[1]https://tutorial.ponylang.io/reference-capabilities/consume-...
[2]https://www.mercurylang.org/information/doc-latest/mercury_r...
When you don't learn elementary type-safety, you create languages like Java and Go which are plagued with NullPointerException's and nil panics.
So please, learn an ML (and a Lisp).
For instance, when we think of a type like "the natural numbers", that's just a set of 1, 2, 3, ... there is no "null reference to Integer" in there.
Thus I think terms like "null-safe type" is just something we should leave to Java programmers, and not use as a way to talk about types.
[1]: https://www.usenix.org/system/files/1311_05-08_mickens.pdf
But by a large margin nothing melted my brain more than learning a pure functional language (xquery, in my case). To have to think about programming in a completely different way was extremely beneficial. I highly recommend it, given the chance.
I became interested in F# and found a F# to JS compiler.
I can write F# code and call it from JS and do it in any toy code or side project I have.
This lets me lift up any JS or TS to F# to any degree I want across any project.
That's an awesome environment to build in as I can experiment and learn more about the language just by rewriting code that is already figured out.
At the very least, nothing wrong with being a bit more open-minded.
My job involves using multiple languages, our small team own packages that include java, kotlin, ruby, python, typescript. Everyone is expected to write in any language as needed.
We also conduct interview in any language a candidate would like to use.
Elixir vs Javascript vs Python vs Ruby... so much difference.
> For me, JavaScript became my primary language and stays
Oh, my God! Computer science of leftPad.js strikes back.
A high level script language would be such "real program". Idem for a command shell.
risc-v, if successfull, will help a lot to this then remove a lot of what I consider toxic.
Taking more time to code, even duplicate some code paths for major ISAs is saner than to depend on the planned obsolescence of grotesquely and absurdely massive compilers and many computer language syntaxes. See that as short term thinking vs long term thinking.
You would find some kind of middle ground when combining those with high level scripting languages.
Think about a world where many code paths are available in risc-v assembly, with risc-v written python-like interpreters.
That would be an awful world to live in, though more people would be employed. Less would actually get done, though.
Yes, and porting from 1 assembly to another is easy.
So I disagree with this because it discounts the actual differences between hardware systems (including RISC-V variations, 32-bit memory addresses versus 64-bit, different sets of instructions depending on the underlying variant). Then again, I've worked with systems that gave you complex number registers which are not terribly common on any other system. Could be translated away (and sometimes was, but usually to a high level language first) but if you stuck to assembly you exploded the instruction count to do so since now you needed complex math routines (among other things) that were baked into the older system.
But let's say it is easy, then you are either wasting a massive amount of time doing something by hand a computer can do for you (only a fool does this for long), or you have a computer do it for you. At which point your ISA I Assembly->ISA II Assembly translator becomes, wait for it, a compiler. And then you have a really shitty language (some old system's ISA) instead of an actually useful high level language.
EDIT: And suppose, somehow, you kill all non-RISC-V ISAs (good luck). How do you handle other hardware like GPUs? Do you really expect a common ISA to form there any time soon?
And high level script interpreters (python-like/swift-like/lua-like/javascript-like/etc-like) written themselve in assembly would be around.
In the interim, I would still code plain and simple C (probably "stuck" at c89 with benign bits of c99/c11), but using the most idiotic small and simple compilers out there, and never ever use gcc or clang to compile it.
And with risc-v, even ultra-small risc-v SOCs offer at least a 64bits core, so I would stick to 64bits.