The lack of secure composability in almost all existing languages. You cannot import untrusted code and run it in a sandbox. Unless you are completely functional, you cannot restrict the effects of your own code either.
The solution to this seems to be the object capability (key) security paradigm, where you combine authority and designation (say, the right to open a specific file, a path combined with an access right). There are only immutable globals. Sandboxing thus becomes only a matter of supplying the absolutely needed keys. This also enables the receiver to keep permissions apart like variables, thus preventing the https://en.wikipedia.org/wiki/Confused_deputy_problem (no ambient authority).
Even with languages that have security policies (Java, Tcl, ?), control is not fine grained, and other modes of interference are still possible: resource exhaustion for example. Most VMs/languages do not keep track of execution time, number of instructions or memory use. Those that do enable fascinating use cases: https://stackless.readthedocs.io/en/latest/library/stackless...
All of this seems to become extremely relevant, because sufficient security is a fundamental requirement for cooperation in a distributed world.
Spectre is another mode of interference, and perhaps one which means we're going to have to give up altogether on running untrusted code without at least a process boundary.
I recently had a lecture about something related to this in a course on advanced functional programming. Basically we were shown how you can implement a monad in Haskell that lets you preserve sensitive data when executed by untrusted code. Together with the SafeHaskell language extension, which disallows libraries to use operations that could potentially break the invariants, this seems like a very cool concept!
Well put. I'd add that this is a case where the decoupling of OS and language design is bad: capabilities are much more valuable if the OS can provide a sufficiently expressive basis for them.
Tcl has the concept of "safe interpreters" which can be spawned as slaves of the main interpreter. There is a default safe interpreter policy, which is fully scriptable for customization.
Among the available customizations in a safe interpreter are: restriction of use of individual commands, ability to set a time limit for execution of code, a limit to number of commands executed, and depth of recursion.
Memory usage can't be directly limited, but Tcl has trace features that allow examination of a variable value when set, so one could write custom code that prevents total contents of variables from exceeding a specified limit.
Almost every time I experienced a shift like this in my thinking it was due to experiencing a problem I hadn't experienced until that point.
I discovered the value of compile time type checks when I worked on large codebases in dynamic languages where every change was stressful. In comparison having the compiler tell you that you missed a spot was life changing.
I discovered the value of immutable objects when I worked on my first codebase with lots of threading. Being able to know that this value most definitely didn't change out from under me made debugging certain problems much easier.
I discovered the value of lazy evaluation the first time I had to work with files that wouldn't fit in entirely in memory. Stream processing was the only way you could reasonably solve that problem.
Pretty much every paradigm shift or opinion change I've had was caused by encountering a problem I hadn't yet run into and finding a tool that made solving that problem practical instead of impractical.
I might even go farther. I wonder if most of the techniques (and languages) that we think are stupid are instead aimed at problems that we don't have. (Of course, those techniques become stupid when people try to apply them on the wrong problems...)
>I discovered the value of compile time type checks when I worked on large codebases in dynamic languages where every change was stressful. In comparison having the compiler tell you that you missed a spot was life changing.
Sounds like me in reverse: I discovered that value when I had to do work in a dynamic language after working only in C and C++. It's like that old saying but not knowing the value of something you have until you lose it.
Our introductory programming course at university used ML and I didn't like it or get it. I already knew some C++, BASIC and Java and was mostly interested in real time graphics programming and the kind of examples used in the ML course were not interesting to me and I didn't see how it would help me tackle the kinds of programming tasks I was interested in.
I found recursion pretty unintuitive and didn't find the way it was taught in that course worked for me. At the time it mostly seemed like the approach was to point at the recursive implementation and say "look how intuitive it is!" while I completely failed to get it.
Many years later after extensive experience with C++ in the games industry I discovered F# and now with an appreciation of some of the problems caused by mutable state, particularly for parallel and concurrent code I was better prepared to appreciate the advantages of an ML style language. Years of dealing with large, complex and often verbose production code also made me really appreciate the terseness and lack of ceremony of F# and experience with both statically typed C++ code and some experience with dynamically typed Python made me appreciate F#'s combination of type safety with type inference to give the benefits of static typing without the verbosity (C++ has since got better about this).
I still struggle to think recursively and my brain naturally goes to non recursive solutions first but I can appreciate the elegance of recursive solutions now.
I think it's unfortunate the first/often only exposure people get to FP is a build-up-from-the-foundations approach that emphasizes recursion so much, I think it leaves most students with a poor understanding of why functional paradigms and practices are practical and useful.
I've written primarily in a functional language (OCaml) for a long time now, and it's very rare I write a recursive function. Definitely less than once a month.
In most domains, almost every high-level programming task involves operating on a collection. In the same way that you generally don't do that with a while loop in an imperative language, you generally don't do it with recursion in a functional one, because it's an overly-powerful and overly-clunky tool for the problem.
For me the real key to starting to be comfortable and productive working in a functional language was realizing that they do actually all have for-each loops all over the place: the "fold" function.
(Although actually it turns out you don't end up writing "fold"s all that often either, because most functional languages have lots of helper functions for common cases--map, filter, partition, etc. If you're solving a simpler problem, the simpler tool is more concise and easier to think about.)
I try to learn a new platform every once in a while. The best way to learn is to do. So I pick a project from my list and try it out. That's how I learned I didn't like Meteor and Angular when they came out and were hyper hyped but I enjoy Go very much but only for certain things. Or how I learned I disliked PHP or loved working with Python, or that MongoDB is ok for some things and less so for others (or that Postgres is bliss). I used them for something and I find what is the problem those "tools" are best at solving and where they don't fit. Then some of them I keep using over and over again, and some I file under been-there-tried-that. What I really really try to do, is get peer-biased. Many times the internet hypes something for 5 minutes and dumps it later. Or ignores something for 5 years only to rediscover it and rave about it.
Totally agreed. I started respecting dynamic languages after I got a job writing Python. I'd underestimated a lot of the upsides and overestimated a lot of the downsides.
Ditto JS (and web apps in general), testing, functional(ish) design, package management and code autoformatters. Containers to a lesser extent. A bit of getting used to it, and then a very clear sense that actually this does make my life easier, that the benefits aren't just propaganda.
For a counterexample, I "used" and rolled my eyes at daily stand-ups, close in-team communication and project management process, and now that I'm working on a team that doesn't do any of that I miss it a lot.
Rich Hickey's "Value of Values"[0] is what finally sold me on the benefits of pure functional programming and immutable data structures. (It remains horrifying to continue working with MySQL in my day job, knowing that every UPDATE is potentially a destructive action with no history and no undo.)
That feature's "coming" - part of SQL 2011 called System Versioning, currently supported in DB2 and SQL Server, but available in MariaDB 10.3 Beta according to this talk:
Often times MySQL is set up with auto-commit set to true, where every DML statement (like UPDATE) is wrapped in an implicit BEGIN and COMMIT. It doesn’t have to be that way though, you can manage the transaction yourself, and you don’t have to COMMIT if you don’t want to, you can undo (ROLLBACK) if necessary.
It’s true, transactions in MySQL work great. But once the change is committed, the previous value is overwritten permanently. If the user wants to undo five minutes later, or I want to audit based on the value a month ago, we’re hosed unless we’ve jumped backflips to bake versioning into the schema.
I think Hickey’s comparison to git is apt: we don’t stand for that in version control for our code, why should we find that acceptable for user data?
Because there is vastly more state in the world than there is code. And frankly, most state is not that important. Just wait until you work with a sufficiently large immutable system, they are an operational nightmare.
You should opt-in to immutability when the state calculations are very complex and very expensive to get wrong.
I do wish mainstream languages had better tools for safe, opt-in immutability. Something like a "Pure" attribute you assign to a function. It can only call other pure functions and the compiler can verify that it has no state changes in it's own code.
I’ve always found programming interesting. It’s possibly one of neatest ways to figure things out with computation. But early on I got the impression the profession was abusive - maybe inherently. I got fired from my first programming job for prioritizing finishing high school over a programming job. I dabbled after that and every project related to computers had this “natural” way of feeling like something I have to do “now” - even if it meant not sleeping. Then I saw the cs majors I knew in college doing the same. I heard about the industry being the same way. Not only that, most programming work was downright boring to the point of tedious. CRUD apps, basically everywhere. Nothing ever very original. Maybe it’s different now, but it sure doesn’t look like it’s progressed very far over the years.
Maybe it's different in other places, but I've worked in a few EU countries and I don't agree that abusiveness is inherent. I see the company culture and attitude of the bosses as the determinant factor, but overall most places I've worked have been fine. And even in the worst, where overtime was rampant and stress was high, it was unthinkable to fire one of the intern students for prioritizing school.
I can't argue with boring; CRUD is rampant. But I think you can escape it if you really care.
For a long time while my day jobs were centered in Python, C and C++, I was separately very interested in strict functional programming, particularly in Haskell and slightly less but somewhat in Scala. I taught myself enough to be roughly an “advanced intermediate” programmer in both languages just via tutorials and side projects.
In the time since, I’ve had a job working in a large Haskell codebase and a separate job in a large Scala codebase. I remember feeling a lot of pride and self-confidence when I passed difficult technical interviews hitting on pragmatic day-to-day issues in these languages despite having not had prior job experience with them.
After years of working in these languages, I’ve basically had a 180 degree flip in my opinion of them at least in terms of solving business problems.
I no longer have any desire to work in functional programming, and believe that many of the promises it makes in regards to type safety, encoding IO or side-effectful operations into the type system, automatic parallelism, fast compile times, etc., are mostly just fantasies achievable only in idealized settings, and that there are fundamental incongruencies between the types of mutable systems that customer-facing products represent and immutable design patterns that make the goals of software as a product development activity incompatible with functional programming.
Since my background is in mathematics, I had always loosely believed that programming languages are supposed to go in the direction of removing any mental burden placed on the programmer to know how to represent a concept inside the syntax of the language.
What I mean is, I had always thought for example that if a language requires me to know something about computer memory layouts or the data structure internals of say, floating point values, then it’s a failure of the language and it should instead present me with an abstraction that renders those memory details or data structure internals to be unnecessary in all possible situations.
In many ways I think functional programming and the idea of formal verification generally is a type of expression of this way of thinking.
But my 180 degree change of heart has made me feel based on my experiences that it’s really the opposite. In all situations, I will frequently need access to every layer of abstraction possibly all the way down to the actual chemistry or physics of hardware components, and at the very least I’ll need no abstractions wrapping memory layout or data structure internals.. I’ll always need to get outside of the abstraction and “do it myself” so to speak.
This has in turn led me back to C/C++, some assembly and direct llvm programming, and writing a lot of Python tools that expose more of these lower level concerns at the Python level.
In a sense, I feel now more like there are not really programming languages so much as there are specific pieces of software tightly coupled with specific pieces of hardware, and I may need to modify or subvert fixed rules or abstractions of any part of it. Then a programming language just ends up being whatever common pattern of stuff on the software side ends up being useful enough to get repeated from project to project, and I don’t think it’s an accident that C is such a ubiquitous language in this sense.
I used to hate C and thought it was primitive, ugly, dangerous, tedious to write in and annoying to read. While writing a big project in it (https://github.com/RedisLabsModules/RediSearch/) , I've discovered the zen of C I guess. Instead of primitive I started seeing it as minimalist; I've found beauty in it; and of course the great power that comes with the great responsibility of managing your own memory. And working in and around the Redis codebase, I also learned to enjoy reading C. While I wouldn't choose it for most projects, I really enjoy C now.
I made the decision to do that project in C, in part to be better at it (I did a bunch of small things with it, but nothing serious). Since I had to interact with Redis' C API, the choices were basically C or C++. I hated C++ much more than C having worked with a lot, so C it was. I can't say I didn't miss things like having shared_ptr and, you know, having destructors, but all in all it was a good experience. (side note - I now work a lot in C++ and sort of liking the newer stuff like lambdas for example)
I don't miss, `shared_ptr`. It is a disaster for anything other sharing a thing between threads (in which case, it is a way of managing the disaster you already have).
Now `unique_ptr` is worth missing. And destructors are good too -- you couldn't have unique_ptr without them. But in it's own way, C has both: its just you just have to remember to call the destructor yourself, every single time.
You start using `shared_ptr` because you are too confused about the code to know which of the two "contexts" is supposed to own the thing. So shared_ptr (might) fix your memory freeing problem, but it maintains (and sometimes worsens) the problems caused by having two different contexts that might or might not be alive at any given time.
With two different threads however, such problems are often unremoveable, which is why shared_ptr is the right solution mitigation.
90% of my dislike for C comes from a) that it is too tedious to work with strings and b) the non-existing standardized module/build system.
The C language itself is _beautiful_ but I am missing a beautiful standard library! Things which are trivial one-liners in other languages are sometimes 10-20 lines of brittle boilerplate code in C. If the standard library would have a bit more batteries included it would make trivial task easier and I could concentrate on actually getting work done. Opening a file and doing some text processing take usually a few lines in Python/PHP but in C you have 3 screens of funky code which will explode if something unforseen happens.
And working with additional libraries also a nightmare compared to composer/cargo. Adding a new library (and keeping all your libraries up to date) is dead-simple in basically any language besides C/C++.
tldr: I love the language itself but the tooling around it sucks.
What’s hard about adding a library? You include the header, tell the linker about it, and update the library/include patches if it’s not already on it. Job done.
Cadillac languages with a bunch of stuff in the std lib take away a lot of fussy bike shedding problems. I like not having to worry about library selection in my first through fifth revisions.
I’m willing to put up with a lot more using a built in. It’s built in, a junior dev can be expected to cope with that. Adding dependencies has a cost. Usually the cost is bigger than realized at the time.
That's relying on distribution package management to save you. Here is what happens when you add libraries in any other context:
The library has a dependency on another library, and when you go look at the dependency, it tells you that it needs to use a specific build system. So you have to build the library and its dependency, and then you might be able to link it.
But then, turns out, the dependency also has dependencies. And you have to build them too. Each dependency comes with its own unique fussiness about the build environment, leading to extensive time spent on configuration.
Hours to days later, you have wrangled a way of making it work. But you have another library to add, and then the same thing happens.
In comparison, dependencies in most any language with a notion of modules are a matter of "installation and import" - once the compiler is aware of where the module is, it can do the rest. When the dependencies require C code, as they often do, you may still have to build or borrow binaries, but the promise of Rust, D, Zig et al. is that this is only going to become less troublesome.
I guess... though I’ve always found C++ libraries to be light years easier to manage than python.
To be honest, the per-language package management seems wasteful and chaotic. I must have a dozen (mutually compatible) of numpy scattered around. And why is pip responsible for building FORTRAN anyway?
The happy path (apt-get install, or even ./configure && make && make install) is about the same.
When things go sideways, I find troubleshooting pip a little trickier. Some of this might be the tooling, but there's a cultural part too: C++ libraries seem fairly self-contained, with fewer dependencies, whereas a lot of python code pulls in everything under the sun.
It was the Postgresql codebase that did this for me, but C is a mixed bag. Because it gives you so much space, it's more on the folks managing a project to establish a specific design mindset (design patterns, documentation, naming conventions, etc...), and ensure that its enforced throughout the project. Codebases where this is done right are a joy to work with. Others, less so.
I also used to hate C and thought it was primitive, ugly, dangerous, tedious to write in and annoying to read. Later, I too, discovered the actual point of C, the beauty and minimalism of it. Then I became a better coder, and once again saw the nature of C as primitive, ugly, dangerous, tedious to write in and annoying to read. I suppose that's what enlightenment feels like.
"Before I learned the art, a punch was just a punch, and a kick, just a kick.
After I learned the art, a punch was no longer a punch, a kick, no longer a kick.
Now that I understand the art, a punch is just a punch and a kick is just a kick."
-- Bruce Lee
Using TypeScript for about 2 hours made me wonder why anyone would write JavaScript ever again. This was several years ago, when TypeScript wasn't nearly as good as it is now.
What's the correct way to use typescript when you're directly working with the Dom, and using minimal jQuery? It seems to really not like it if you're explicitly asking for things you know you have in your document, but it reports an error.
You write TypeScript the same way you write JavaScript. It just enforces a lot of the proper discipline and practices that JavaScript doesn't, and it doesn't let you incorrectly use APIs.
> you're explicitly asking for things you know you have in your document, but it reports an error
I'm not 100% sure what you're talking about, but I bet you'd get a great answer on StackOverflow by posting example code and the error message.
First of all, TypeScript doesn't know what's in your document unless you built the document with TypeScript objects. If it's HTML, TypeScript can't read or understand it.
My guess is that you're using something like "getElementBy..." and then TypeScript complains that you're using a value that might be null. You just need to check if the value is null and (for example) throw an error. Once you do that, any line after the null check will assume there's a value.
There are other workarounds when you're 100% sure you don't need TypeScript's error checks (like // @ts-ignore flags), but I strongly recommend against using them.
> using minimal jQuery
Why are you using jQuery at all? It's a solution for a problem that doesn't exist anymore. Just use vanilla JS.
I'm using jquery for certain layout libraries that I need unless you have another library that provides it. Also I'm using datatables. I don't think there's an alternative to datatables that even comes close to being as well put together.
There's just a huge number of libraries around that depend on jquery, and are rock solid with continuing support. I would prefer to use something proven than something that will be gone in a couple of years or abandoned or the underlying library will end up not supporting a feature that widget requires.
I don't want to come across as unappreciative, but there's still reasons to use jquery.
> something that will be gone in a couple of years
I suspect jQuery and it's ecosystem will themselves become irrelevant and eventually (effectively) abandoned. The (at the time compelling) rationale of abstracting away different browser behaviour has become less and less necessary, and what I would see as the other advantage of using jQuery - like easier access to behaviours like animation, $ajax - less relevant.
> I don't think there's an alternative to datatables that even comes close to being as well put together.
One of my projects uses ag-Grid[1]. It works very well and doesn't depend on jQuery. There are lots of others to choose from, including FancyGrid[2] and a vanilla-JS version of datatables[3].
ag-Grid has paying enterprise clients that contractually obligate the developers to continue support. As a guarantee of future support, that's as good as it gets in the open-source world.
> rock solid with continuing support
It looks like jQuery itself is not very actively developed anymore[4].
> I don't want to come across as unappreciative, but there's still reasons to use jquery.
It's completely fine to disagree. That's what makes HN interesting. I didn't take it personally.
I've been using TS with RxJS for the last 4 months and I think JS is totally fine. What bothers me the most is JS abuse. Why everything has to be a SPA and thus have to be recreated in JS. E.g. history API. And wtf we need RxJS at all? Other than that management has decided to go with the most strict rules for TS, so I'm spending countless hours fixing silly type errors that don't add anything to the product. I've worked with huge JS codebases a couple of times and we were fine without it. TS is overhyped in my opinion. Let's make more use of the server instead of doing everything on the client.
Long term job and total comp prospects. Having done tacked on FP in Node.js (Fantasy Land, Sanctuary, Ramda) and Scala (scalaz, cats) for quite some time and now programming in "proper" FP languages like PureScript and Haskell daily, I absolutely love life as a programmer.
However, having a decent sized family with deep roots, wanting to do college for my children as a sole earner, not living near a tech-hub of any sort, I come away with this wish list -- and I have to sustain for 20 to 30 years. ~150k TC, remote, FP languages, for the net 20 to 30 years. Seems like a tall order. We could add up all the FP Scala, FP JS, Clojure, Rust, Haskell, PureScript, Elm, OCaml, ML, etc gigs and it would be a sliver compared to the prospects of just picking the same list with just any of the other top 10 languages.
I could hop into a remote Node.js/Scala gig and slowly sell them on FP, but having done this for the past few years it gets a bit draining to fight over small FP items when you are ready to do so much more.
1) This talk made me realize that changing a paradigm is not just about changing a language. It is a way of changing the way you approach all problems and solutions. Even in real life.
I was struggling with Mathematica in grad school (it's bread and butter for theoretical physicists, since it is particularly good at symbolic math, and good enough for ancillary numerics, plotting, etc). My previous programming experience included C/C++, Python (numpy/scipy), Matlab/Scilab/Octave, and somehow, getting Mathematica to do things seemed like pulling teeth.
Incidentally, around the same time, I was exploring lambda calculus and "Learn You a Haskell" [1] for fun. Suddenly, at some point, several things about the declarative paradigm just clicked, and within a space of a few days, I went from struggling against Mathematica and hating the experience, to appreciating it's elegance and enjoying getting things done with it! For functional programming in Mathematica, see [2, 3] and the fact that it gives you access to the expression tree in a structured manner means that some really cool stuff becomes really easy!
Later, I learned that Mathematica's computation model is based on pattern matching and term rewriting. I don't know enough computing theory to disambiguate functional/rewriting/declarative, but I find it very satisfying to write code in roughly that style -- it feels clean and understandable. See Yann Esposito's write-up [4] for a fantastic illustration of this.
In that illustration, Yann takes a simple problem with an obvious procedural solution, and proceeds to rewrite it step-by-step in ten versions, each time making it slightly more modular. The modularity and flexibility of the later versions is breathtaking in its simplicity!
Every once in a while I search for something I need in Python and find that it only exists in Python 3, but I'm not quite convinced it's worth the effort to transition from 2.
However... I've just learned about Python 3's f-strings and they gave me a big smile, and probably the final push I needed :)
(Yes, I do know that sounds silly and there are probably much better reasons to transition to Python 3 ¯\_(ツ)_/¯ )
f-strings really are great. As someone who has to do a lot of string manipulation, they're both easier to maintain and easier to read than any other Python alternative.
Thanks for pointing f-strings out. I'm transitioning a script with a lot of string manipulation from Python 2 to 3 and this is better than concatenation, % formatting, or str.format().
The non-Python-specific term is string interpolation, and it should be in every language.
From a language design perspective, the problem is that the compiler needs to know about it, so it's a core feature, but for maximal efficiency it might require some feature that isn't part of a language's core, like string streams. Another possibility would be a powerful macro system that allows the transformation of a format string into code, but few languages offer that.
Working with that crappy thing for few months. At some moment the crappy disappears and it may even becomes actually cool.
Alternatively, working with that cool thing for few months on something complex. At some moment the cool disappears and it may even becomes actually crappy.
Kotlin's non-null type checks seemed nice, but I didn't start appreciating them until I had to debug a NPE being thrown 100 frames down a stack-trace in an entirely unsuspected piece of code in a legacy Java app.I had to release new debug code to production just to figure out where the hell it was happening.
Just having the type system making 'this could be null' and 'this is assured to never be null' explicit would've saved me hours.
Also, playing with Erlang made me really appreciate the actor model of concurrency.
I the early Nineties, I had a good idea of what an ideal programming language would be. Then I encountered Perl, which was diametrically opposite to everything I thought I wanted. Perl enabled me to do in a morning what would have taken a week in C. I was blown away. I was converted. I became a Perl evangelist. I used Perl everywhere I could.
20-odd years later, writing Perl for fun and C++ for money, I had a Perl program which, over ten years, had gradually grown to about 5,000 lines and 25 classes. It needed more testing than anything else I maintained. I missed the help I would have got from a C++ compiler -- what some Perl people disparagingly call BDSM. In C++, if I need to add a Widget argument to a leaf method, the compiler will find all the call sites that need updating. If they in turn need updating to accept a Widget, the compiler will find their call sites, and so on. By the time the program compiles again, it's much of the way towards working. But Perl doesn't do that for me. I can't even specify the number of arguments a method should take, let alone their types.
I eventually rewrote the whole thing in D. It came out at almost exactly the same length, which was a surprise, but I got native speed, much-reduced memory consumption, and proper type-safety. My hunch is that a translation to modern C++ would have been longer and taken more work than doing it in D, but not that much more, and the performance would have been better still.
This is not so much a story about Perl, C, C++ and D as about weakly- and strongly-typed languages. (Perl 6, in particular, remedies some of the deficiencies in Perl 5's type system, and some of the bolt-on object systems for Perl 5 take steps in that direction as well. These are heroic and ingenious efforts to give users the best of all possible worlds, to the extent that that's possible, and I wish them well.) Languages that are too weakly typed, too dynamic, too flexible, are fine for short programs that are maintained over a short period of time, but they just don't scale -- maintenance becomes too difficult, too time-consuming and too error-prone. At some point, it's best to rewrite in a well-chosen language that imposes more structure and provides more type-safety, and work with that structure (not against it) to make your program rigid enough to stand up under its own weight.
As a result of that experience, I no longer use Perl, or any other very dynamic language, for anything but the smallest throwaway programs. For everything else, I anticipate the need to grow by using C++ or D, and the next language I learn is more likely to be Rust than, say, Python.
A more general point: if you only know one composer, one band or one style of music and you think the rest aren't worth knowing, you don't know music. Even though I don't use dynamic languages much nowadays, I'm a stronger engineer for having done so. Do take the time to learn several different languages that differ widely from each other and from what you already know -- preferably including some assembler -- and don't assume that the language of the month is best suited to your unique temperament or to the job in front of you. Having an abundance of tools and choosing between them wisely is better than having one and using it for everything. It doesn't matter how dexterously you can use a soldering iron when what you need is a chainsaw.
I avoided having to learn Javascript in-depth because I thought its type system was like coding in the dark.
Your IDE would either return all possible methods of all possible objects in your whole project, or wouldn't return anything at all.
You need to either memorize the API of whatever object you're currently working with, or have its API open in a separate window to always see what's what.
Wait, no, I still think this is true. I discovered Typescript a while ago and it is a joy to work with. I don't understand why developers would subject themselves to the modern equivalent of coding in notepad.exe when they could use a language that actively helps them.
I realized just how useful shell scripting is (bash) when it occurred to me a simple five line script I wrote to erase and reclone my main work repo is the single most useful piece of code I’ve written in my first year as a web developer. God that’s a time saver.
The first thing I thought when I read this is why do you have to do this so often that you scripted it? It's good that you thought of a way to save time but this solution seems like a very blunt instrument to get back to some previous state.
Most likely answer is “git’s ux”, in my experience. So often it’s just faster and reassuring to reclone rather than resolve some unusual state you’ve accidentally put your local repo in, because a command didn’t do what you expected it to do.
As someone who appreciated C and C-style languages I hated on python for a long time.
I disliked python because of syntactical annoyances,mistakes I would notmally iron out at compile time now happen at run time and version fragmentation with no backward compatibility at times. On that last point,new java versions for example would deprecate features over time allowing the programmer the option of enabling these features. I still think the simple things like mixing tabs and spaces shouldn't have caused a hard failure,warnings or ability to override the deprecation would have been nice. Not only that,I hated how I would ship a python program having tested it in one python version and five years from now the default python version on linux distros (or default installer for windows) would install an incompatible version,I hated how users of my program would have to figure out the correct version.
But I no longer hate python mostly as a result of having seen how easily it has helped me solve different types problems that are otherwise difficult or time consuming to solve. At the end of the day,it gets the job done well,is easy to learn,harder to screw up in and has saved me a ton of time.
This sounds similar to my journey with Python. For years, I used it as a go to for programs that were bigger than a bash script but smaller than an application or service. I mostly wrote single .py files with maybe the odd module and avoided setuptools and friends. In the last year, I’ve been working on a larger library in Python, which has meant getting into the packaging and deployment tool chain and structuring a project with dozens of files, tests, etc. My background has been in mainline languages such as Java and C#, with excursions into Clojure, F#, etc. We adopted the new Python type annotations and they seem to increase the visual clutter of the language without delivering many benefits in terms of confidence or ease of development/code completion. Our library is aimed at data science, so I’m sure not many users will actually bother setting up the tools to do type checking.
I do still appreciate Python, but I wonder whether my issues are due to our approach, my current level of familiarity with Python and the ecosystem or the fact that we’re working in the data science world which is new to me as well.
I used to dislike Java, then recently I discovered java streams, and some of the nice features with newer versions like more immutable types. After that I found Java a whole lot more enjoyable to use.
Same. Then I discovered Kotlin, which is really just a much improved Java, and my only regret is that there aren't more server side gigs available for it (yet).
Scala collection processing--and syntax--will, after not too long, quite possibly put you back to disliking Java. "Why do I have to keep asking for .stream()? Where is '_'??"
This is probably an unpopular opinion - Haskell, I used to think it must be cool (and useful) since people go on about it so much. I spent quite a bit of time learning it, and imho the usefulness to practicing programmers is marginal at best. It does present some useful techniques that are making their way into languages (e.g. swift optionals), but in general it didn't live up to the hype for me. I feel a lot of the things they go on about are overly complified to impress.
Haskell has some problems that mean it is not suited to a lot of tasks, esp. the difficulty of predicting space/time performance and integration with OS-level facilities. But it is a serious contender in many cases where correctness is essential: Haskell's type system is expressive and the language makes valuable promises about how code behaves.
Basically you start to ingrain how to use the tools there for problems that you solve differently in other systems. Then you start looking for ways to get rid of the awkward parts of your code
The old list of official aha's is: Monads, applicative functors, lenses. But really it's about spending time learning them well enough to use normally.
400 comments
[ 4.8 ms ] story [ 309 ms ] threadThe solution to this seems to be the object capability (key) security paradigm, where you combine authority and designation (say, the right to open a specific file, a path combined with an access right). There are only immutable globals. Sandboxing thus becomes only a matter of supplying the absolutely needed keys. This also enables the receiver to keep permissions apart like variables, thus preventing the https://en.wikipedia.org/wiki/Confused_deputy_problem (no ambient authority).
Even with languages that have security policies (Java, Tcl, ?), control is not fine grained, and other modes of interference are still possible: resource exhaustion for example. Most VMs/languages do not keep track of execution time, number of instructions or memory use. Those that do enable fascinating use cases: https://stackless.readthedocs.io/en/latest/library/stackless...
All of this seems to become extremely relevant, because sufficient security is a fundamental requirement for cooperation in a distributed world.
Functional pearl: http://www.cse.chalmers.se/~russo/publications_files/pearl-r... Slides: https://1drv.ms/p/s!Ahd2uwlk3jmIlCZr0spYc_I-OveR Source: https://bitbucket.org/russo/mac-lib
I'm watching Zircon with great interest.
https://github.com/fuchsia-mirror/zircon
Among the available customizations in a safe interpreter are: restriction of use of individual commands, ability to set a time limit for execution of code, a limit to number of commands executed, and depth of recursion.
Memory usage can't be directly limited, but Tcl has trace features that allow examination of a variable value when set, so one could write custom code that prevents total contents of variables from exceeding a specified limit.
I discovered the value of compile time type checks when I worked on large codebases in dynamic languages where every change was stressful. In comparison having the compiler tell you that you missed a spot was life changing.
I discovered the value of immutable objects when I worked on my first codebase with lots of threading. Being able to know that this value most definitely didn't change out from under me made debugging certain problems much easier.
I discovered the value of lazy evaluation the first time I had to work with files that wouldn't fit in entirely in memory. Stream processing was the only way you could reasonably solve that problem.
Pretty much every paradigm shift or opinion change I've had was caused by encountering a problem I hadn't yet run into and finding a tool that made solving that problem practical instead of impractical.
That in itself can turn into a learning experience if you stick around for the aftermath.
Sounds like me in reverse: I discovered that value when I had to do work in a dynamic language after working only in C and C++. It's like that old saying but not knowing the value of something you have until you lose it.
I found recursion pretty unintuitive and didn't find the way it was taught in that course worked for me. At the time it mostly seemed like the approach was to point at the recursive implementation and say "look how intuitive it is!" while I completely failed to get it.
Many years later after extensive experience with C++ in the games industry I discovered F# and now with an appreciation of some of the problems caused by mutable state, particularly for parallel and concurrent code I was better prepared to appreciate the advantages of an ML style language. Years of dealing with large, complex and often verbose production code also made me really appreciate the terseness and lack of ceremony of F# and experience with both statically typed C++ code and some experience with dynamically typed Python made me appreciate F#'s combination of type safety with type inference to give the benefits of static typing without the verbosity (C++ has since got better about this).
I still struggle to think recursively and my brain naturally goes to non recursive solutions first but I can appreciate the elegance of recursive solutions now.
I've written primarily in a functional language (OCaml) for a long time now, and it's very rare I write a recursive function. Definitely less than once a month.
In most domains, almost every high-level programming task involves operating on a collection. In the same way that you generally don't do that with a while loop in an imperative language, you generally don't do it with recursion in a functional one, because it's an overly-powerful and overly-clunky tool for the problem.
For me the real key to starting to be comfortable and productive working in a functional language was realizing that they do actually all have for-each loops all over the place: the "fold" function.
(Although actually it turns out you don't end up writing "fold"s all that often either, because most functional languages have lots of helper functions for common cases--map, filter, partition, etc. If you're solving a simpler problem, the simpler tool is more concise and easier to think about.)
I try to learn a new platform every once in a while. The best way to learn is to do. So I pick a project from my list and try it out. That's how I learned I didn't like Meteor and Angular when they came out and were hyper hyped but I enjoy Go very much but only for certain things. Or how I learned I disliked PHP or loved working with Python, or that MongoDB is ok for some things and less so for others (or that Postgres is bliss). I used them for something and I find what is the problem those "tools" are best at solving and where they don't fit. Then some of them I keep using over and over again, and some I file under been-there-tried-that. What I really really try to do, is get peer-biased. Many times the internet hypes something for 5 minutes and dumps it later. Or ignores something for 5 years only to rediscover it and rave about it.
Ditto JS (and web apps in general), testing, functional(ish) design, package management and code autoformatters. Containers to a lesser extent. A bit of getting used to it, and then a very clear sense that actually this does make my life easier, that the benefits aren't just propaganda.
For a counterexample, I "used" and rolled my eyes at daily stand-ups, close in-team communication and project management process, and now that I'm working on a team that doesn't do any of that I miss it a lot.
[0]: https://www.youtube.com/watch?v=-6BsiVyC1kM
https://youtu.be/xEDZQtAHpX8?t=2278
I think Hickey’s comparison to git is apt: we don’t stand for that in version control for our code, why should we find that acceptable for user data?
You should opt-in to immutability when the state calculations are very complex and very expensive to get wrong.
I do wish mainstream languages had better tools for safe, opt-in immutability. Something like a "Pure" attribute you assign to a function. It can only call other pure functions and the compiler can verify that it has no state changes in it's own code.
I’ve always found programming interesting. It’s possibly one of neatest ways to figure things out with computation. But early on I got the impression the profession was abusive - maybe inherently. I got fired from my first programming job for prioritizing finishing high school over a programming job. I dabbled after that and every project related to computers had this “natural” way of feeling like something I have to do “now” - even if it meant not sleeping. Then I saw the cs majors I knew in college doing the same. I heard about the industry being the same way. Not only that, most programming work was downright boring to the point of tedious. CRUD apps, basically everywhere. Nothing ever very original. Maybe it’s different now, but it sure doesn’t look like it’s progressed very far over the years.
I can't argue with boring; CRUD is rampant. But I think you can escape it if you really care.
In the time since, I’ve had a job working in a large Haskell codebase and a separate job in a large Scala codebase. I remember feeling a lot of pride and self-confidence when I passed difficult technical interviews hitting on pragmatic day-to-day issues in these languages despite having not had prior job experience with them.
After years of working in these languages, I’ve basically had a 180 degree flip in my opinion of them at least in terms of solving business problems.
I no longer have any desire to work in functional programming, and believe that many of the promises it makes in regards to type safety, encoding IO or side-effectful operations into the type system, automatic parallelism, fast compile times, etc., are mostly just fantasies achievable only in idealized settings, and that there are fundamental incongruencies between the types of mutable systems that customer-facing products represent and immutable design patterns that make the goals of software as a product development activity incompatible with functional programming.
Since my background is in mathematics, I had always loosely believed that programming languages are supposed to go in the direction of removing any mental burden placed on the programmer to know how to represent a concept inside the syntax of the language.
What I mean is, I had always thought for example that if a language requires me to know something about computer memory layouts or the data structure internals of say, floating point values, then it’s a failure of the language and it should instead present me with an abstraction that renders those memory details or data structure internals to be unnecessary in all possible situations.
In many ways I think functional programming and the idea of formal verification generally is a type of expression of this way of thinking.
But my 180 degree change of heart has made me feel based on my experiences that it’s really the opposite. In all situations, I will frequently need access to every layer of abstraction possibly all the way down to the actual chemistry or physics of hardware components, and at the very least I’ll need no abstractions wrapping memory layout or data structure internals.. I’ll always need to get outside of the abstraction and “do it myself” so to speak.
This has in turn led me back to C/C++, some assembly and direct llvm programming, and writing a lot of Python tools that expose more of these lower level concerns at the Python level.
In a sense, I feel now more like there are not really programming languages so much as there are specific pieces of software tightly coupled with specific pieces of hardware, and I may need to modify or subvert fixed rules or abstractions of any part of it. Then a programming language just ends up being whatever common pattern of stuff on the software side ends up being useful enough to get repeated from project to project, and I don’t think it’s an accident that C is such a ubiquitous language in this sense.
I now get way more annoyed by the in-house build system than the huge C codebase.
Now `unique_ptr` is worth missing. And destructors are good too -- you couldn't have unique_ptr without them. But in it's own way, C has both: its just you just have to remember to call the destructor yourself, every single time.
You start using `shared_ptr` because you are too confused about the code to know which of the two "contexts" is supposed to own the thing. So shared_ptr (might) fix your memory freeing problem, but it maintains (and sometimes worsens) the problems caused by having two different contexts that might or might not be alive at any given time.
With two different threads however, such problems are often unremoveable, which is why shared_ptr is the right solution mitigation.
The C language itself is _beautiful_ but I am missing a beautiful standard library! Things which are trivial one-liners in other languages are sometimes 10-20 lines of brittle boilerplate code in C. If the standard library would have a bit more batteries included it would make trivial task easier and I could concentrate on actually getting work done. Opening a file and doing some text processing take usually a few lines in Python/PHP but in C you have 3 screens of funky code which will explode if something unforseen happens.
And working with additional libraries also a nightmare compared to composer/cargo. Adding a new library (and keeping all your libraries up to date) is dead-simple in basically any language besides C/C++.
tldr: I love the language itself but the tooling around it sucks.
I’m willing to put up with a lot more using a built in. It’s built in, a junior dev can be expected to cope with that. Adding dependencies has a cost. Usually the cost is bigger than realized at the time.
The library has a dependency on another library, and when you go look at the dependency, it tells you that it needs to use a specific build system. So you have to build the library and its dependency, and then you might be able to link it.
But then, turns out, the dependency also has dependencies. And you have to build them too. Each dependency comes with its own unique fussiness about the build environment, leading to extensive time spent on configuration.
Hours to days later, you have wrangled a way of making it work. But you have another library to add, and then the same thing happens.
In comparison, dependencies in most any language with a notion of modules are a matter of "installation and import" - once the compiler is aware of where the module is, it can do the rest. When the dependencies require C code, as they often do, you may still have to build or borrow binaries, but the promise of Rust, D, Zig et al. is that this is only going to become less troublesome.
To be honest, the per-language package management seems wasteful and chaotic. I must have a dozen (mutually compatible) of numpy scattered around. And why is pip responsible for building FORTRAN anyway?
When things go sideways, I find troubleshooting pip a little trickier. Some of this might be the tooling, but there's a cultural part too: C++ libraries seem fairly self-contained, with fewer dependencies, whereas a lot of python code pulls in everything under the sun.
You write TypeScript the same way you write JavaScript. It just enforces a lot of the proper discipline and practices that JavaScript doesn't, and it doesn't let you incorrectly use APIs.
> you're explicitly asking for things you know you have in your document, but it reports an error
I'm not 100% sure what you're talking about, but I bet you'd get a great answer on StackOverflow by posting example code and the error message.
First of all, TypeScript doesn't know what's in your document unless you built the document with TypeScript objects. If it's HTML, TypeScript can't read or understand it.
My guess is that you're using something like "getElementBy..." and then TypeScript complains that you're using a value that might be null. You just need to check if the value is null and (for example) throw an error. Once you do that, any line after the null check will assume there's a value.
There are other workarounds when you're 100% sure you don't need TypeScript's error checks (like // @ts-ignore flags), but I strongly recommend against using them.
> using minimal jQuery
Why are you using jQuery at all? It's a solution for a problem that doesn't exist anymore. Just use vanilla JS.
There's just a huge number of libraries around that depend on jquery, and are rock solid with continuing support. I would prefer to use something proven than something that will be gone in a couple of years or abandoned or the underlying library will end up not supporting a feature that widget requires.
I don't want to come across as unappreciative, but there's still reasons to use jquery.
I suspect jQuery and it's ecosystem will themselves become irrelevant and eventually (effectively) abandoned. The (at the time compelling) rationale of abstracting away different browser behaviour has become less and less necessary, and what I would see as the other advantage of using jQuery - like easier access to behaviours like animation, $ajax - less relevant.
$ajax supports async/await, yes - https://petetasker.com/using-async-await-jquerys-ajax/ - but why would I not want to just use the fetch apis? https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API (ignoring browser compatibility, which is becoming the norm)
One of my projects uses ag-Grid[1]. It works very well and doesn't depend on jQuery. There are lots of others to choose from, including FancyGrid[2] and a vanilla-JS version of datatables[3].
ag-Grid has paying enterprise clients that contractually obligate the developers to continue support. As a guarantee of future support, that's as good as it gets in the open-source world.
> rock solid with continuing support
It looks like jQuery itself is not very actively developed anymore[4].
> I don't want to come across as unappreciative, but there's still reasons to use jquery.
It's completely fine to disagree. That's what makes HN interesting. I didn't take it personally.
1. https://www.ag-grid.com/
2. https://fancygrid.com/
3. https://github.com/Mobius1/Vanilla-DataTables
4. https://github.com/jquery/jquery/issues/3886
+1
Also https://medium.com/javascript-scene/the-typescript-tax-132ff...
However, having a decent sized family with deep roots, wanting to do college for my children as a sole earner, not living near a tech-hub of any sort, I come away with this wish list -- and I have to sustain for 20 to 30 years. ~150k TC, remote, FP languages, for the net 20 to 30 years. Seems like a tall order. We could add up all the FP Scala, FP JS, Clojure, Rust, Haskell, PureScript, Elm, OCaml, ML, etc gigs and it would be a sliver compared to the prospects of just picking the same list with just any of the other top 10 languages.
I could hop into a remote Node.js/Scala gig and slowly sell them on FP, but having done this for the past few years it gets a bit draining to fight over small FP items when you are ready to do so much more.
Keep Ruby Weird 2018 - Closing Keynote by Avdi Grimm https://www.youtube.com/watch?v=UJnsXUVsr7w
2) And this talk made me very interested about Elixir
Elixir: The only Sane Choice in an Insane World - Brian Cardarella https://www.youtube.com/watch?v=gom6nEvtl3U
Incidentally, around the same time, I was exploring lambda calculus and "Learn You a Haskell" [1] for fun. Suddenly, at some point, several things about the declarative paradigm just clicked, and within a space of a few days, I went from struggling against Mathematica and hating the experience, to appreciating it's elegance and enjoying getting things done with it! For functional programming in Mathematica, see [2, 3] and the fact that it gives you access to the expression tree in a structured manner means that some really cool stuff becomes really easy!
Later, I learned that Mathematica's computation model is based on pattern matching and term rewriting. I don't know enough computing theory to disambiguate functional/rewriting/declarative, but I find it very satisfying to write code in roughly that style -- it feels clean and understandable. See Yann Esposito's write-up [4] for a fantastic illustration of this.
[1]: http://learnyouahaskell.com/
[2]: https://reference.wolfram.com/language/guide/FunctionalProgr...
[3]: https://mathematica.stackexchange.com/questions/163992/what-...
[4]: See Section 3.1 (you don't actually have to know any Haskell to get value from this example): http://yannesposito.com/Scratch/en/blog/Haskell-the-Hard-Way...
In that illustration, Yann takes a simple problem with an obvious procedural solution, and proceeds to rewrite it step-by-step in ten versions, each time making it slightly more modular. The modularity and flexibility of the later versions is breathtaking in its simplicity!
However... I've just learned about Python 3's f-strings and they gave me a big smile, and probably the final push I needed :)
(Yes, I do know that sounds silly and there are probably much better reasons to transition to Python 3 ¯\_(ツ)_/¯ )
From a language design perspective, the problem is that the compiler needs to know about it, so it's a core feature, but for maximal efficiency it might require some feature that isn't part of a language's core, like string streams. Another possibility would be a powerful macro system that allows the transformation of a format string into code, but few languages offer that.
Alternatively, working with that cool thing for few months on something complex. At some moment the cool disappears and it may even becomes actually crappy.
Just having the type system making 'this could be null' and 'this is assured to never be null' explicit would've saved me hours.
Also, playing with Erlang made me really appreciate the actor model of concurrency.
Type "user.", waits 500ms, see complete db schema of user table with types. Foreign keys and comments 1 Ctrl-click away.
No more typos in column names.
Db schema changed? Press build and you have 234 errors in 56 files.
What was the name of that table? Adr(alt space)essCommentFooBar
Need to insert new row?, Ctrl-c class interface and just assign the values.
But selects I write my own.
20-odd years later, writing Perl for fun and C++ for money, I had a Perl program which, over ten years, had gradually grown to about 5,000 lines and 25 classes. It needed more testing than anything else I maintained. I missed the help I would have got from a C++ compiler -- what some Perl people disparagingly call BDSM. In C++, if I need to add a Widget argument to a leaf method, the compiler will find all the call sites that need updating. If they in turn need updating to accept a Widget, the compiler will find their call sites, and so on. By the time the program compiles again, it's much of the way towards working. But Perl doesn't do that for me. I can't even specify the number of arguments a method should take, let alone their types.
I eventually rewrote the whole thing in D. It came out at almost exactly the same length, which was a surprise, but I got native speed, much-reduced memory consumption, and proper type-safety. My hunch is that a translation to modern C++ would have been longer and taken more work than doing it in D, but not that much more, and the performance would have been better still.
This is not so much a story about Perl, C, C++ and D as about weakly- and strongly-typed languages. (Perl 6, in particular, remedies some of the deficiencies in Perl 5's type system, and some of the bolt-on object systems for Perl 5 take steps in that direction as well. These are heroic and ingenious efforts to give users the best of all possible worlds, to the extent that that's possible, and I wish them well.) Languages that are too weakly typed, too dynamic, too flexible, are fine for short programs that are maintained over a short period of time, but they just don't scale -- maintenance becomes too difficult, too time-consuming and too error-prone. At some point, it's best to rewrite in a well-chosen language that imposes more structure and provides more type-safety, and work with that structure (not against it) to make your program rigid enough to stand up under its own weight.
As a result of that experience, I no longer use Perl, or any other very dynamic language, for anything but the smallest throwaway programs. For everything else, I anticipate the need to grow by using C++ or D, and the next language I learn is more likely to be Rust than, say, Python.
A more general point: if you only know one composer, one band or one style of music and you think the rest aren't worth knowing, you don't know music. Even though I don't use dynamic languages much nowadays, I'm a stronger engineer for having done so. Do take the time to learn several different languages that differ widely from each other and from what you already know -- preferably including some assembler -- and don't assume that the language of the month is best suited to your unique temperament or to the job in front of you. Having an abundance of tools and choosing between them wisely is better than having one and using it for everything. It doesn't matter how dexterously you can use a soldering iron when what you need is a chainsaw.
Your IDE would either return all possible methods of all possible objects in your whole project, or wouldn't return anything at all.
You need to either memorize the API of whatever object you're currently working with, or have its API open in a separate window to always see what's what.
Wait, no, I still think this is true. I discovered Typescript a while ago and it is a joy to work with. I don't understand why developers would subject themselves to the modern equivalent of coding in notepad.exe when they could use a language that actively helps them.
I disliked python because of syntactical annoyances,mistakes I would notmally iron out at compile time now happen at run time and version fragmentation with no backward compatibility at times. On that last point,new java versions for example would deprecate features over time allowing the programmer the option of enabling these features. I still think the simple things like mixing tabs and spaces shouldn't have caused a hard failure,warnings or ability to override the deprecation would have been nice. Not only that,I hated how I would ship a python program having tested it in one python version and five years from now the default python version on linux distros (or default installer for windows) would install an incompatible version,I hated how users of my program would have to figure out the correct version.
But I no longer hate python mostly as a result of having seen how easily it has helped me solve different types problems that are otherwise difficult or time consuming to solve. At the end of the day,it gets the job done well,is easy to learn,harder to screw up in and has saved me a ton of time.
That's how I felt coming from perl to python, but I did learn to like it after working with it a bit.
I do still appreciate Python, but I wonder whether my issues are due to our approach, my current level of familiarity with Python and the ecosystem or the fact that we’re working in the data science world which is new to me as well.
Now I feel I could race a team of programmers in those languages and be far in front.
The old list of official aha's is: Monads, applicative functors, lenses. But really it's about spending time learning them well enough to use normally.