Hacker News new | past | comments | ask | show | jobs | submit login
Leaving Python for JavaScript (jonasgalvez.com.br)
81 points by jgalvez on Aug 25, 2017 | hide | past | web | favorite | 103 comments



So a person who writes web apps prefers JS over Python. Cool. Pick the right tool for the job. If your job is writing client-side-heavy web apps that share code between the client and server, then JS might be a better choice than Python.

Look, writing programs that respond to HTTP requests with HTML is not rocket science. You can do it in JS, Python or Ruby and be very productive. If you hate yourself, you can do it in Java, Go or whatever else and trade server performance for development speed. If you really hate yourself (or are Amazon circa 2004), you can write your whole website as a C++ static binary.

My theory is that the endless proliferation of JS libraries, frameworks and language revisions are because lots of smart and talented developers are employed to build websites - but building websites is just not that technically interesting, so they express their creativity by creating new tools, frameworks, package managers and ES revisions. It's a symptom of boredom.

But my one bit of career advice would be don't "Leave X for Y." Don't tie your livelihood and career to a single X or Y. Learn how to use a variety of tools and pick the best one for each job. Any big project will require using a few different tools and really valuable developers are the ones who can navigate that entire landscape intelligently.


Right? When I read these kind of posts I always imagine a carpenter saying the equivalent "After over 10 years using a hammer as my main tool, I have moved on to the screwdriver." Which is nonsense.


With programming languages you can both screw and hammer. I'ts not like you need to know C++ to do GUI's and D to make CLI's, or the other way around. What's the best tool for the job when all tools does basically the same thing ? I would say the tool you are best at.


But in this case, the function of the tool is very similar. It's more like switching from one multi-tool to another. Both have a screwdriver, a knife and maybe a bottle opener. They're more than just superficially different for all tasks, but for web development they're only superficially different.


All very well said. I never said I was leaving Python for good, in fact, it needs to be pointed out it's still king for machine learning and I'll even integrate a Python-based solution as microservice to a Node.js project. Perhaps a clearer title would have been, "JavaScript is now my main language".

As for boredom, though, it's true, but every now and then something special emerges out of boredom, as was the case for Vue.js and most recently, Nuxt.js for me.

I hate the amount of JS frameworks and libraries available like everybody else and if anything, this article is also a short reference for myself in disguise on _how to get started_. I'll say though, knowing your way around a Node.js project can be painful, but with a little experience can become really reassuring. Code runs fast, it's easy to write (easy idioms to memorize like Python) and is generally very predictable.


I started programming in JS then moved to Python. It seems like JS has become very, very complicated. I look at his post and I am lost... I remember Knockout.js and Node in the v0.8 days.

I mean look at this:

  dotenv (lets you load the environment from an .env file), axios 
  (HTTP client library), cheerio (HTML parsing library), bcrypt 
  (password hashing), co-body (HTTP body parser), co-busboy (HTTP 
  multipart parser), jsonwebtoken (JWT generator), koa-jwt (JWT 
  middleware), koa-router, koa-sslify, vue-no-ssr and source-map.
What the hell is all that? I am not saying he is wrong... I am just saying that I don't have the capacity to play in that playground. I am also asking if any of those libraries will be around in a year?


I have been in your position about a year ago. The approached that helped me, was to not worry about all this.

Just start somewhere; and in a month or two, you would mostly know which libraries you need to add to your project.

The best way to go about it, is to check a few popular open source projects; what libraries they typically use.

> I am also asking if any of those libraries will be around in a year?

You can check the download stats of these in https://npmjs.com, and activity on the GitHub repo, before adding it to your project.

If they are still around, but have published a new major version to update a few APIs; you can slowly migrate to it.

If not, you'd read some Medium post or Hackernews discussion, about what is replacing that.

There's something called Greenkeeper (https://greenkeeper.io/) that integrates a GitHub bot to your repo, and files a PR when something needs to be upgraded.

You can implement something similar on your own, if you aren't interested in using something like this.


I mean no offense, but the "just use it you'll get used to it" strategy seems the antithesis of the goal of engineering as a discipline.

A materials engineer simply cannot afford to pick concrete unless she knows the specific load and weathering characteristics of it. The same should be true for software projects. You shouldn't use cheerio because everyone uses it in their project, but because you've looked through the source and considered all your options for need it fills.

Sorry for the rant, but I think it's important to encourage software developers to think before they code.


It's important to not disregard the expertise of a fellow developer before you have decisive proof that their approach is genuinely poorly thought out. That is to say: Give people the benefit of the doubt.

As an outsider the big amount of dependencies look daunting, but the tendency in JavaScript as of late has been to use small libraries that do one thing, do it well, and play well together with others (Composable is the buzzword). Whereas Java and C# for instance tend to have macro libs that "Have it all bundled in".

It's different approaches, and you might not _need_ absolutely everything, then again you might, and you are in a much worse position to evaluate that as an outsider than the developer who made it.

All in all, I quite like the JavaScript approach where pieces are just that, instead of a pre-built lego set which I'll have to study to build, I have small pieces which give me more flexibility to take my project where I want to take it. The tradeoff, and they naturally exist, is that dependency maintenance is substantially more difficult, and the likelihood of something you use being straight up abandoned raises for each dependency you add. Which might lead to having you either maintain that piece yourself, or abandon it, and add another piece that fulfills a similar role.


If you're building in cement, you need to know the cement is strong enough. If you're building in tofu, choosing extra firm over silken isn't really going to help.


Most of the Js libs are super tiny. You integrate them, learn what you need to learn in less than 10min. Each is like learning a numpy operation :). It has its benefits and problems, but it's for the better imo.


Koa has been here since 2013 (and some of those other libraries even longer). It is here to stay.

Do you see ancient but popular PHP frameworks going unmaintained and reach the point where they collect a significant amount of bugs and vulnerabilities? Not really...


Apparently it's just the guy's routine JS rig, not any sort of new standard you would have to follow or even care about.


> What the hell is all that?

Libraries of code.

> I am not saying he is wrong...

Good, because they are right.

> I am just saying that I don't have the capacity to play in that playground

Good, because you are right.

> I am also asking if any of those libraries will be around in a year?

Yes. Some have been around for years already.

Listen, it's fine that you are using Python. It's fine that the OP is using JavaScript. It doesn't make you wrong. It doesn't make them wrong. Just because you are confused doesn't make it wrong. Hell, Python should worry about itself; it has enough warts of it's own it doesn't need people pretending that a library like bcrypt is confusing.


I suspect you are being downvoted for presuming to know what the parent's capacity is.


> there's no acceptable way to pass a function body to another in Python.

There are sentences which are telling a huge incompetence about the person using these.

This sounds the same story to me as "We used tech X, but X is shit/can't do something so we switched to Y, and Y is awesome and fast." Where the person switching was just incompetent with X but it would have been perfectly solvable and they just do a totally different thing with Y.

In the specific "passing a function" case, yes, it's impossible to pass only a function body, but I think it's impossible to do in JavaScript also? (correct me if I'm wrong), because you pass in function. You can do the same in Python by defining a function in the same scope and passing that in. If you don't like that, it's just one opinion, not necessarily good or bad. For example, I find it ugly and difficult to handle nesting this multiple times (defining another anonymous function inside an anonymous function) which is called "callback hell" if I understand right, but that's just my opinion also, because people seem to able to accept this hell. :)


I really like chaining Promises in JavaScript. The Python way to do this doesn't seem as powerful (what if you want to inject a parallel set of async operations at some point in the chain?): https://stackoverflow.com/questions/43325501/how-do-i-write-... - JS also supports async/await but the advantage of JS is that you can return promises (or parallel combinations of multiple promises) from inside promises so any link in the promise chain can essentially inject new links in the chain in its own place (so the chain can change dynamically based on the results of previous async operations).

You can build really complex dynamic async chains in JS very easily because of this. It's hard to explain why it's so nice until you try it.

In JS, I can do something like:

Promise.resolve().then(() => {

  let innerPromiseChain = serialOperation1()
  .then(serialOperation2);

  if (somethingIsTrue) {
    // Only inject serialOperation3 at the end of the
    // innerPromiseChain if somethingIsTrue
    return innerPromiseChain.then(serialOperation3);
  }
  // else return the inner chain without serialOperation3
  return innerPromiseChain;
}) .then((resultOfLastSerialOperation) => {

  // resultOfLastSerialOperation could be the result of
  // either serialOperation2 or serialOperation3 depending
  // on the value of somethingIsTrue

  // parallelOperation1 and parallelOperation2 will be
  // executed in parallel.
  return Promise.all(parallelOperation1(), parallelOperation2());
}) .then((parallelOp1Result, parallelOp2Result) => {

  // This will be called once the previous parallel
  // operations have both finished (therefore the
  // whole chain has been processed).
});


You realize your promise code is ugly af. All your code is nested within other functions and chained with dots. It's not clear.

For imperative code the goal is to write code that is just as clear as writing a single threaded algorithm: a simple list of steps. Promises and callbacks DO NOT ACHIEVE THIS GOAL.

That stackoverflow post showed a python solution that's infinitely cleaner than js. Python skipped callbacks and promise chains and jumped directly to the best way to do async operations... async/await.

You want to shove a parallel set in an async chain of operations? Here's the way to do serial and parallel async operations in Python:

    async def main():
        # run them sequentially (but the loop can do other stuff in the meanwhile)
        body1 = await get_body('http://httpbin.org/ip')
        print body1
        body2 = await get_body('http://httpbin.org/user-agent')
        print body2


        # run them in parallel
        task1 = get_body('http://httpbin.org/ip')
        task2 = get_body('http://httpbin.org/user-agent')
    
        for body in await asyncio.gather(task1, task2):
            print(body)
You see that code? Way more clearer way more understandable than your promise chain. Python has it's problems but your promise code is not powerful at all, it leads to over complicated code and just slightly better than callbacks.


Well put. Good to see that Python can handle this cleanly.

You can do pretty much exactly the same with JS but I do think that there is some merit to this intermediate step of having the then() clauses for backwards-compatibility because you can't always guarantee that the caller will in be an async function.

Async/await is an all-or-nothing improvement not an incremental one.


The way I see it, there are two types of callbacks - synchronous and asynchronous.

This is a synchronous callback (but callback nonetheless), that takes an array of numbers, and adds its entries:

arr.reduce(function(acc, item){ return acc + item; });

There's some boilerplate in this, so as per latest JS specs, you can remove all that and rewrite it as:

arr.reduce( (x, y) => x + y);

Then there's asynchronous callback. Most of "callback-hell" stems from these type of callbacks, because you would do "do-this-then-do-this-then-the-other-thing", and nest these one within other.

We realized that callback-hell prevents us from using return or handle errors in a clean & reliable way, so we went to promises.

Then promises turned out to be quite a pain too. Then generators, and now we have settled for async-await.

But in no way that "hell" is acceptable!


No one likes callback hell but I believe they meant that you can just pass a function expression directly. No need to define a named function. It's the difference between:

  let doubled = nums.map(x => x * 2)
and this:

  def double(x):
    return x * 2
  doubled = map(double, nums)
(my Python is pretty rusty so maybe there's a better way of doing that)


You can do the exact same thing in Python, and this is not any new feature:

    doubled = map(lambda x: x * 2, nums)
JS it's just not as explicit with what you're exactly doing.


If you want more than a one-line lambda, however, you're in for a rough time, and need to go the route of

    def foo():
        # ... four lines here ...
    modified = map(foo, items)


What's rough about giving a function a name?


Naming things is one of the hard things in computer science. :) Anonymous functions, when appropriate, let us avoid having to think about that.

For example, `f` is a refactoring of an anonymous function:

    function f (item) {
      return {
        // foo properties
      }
    }
    const fooItems = values.map(item => f)
Imagine that you have to map over several different collections of items, and generating the lists `foo`, `bar`, and `baz`. You have to either name the functions we pass to map `f`, `g`, and `h` (which I bet our reviewers would hate), or `fooMapFunc`, `barMapFunc`, `bazMapFunc`. It just pollutes the namespace that I have to keep in my head, because I have to wonder "is this used somewhere else?".

Moreover, is it _more readable_ to define the helpers first, and then the collections that are made by using them, or to define the pairs (fooMapFunc, fooItems) in sequence? This could easily be felt one way or the other, and both are valid, but leads to code review holy wars. "I feel this is more readable / I feel exactly opposite".

For comparison, the anonymous version of this similar operation:

    const fooItems = values.map((item) => {
      return {
        // foo properties
      }
    })

    const barItems = values.map((item) => {
      return {
        // bar properties
      }
    })

    const bazItems = values.map((item) => {
      return {
        // baz properties
      }
    })

In this case, the anonymous function is clearly not usable anywhere else, so it's easier to be sure that it's something one can change, and completely removes the chance of holy war over readability of whether to define helper functions together or with their collection.


Naming things is hard because names represent abstractions. Complex abstractions are hard to get right and hard to summarize; widely-used abstractions and names are hard to change.

A nested function is a simple abstraction in a narrow scope. Both the abstraction and the name are easy to change, so the name only has to describe what the function does now. When that's hard, it's often because the function does too much, does too little, or belongs somewhere else. Likewise, people arguing about the organization of inner functions is usually a sign the outer function should be refactored. Anonymous functions are appropriate when names would be almost redundant, not when coming up with a name is hard.

My editor can show me where a function is used; it can't summarize what a function does. Either way, you have to find where "fooItems" is used to know if you can modify the function.

Conversion functions are very easy to name ("itemToFoo"). Idiomatic Python would just use a loop:

    foo_items = []
    for item in values:
        foo_items.append({
            # foo properties
        })


The pythonic way is to use a list comprehension for maps and filters.


yes, but we were talking about passing an anonymous function as an argument and that was the example used.


Now try a multi-line one in Python.


> (my Python is pretty rusty so maybe there's a better way of doing that)

    return [x*2 for x in nums]


It's true js is better with anonymous functions. But the design of js (node esp.) requires you to constantly pass these callbacks to do async. Critics of JS are basically claiming that a programming style that overuses anonymous functions as parameters into other functions leads to unreadable and overcomplicated code... hence why python restricts anonymous functions from being over one line long.

Typically in programming you don't often encounter the need to pass a multiline anonymous function into another function, it leads to confusing code. Async/await gets rid of this bs all together which is the pythonic way to do async.

btw pythonic code for maps or filters is to use list comprehensions so

doubled = [x2 for x in nums]

or if you want to use a anon function:

doubled = [(lambda y: y2)(x) for x in nums]


I'd say that anyone who's complaining about callback hell isn't up-to-date with the JS world. Promises have fixed this problem and, as you pointed out, async/await make it even nicer.

In my experience (I write mostly JS but also a lot of C# and, not as much, Rust), passing functions around is very easy to read and reason about. Functional programming "Just Makes Sense™" to me. I think it's just an exposure thing. Many people find Python's significant whitespace cleaner. I disagree but I promise you it's because I haven't spend much time with it. If I wrote a lot more Python, I'd probably grow to like it.


First off, you aren't doing functional programming when you have first class functions or if you use HO functions like map and reduce. Those concepts can exist in imperative programs. The only concept that makes a program functional is immutability. That's it. Functional programming does not make sense to you if you don't understand this.

>I'd say that anyone who's complaining about callback hell isn't up-to-date with the JS world. Promises have fixed this problem and, as you pointed out, async/await make it even nicer.

The latest node api still uses callbacks. Promises haven't fixed this problem, it's just a bandaid on stab wound. Async/await is the proper way to deal with this problem and python supports it so there's no upside for node in this area.


Passing around functions is great, but it's easier to read and reason about when the functions have names. I see a lot of complex/nested anonymous functions in JavaScript code, even with promises.


    doubled = [x * 2 for x in nums]


I think he's complaining about the fact that lambdas in Python can't be multiline. The equivalent in Javascript can be as big as you want. That means in Python you have to separately define your function and then refer to it, whereas in JS you can just give it all in one go.

JS:

  doThing(function(){
    blah();
    de();
    blah();
  });
Python:

  def thing_doer():
    blah()
    de()
    blah()

  doThing(thing_doer)


This guy does sound incompetent. Sounds like he likes js because it's easier to use callback hell with multiline anonymous functions.

You can pass a function as a callback or something else in python. You just give the function a name then pass it. No function literals.

Also he's talking about async/await in koa... does this fool realize that async/await is built into python?

I'm proficient in both python and js and I have to tell you, python is light years ahead of js in terms of language design.


> Sounds like he likes js because it's easier to use callback hell with multiline anonymous functions.

Sounds like you're more pissed off at the article because it contradicts your views than anything else. Sounds like you didn't even pay full attention to it.

Yes, those who assumed I meant you can't pass an anonymous multi-line function to another were obviously right -- it dazzles me people spent so much time arguing over this point. There's not much else to it.

I'll just say: that's definitely NOT the sole reason -- I point out several other factors that weighed in my decision in making JS my #1 choice. Above all, after seeing a few successful large projects deployed (and no issues in production), I just came to trust the Node.js stability and ecosystem.


A lot of innovation happened in JavaScript space over the past few years and yet the learning curve is high. I prefer to use Python for Data Analysis, Data Transformations & Machine Learning. For web applications, Node/Express + React/Vue seems to work better.


Yep, Python is still king for machine learning.


> So with JavaScript you've got arrow functions, the method shorthand definition syntax, the spread operator, destructuring assignments, all functional Array methods and async functions.

This confuses me quite a bit, there are nuanced differences between these ideas in the two languages but at the surface these are things that very much exist in both languages

arrow functions:

  (x,y) => { x + y } 
vs.

  lambda x,y: x + y

method shorthand definition:

  MyObj = {
      foo(x,y) { return x + y }
      bar(x,y) { return x * y }
  }
vs.

  class MyObj:
      foo(self, x, y): return x + y
      bar(self, x, y): return x * y
spread operator:

  function add(x,y) {
      return x + y
  }
  add(...[1,2])
vs.

  def add(x, y):
      x + y
  add(*[1,2])
destructuring:

    [a, b, ...rest] = [10, 20, 30, 40, 50];
vs.

    a, b, *rest = [10, 20, 30, 40, 50]
functional array methods:

    forEach(["Wampeter", "Foma", "Granfalloon"], print);
vs.

    list(map(print, ["Wampeter", "Foma", "Granfalloon"]))
async methods:

    function resolveAfter2Seconds(x) {
      return new Promise(resolve => {
        setTimeout(() => {
          resolve(x);
        }, 2000);
      });
    }

    async function add1(x) {
      var a = resolveAfter2Seconds(20);
      var b = resolveAfter2Seconds(30);
      return x + await a + await b;
    }

    add1(10).then(v => {
      console.log(v);  // prints 60 after 2 seconds.
    });
vs.

    async def resolve_after_2_seconds(x):
        await asyncio.sleep(2)
        return x

    async def add1(x):
        a = resolve_after_2_seconds(20)
        b = resolve_after_2_seconds(30)
        return x + await a + await b

    loop = asyncio.get_event_loop()
    loop.run_until_complete(add1(10))
There's a lot of fun differences between the origins of these features and how they work but the sentence to me doesn't quite to the idea of "Leaving Python for JavaScript" justice


Why is all the actual content on the far right side of the page? Even more interesting given that you specialize in UI/UX.


Why not? In what way this is worse than having the same column of content centered or on the left side of the page?

I find it refreshingly different.


Hi, it's just a blog, not a paid job. Not a lot of effort went into it. I'll take a look when I have a chance (it looks fine on my MBP and several others).


I don't think it's broken. It just looks pretty cramped and weird if you've got your browser in a portrait orientation.


With async/await now landing in mainstream engines, Koa instead of Express, the new Turbofan+Ignition optimization pipeline "evening out" V8's performance characteristics, and ES6 enjoying reasonably broad support, I feel like JS is now crossing a threshold into being not a completely sucky programming experience, which is a sentiment this article echoes.

I'm probably being overly optimistic, but I look forward to a future where a "naked JS" movement rises up, similar to "vanilla JS", but rebelling against tooling complexity instead of jQuery bloat, in which your website's /js/ folder contains pretty much exactly what's in git.


I'm split on it myself. I like being able to go Right Click -> View Source and poke around but with more modern sites, you're given nothing but a single JS file. You can scroll through the DOM in developer tools but it's not as neat as text :(


> there's no acceptable way to pass a function body to another in Python.

What. Why would you ever want a function body as a string? What kind of vile sorcery are you doing? One can use inspect.getsourcelines in python if really pressed to, but I find it horrifying that javascript developers actually seem to require this functionality often.


The author did not say "as a string" and I am extremly confused as to how you read it like that... what? Essentially, the complaint (which is extremely common) is that functions that contain statements are not expressions in Python, so functional coding styles using things like .map() and .reduce() end up fragmented (and you are horribly tempted to play extremely insane tricks with decorators to build something that feels like a Frankenstein Ruby block); this is why the author then brings up arrow functions: it has nothing to do with strings.


> the complaint (which is extremely common) is that functions that contain statements are not expressions in Python, so functional coding styles using things like .map() and .reduce() end up fragmented

You mean these critics find

    >>> map(lambda x: x**2, filter(lambda x: not x % 2, range(11)))
preferable to the "fragmented":

    def square(x):
        return x ** 2

    def is_even(x):
        return not x % 2

    >>> (square(x) for x in range(11) if is_even(x))
Both result in the same lazy iteration (to see results below - requires materialization with a constructor like list or tuple), but I find the "fragmented" approach much more readable (so long as the functions do what their name says they do - I have seen Python where they actually did the opposite...)

    >>> list((square(x) for x in range(11) if is_even(x)))
    [0, 4, 16, 36, 64, 100]
    >>> list(map(lambda x: x**2, filter(lambda x: not x % 2, range(11))))
    [0, 4, 16, 36, 64, 100]


But your lambda example would be more clear if collections functions supported chaining. You can add a small comment if you want to be explicit. This way the code reads linearly, you don't have to wonder why a "square" function and "is_even" function are defined before you see how they are used.

    # square even numbers
    range(11)
      .filter(lambda x: not x % 2)
      .map(lambda x: x ** 2)
However such a chaining API is not practical in python, because lambda syntax is voluntarily crippled to one expression only.


Long method chains are impractical in Python because of the line continuation rules. That's orthogonal to the question of whether complex functions should be required to have names.


There are a few gotchas but you can do it. Mostly you need to put you multi-line chain call inside parentheses if it is not already inside a function call or data structure (similarly to generator expressions).

For example I often end up doing:

    something(
        "Template string {thing}"
        .format(thing=33)
    )


It's possible but rarely more concise or clear than just using variables.

    label = "Template string {thing}".format(thing=33)
    something(label)


I guess it is a matter of preference at some point.

Storing each step in variables has the advantage to be self-documenting and nicer when debugging. However in many cases I feel like wasting energy trying to find short and adequate variable names for each steps in a computation, especially when the steps are clear enough by themselves but difficult to describe in 1-2 short words.

You also need to keep the variable names in sync when refactoring, which may cause even more refactoring if the line gets too long with the new name.

IMO it makes sense to use both styles where they feel most adequate.


Programmers are terrible at naming things and we copy each other's mistakes.

In the real world you filter water to get the water. In most programming languages filter() is more like a strainer or colander.

My guess is you ran into someone stubbornly insisting on the meaning of the word filter despite all evidence.


Well truth be told it was hard for me to understand what the author meant. Thanks for clarification.


I'm guessing, since the author used the arrow shorthand notation in Javascript as an example, that the limitations of Python's lambdas are the chief complaint


It's more of a feature than an arbitrary limitation I think. Python philosophy, 'explicit over implicit', and that stuff.


An intentional limitation, then :P

I tend to agree with the Python philosophy, that it's usually better to extract and name a function that to declare it inline. The `asyncio` module and `await` keywords go a long way in eliminating most use cases where I'd still prefer an anonymous function, for arbitrary callbacks.


I cant figure out what he means either since he follows it up talking about map() and reduce() whcih you can use with function pointers (references? IDK what they actually are in python)


I edited my post, as after giving it some thought I concluded that the author wants to pass around the source code of the function. That indeed is something that python cannot provide in cases when function is already precompiled.


Higher order functions maybe? Though python has them. Spread operator can be done as unpacking a list. Destructuring assignment can also be done. Map is already there while reduce is in functools stdlib. So are all the other functional stuff. Async is in python 3.

Can't say what python lacked except being able use same language on front end and back end. Except if he still uses python 2 which hasn't any new features in years.


I want to like JavaScript, but the type coercion land mines and the lack of a real numeric tower make me a little queasy. When I have to use JS I stick to the transpile-to-js languages mostly for those reasons.


Is TypeScript one of your goto "transpile-to-js" languages? I'm a Python developer who is learning to love TypeScript. With TypeScript I feel like I can write maintainable, collaborateable JS.


TypeScript has the same number problem that JavaScript has.


ES6+ javaScript in absorbed many great features from python: destructuring vs. tuple unpacking, spread (...) vs *args argument unpacking and generators. Many things I enjoyed in python are now also idiomatic javaScript. Only thing missing is function decorators:

    @connect(...)
    function StateLessComponent(props) {

    }


I did the move but to GOLANG instead.

I think what is more important than the language it self, is the toolings and standard library.

every now and then you hear about a new framework, a lot of hype, which is not for big projects.

I think the wise thing is to move to golang


Why? I mean please elaborate.


> So with JavaScript you've got arrow functions, the method shorthand definition syntax, the spread operator, destructuring assignments, all functional Array methods and async functions.

In language that pythonistas would grok:

Arrow functions: multiline lambda definitions

method shorthand definition syntax: class defined with only classmethods, or in other words a namespace

the spread operator: *args in functions

destructuring assignments: tuple unpacking... But also for creation of instances

all functional Array methods and async functions: functools and async... Plus a bit more

While most of this is good to know, I'd still stick with python for readability and maintainability purposes.


IMO arbitrary rules like lambda expressions being limited to one expression do not directly improve readability and maintainability. Consistently using good conventions in the first place will help you more than arbitrary restrictions.

And to be clear, I would classify, say, Rust as not having many arbitrary restrictions. All the limitations that Rust has compared to C fit clearly within the language's core design. On the other hand, I don't see this same structure and consistency to some of Python's rules.


> IMO arbitrary rules like lambda expressions being limited to one expression do not directly improve readability and maintainability.

In Python’s specific case, I've seen no alternative that isn't bad for readability; the strong line-orientation of Python's broader syntax limits the good options for inline anonymous functions.

That being said complex lambdas in general can be adverse to maintainability and Python’s single-expression limitation largely prevents that (though you can make hideously complex opaque single-expression lambdas if you try.)


If you started with php instead of python you would never have left.

Vue is a great language but php can offer more in the backend


Thanks for your article. It's really helpful to hear about the technologies that have been used in actual projects/production environments. I'd love to hear more about what diminished your excitement with ElementUI vs iView


Mostly aesthetics in the default theme. Both are actively maintained and the APIs are super similar.


When reading these "Leaving X for Y" I first think that maybe some people are unable to fluently use more than 1 language.

Seriously, professionality is being able to use the right tool for the job at hand.


why is the sidebar on the left half the width of the page? I can hardly read the main content crammed into the right half.


> the spread operator

How?

JS:

  var args = [0, 1, 2];
  myFunction(...args);
Python:

  args = [0, 1, 2]
  myFunctions(*args)
A more complicated one:

JS:

  var args = [0, 1];
  myFunction(-1, ...args, 2, ...[3]);
Python:

  args = [0, 1]
  myFunction(-1, *args, 2, *[3])
As used in list building; JS:

  var parts = ['shoulders', 'knees']; 
  var lyrics = ['head', ...parts, 'and', 'toes']; 
Python:

  parts = ['shoulders', 'knees']
  lyrics = ['head', *parts, 'and', 'toes']
"A better way to concatenate arrays"; JS:

  var arr1 = [0, 1, 2];
  var arr2 = [3, 4, 5];
  arr1 = [...arr1, ...arr2];
Python:

  arr1 = [0, 1, 2]
  arr2 = [3, 4, 5]
  arr1 = arr1 + arr2
(Though the star notation works here too.)

I omit JS's use of ... on objects; Python's class system works differently — and IMO, more rigorously — than JS's, making ... less sensible on Python objects. (Python focuses much less on passing around untyped key-value bags and more on strongly typed classes IMO; both are possible in both languages, but the idioms around them differ, and I think Python's idioms tend more towards having a well defined class with well defined attributes, and not having just a bag of attributes, moreso than JS at least, and I feel that direction (well defined classes) leads to more robust code. In particular, it forces you into naming your concepts, and defining their set of attributes: a simplistic type definition.)

> all functional Array methods

Python has map, reduce, etc., as well as generator and list comprehensions which are often easier to use.

> async functions

Python and JavaScript have practically identical syntax in this area.

> there's no acceptable way to pass a function body to another in Python

I find it very acceptable that if your function body is more complicated than an expression, that you're forced to pull it out and name it, frankly. I think it does good things for fighting complexity, and this just isn't something I worry about day to day while using Python. But I will concede that Python does lack a syntax for passing a function in an expression context.

(But I would also note JS's screwed up named-function syntax; in Python:

  def foo():
    # body
is a valid statement. JS's:

  function foo() {
    // body
  }
is not a valid statement, and can only appear in certain, particular contexts. In particular, the following is not valid JavaScript, though many implementations will do The Right Thing™, I'm told:

  if(true) {
    function foo() {
      // body.
    }
  }
[2])

> My code usually revolves around higher order functions, reduce(), map(), etc. I can't remember the last time I wrote a regular for loop in JavaScript.

Because JS for a long time (until ES6's for(… of …) syntax) lacked a for loop (the C style look, and for(… in …), do not count, as they do semantically different things), which is why you're using forEach().

> arrow functions

The best thing about arrow functions is the sane binding of `this`, a problem notably absent in Python to begin with.

> [Python's] class definition boilerplate is still hard to look at

This is sometimes true; I find the attrs[1] package helps greatly here for small, struct-like classes.

[1]: https://pypi.python.org/pypi/attrs

[2]: http://kangax.github.io/nfe/#expr-vs-decl


Poor misguided person


I use to read hacker news and feel that all the articles were by people infinitely smarter than me. Nowadays I occasionally hit something like this. The fact that this makes it to the front page means many programmers are equally misguided...


Then guide the OP yourself.

Posting short inflammatory comments isn't going to change how people approach the topic.


Just in case the author is present in the thread:

Your blog is extremely difficult to read on wide screen monitors. The entire left 50% is basically just blank space. My suggestion is to make the left side maybe 20% width on desktop or less.

Also, regarding the article content:

There really aren't any good reasons presented here about why you moved from Python to Javascript for back-end web development. It seems that you simply prefer Javascript semantics. There's nothing wrong with that, but the title is misleading considering that the bulk of the article is actually about which JS tools you use on recent projects and not why you moved.

Also, could you clarify what you mean by

> Also, there's no acceptable way to pass a function body to another in Python.

Functions are first-class in python and can be passed as arguments to other functions. I'm not sure I understand what you mean with the above statement.

Cool to see someone else has been using Nuxt though.


> Functions are first-class in python and can be passed as arguments to other functions. I'm not sure I understand what you mean with the above statement.

I believe the author is referring to inline definition:

    setTimeout(function(){alert("Timed out!");}, 5000)
You can do something similar with lambdas in Python, but the allowed syntax is relatively limited compared to a full function definition. You could also define an inline function, but it'd be in a separate statement from the call you make with it. Unfortunately Python doesn't allow for creating and returning a first-class function in a single expression.


> Your blog is extremely difficult to read on wide screen monitors. The entire left 50% is basically just blank space. My suggestion is to make the left side maybe 20% width on desktop or less.

Thanks, I'll take a look when I have a chance. I myself don't own a widescreen monitor, the site looks fine on my MacBook and several other computers. It's not a paid job, not a lot of effort went into it ;)

> but the title is misleading considering that the bulk of the article is actually about which JS tools you use on recent projects and not why you moved.

Hm, actually, it's not. I'm surprised by this comment. I point out precisely the JavaScript features that weighed the most in my decision: "So with JavaScript you've got arrow functions, the method shorthand definition syntax, the spread operator, destructuring assignments, all functional Array methods and async functions. Combined with Vue's minimal patterns and Nuxt's conventions, I can't think of a better language to write web applications in."

> Also, could you clarify what you mean by > > Also, there's no acceptable way to pass a function body to another in Python.

Exactly that, a "function body", "closure", "inline function", whatever you wanna call it. In Python there are lambdas, but they're limited and not even encouraged anymore. We're left with passing function references, and that's where JavaScript comes out the winner IMHO.


"Anonymous function" is the term you're looking for. Lambdas are still encouraged for trivial functions; the only thing that's changed is that comprehensions have replaced many uses of map and filter. Python just requires you to name non-trivial functions.

IMHO, complex/nested anonymous functions are usually bad for maintainability, so I'd rather have a language that doesn't allow them (Python) than a community that overuses them (JavaScript).


I couldn't agree with you more. I think that Python's lack of complex anonymous functions actually forces the programmer to compose their code in a more efficient manner.


Python absolutely kills Javascript in the library department (particularly when it comes to doing stuff with tabular data). In my opinion the APIs for the built in data structures are also more intuitive and flexible. The __method__ protocols are also very elegant relative to what Javascript brings to the table. Of course, if you aren't doing anything fancy with data this point is partially moot.

I do admit that Javascript's object and array destructuring is extremely nice. The combination of destructuring and the spread operator makes working with heavily nested JSON much more enjoyable than in Python.


> Hm, actually, it's not. I'm surprised by this comment. I point out precisely the JavaScript features that weighed the most in my decision: "So with JavaScript you've got arrow functions, the method shorthand definition syntax, the spread operator, destructuring assignments, all functional Array methods and async functions. Combined with Vue's minimal patterns and Nuxt's conventions, I can't think of a better language to write web applications in."

I saw that. I guess I just expected you to go into detail about why you believe things like that are better. I use JS a lot in my day-to-day work for the front end of my web application. I also use Django and Python for my back-end. I'm well aware of the differences between Python and Javascript as they are the two languages I work with the most. I would not consider either language "better" than the other except at specific tasks. i.e. I can't really use python to build an SPA and it would be a pain to use Javascript for anything involving heavy number manipulation. I guess what I'm saying is that you should always choose the right tool for the job; sometimes that will Python and sometimes it will be JS. Other times, it might be something else. I'm always a little confused when someone decides that they're going to start writing everything in a particular language even when other languages are more suitable for the task.

> Exactly that, a "function body", "closure", "inline function", whatever you wanna call it. In Python there are lambdas, but they're limited and not even encouraged anymore. We're left with passing function references, and that's where JavaScript comes out the winner IMHO.

To each their own I guess. I feel like programmers these days automatically assume that more functional == more good. There are cases where using FP techniques is definitely advantageous. However, there are also circumstances where OOP is a better choice. I find that Python is equally capable of both paradigms. The idea of anonymous callbacks is good from a productivity standpoint, but readability suffers. Anonymous functions in general cause cryptic error messages and are difficult to debug. I've found that the over use of anonymous functions can also lead to a lot of duplicate code. There are many cases when an anonymous callback would be better if refactored into a named function. I believe that having more named functions aids in creating an efficient program compostion, even when using the FP paradigm. There is one use case where Javscript is much better suited than Python: web applications front ends. This comes as no surprise as JS was designed for exactly that purpose. Asynchronous callbacks and promises cannot be beat when dealing with fetching data from an API.


Python has lambda but they are one liners.

There is no way to do write this in Python:

   someFunc( _ => {
       // this is 
       // a function 
       // with multiple lines
   })
Having to define a named function to use it as a callback is a pain in the ass.

Python has an excellent standard library but the language itself is pretty mediocre IMHO. Classes are an afterthought thus verboses, as so is "functional programming" in Python.

JS biggest issue is that it is not strongly typed enough. But ES2015 makes it really pleasant to use.


I know the lack of multiline lambdas is a common criticism of Python, but I've never really understood it.

Typing

    someFunc( _ => {
       // this is 
       // a function 
       // with multiple lines
    })
Is only one line shorter than

    def foo ():
       // this is 
       // a function 
       // with multiple lines

    someFunc(foo)
There is a case to be made that the second version is more legible and that exposing the function foo makes unit testing it possible. I can understand why people might slightly prefer one or the other, but I honestly can't understand the intensity of opinion around this seemingly very minor syntactic difference.

I'm curious what you find verbose about Python classes as:

    class FooBar():
        text = "Hello World!"

        def hello(self):
            return self.text
feels fairly terse to me.


The problem with python class definition is the need to write instance vars 3x:

    def __init__(self, foo)
        self.foo = foo
There is an attrs package and new proposal (https://github.com/ericvsmith/dataclasses/blob/master/pep-xx...) to remedy that.


Yeah, I guess I normally don't deal with enough properties like that for it to be a big deal in my workflow. When it's come up I've generally done something like:

    class Foo:
        bar = 42

        def __init__(self, **kwargs):
            self.__dict__.update(**kwargs)
But I agree that's not a super robust solution


the problem is callbacks as a pattern

if you have a multiline function it should be named and unit-tested instead of just stuffed anonymously into some pyramid of doom


> if you have a multiline function it should be named and unit-tested instead of just stuffed anonymously into some pyramid of doom

It's like saying all data should be declared as variable, in the functional programming paradigm, functions are data. That's why closures are useful. And it has absolutely nothing to do with unit-testing, that's beside the point. Javascript is not harder to unit test because it has fully featured anonymous functions.


what does functional programming have to do with it? you still unit test functions in functional programming surely?

> It's like saying all data should be declared as variable, in the functional programming paradigm, functions are data. That's why closures are useful.

I don't think you understand what you're talking about. We were already talking about using 'functions as data', and closures and anonymous functions aren't the same thing.

But anyway, how do you unit test an anonymous function?

I assume you mean that you don't, you just test the bigger context around it. But that was my point, once your anonymous function starts becoming much bigger than a one-liner it ought to be tested itself.


> Javascript is not harder to unit test because it has fully featured anonymous functions.

It is if you actually use them. ;)


>> Having to define a named function to use it as a callback is a pain in the ass.

Happy debugging!


I am still learning to like Javascrip and its ecosystem. I do not think I would be able to invest time in transpile-to-js language (looking at you TypeScript). I really hate the evolving ecosystem, hyped libs / frameworks and tons spaghetti new paradigms.


>I really hate the evolving ecosystem, hyped libs / frameworks and tons spaghetti new paradigms.

Then don't use them. The javascript ecosystem on the backend and frontend has settled down quite a bit. Use something that's been established for a while (React, Vue on frontend or Express, Koa on backend) and you're not gonna suffer from churn too badly.


[flagged]


That's a personal attack and we've banned the account.

We detached this subthread from https://news.ycombinator.com/item?id=15099679 and marked it off-topic.


Like any other developer out there, I collected some bad experiences in my growth. In my 14-year career, 2015-2016 were spectacularly bad years for me, definitely the worst. It has to do with focus and productivity, not competence.

I must say I didn't think it was so bad as to leave this kind of sentiment behind. If you worked with me in that period, perhaps you'll be pleased to read this:

http://hire.jonasgalvez.com.br/2017/Jul/16/My-Approach-to-Re...

Since then I'm confident all my current employers will wholeheartedly vouch for the quality and persistence of my work, as you can easily peak from my GitHub graph.

http://github.com/galvez

I flagged your post nonetheless.

I believe HN is no place for ad hominem attacks.


I don't know how to say it without you taking it as a personal attack, but I think it's important to say so you can take the opportunity and learn or at least think about it; If you say things like that I quoted, you are incompetent and you have to learn a lot. I feel I have to say this because you send the wrong message to a wider audience which I really hate.

Also I skimmed your library httplib2 and it's at least terrible to read.

Again, you can take this as a personal attack, flag this comment also, be mad or whatever, or buy Clean Code and start reading, also start learning more about Python idioms from people who are really good at it, e.g. Raymond Hettinger, Armin Ronacher


httplib2 isn't my library, it's Joe Gregorio's, one of the best developers you'll ever read about.

As for the "if you say X, you're incompetent" line of arguing, seriously? If I say I prefer a language because it makes me write functional code faster (in my perception) and disagree that Python is better than JavaScript then I'm incompetent? Seriously, guys?

BTW, I dig Armin Ronacher's work too. His Python code, how beautiful it may look, doesn't diminish any of the points I raised about JavaScript nor my love for it.

I'm done paying attention to this thread.




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: