17 ms·
Fear, trust and JavaScript: When types and functional programming fail
- jeremychone 8y agoFirst, no language is perfect, and building a real system with growing team(s) is always about making purity vs pragmatism tradeoffs with the main goal of keeping the complexity down as the system scale. The truth is that a system is much more than just the languages it is composed of, and regardless of the purity, exactitude, elegance of a language, sloppy work will always lead to a messy result. In other words, languages should not be used as a substitute for discipline, good patterns, or scalable best practices, but rather as an enabler of such conducts. TypeScript favored pragmatism over purity, but it is a very well thought-out, robust, and expressive typing layer on top of JavaScript. Sure, for the sake of language purity, developers can "bytecode your way" to JavaScript Dart, Java with GWT, ReasonML, Elm, but there is a reason why all of those options, how elegant they might be, have limited traction and relatively short lifespan (at least compared to JS), and that is because they add too much friction points (often unexpected ones) for the sake of language purity, which at the end is a never-ending quest anyway. TypeScript is a structural typing system which has its differences with commonly known nominal typing systems (which often confused with soundness), and while there are some unsoundness is some specific cases, overall, TypeScript is a very robust and expressive typing system on top of the most pervasive language, JavaScript. Not perfect, but very robust nevertheless and a huge step up from traditional JS. TypeScript combined Modern JS (module, let/const, async/await/promise, arrow function with related scoping, deconstruction, ...) does create a very robust environment to scale code and developers. We used to all our backend in Java for 20 years (personally comes from Oracle) and we now see nodejs / typescript ecosystem and runtime environment has competitive for big app system. For one of our client, we actually just ported the last of our multi-service system from java to TypeScript, and it got 40% less code and it is better typed than Java. Java's high typing friction and Class-For-Everything purist approach makes good code design more of a contortionism exercise than an intellectual one, and while nowadays most agree that this OO-Obsessed approach was the wrong one, it was considered very pure and "robust" at a time. Anyway, the short version is what makes a big code base scale is not the purity of any given part (e.g., language) but how the whole can minimize present and future friction points. The less friction, the more velocity. Discipline, best practices, tools, environments, and ecosystem are all part of a whole and one needs to make sure to not over optimize one part on the detriment of the others.
- purescriptfan11 8y agoThe author of this article also has a great talk in on the subject: https://youtube.com/watch?v=IvPBMEYxP-Y https://youtube.com/watch?v=IvPBMEYxP-Y
- moocowtruck 8y agowill verify this was a good talk
- stephenr 8y ago> In a dynamic language like JavaScript, it can be hard to know what the shape of your data is. Being dynamic doesn't preclude variables from being type checked by the runtime. Reading this just sounds like "javascript has no built in support for type checking, so lets pretend it's not possible" All the time people spent making weird unreadable 'fat functions', but you still can't define the types of arguments you want to receive?
- sedeki 8y agoI guess he means ”knows [at compile time]”
- qaq 8y agoif someone invented a typed superset of JS life could be so much easier Oh wait ...
- stephenr 8y agoTypescript doesn’t actually do runtime checking though does it? So if I wrote a library in ts and someone called the built js directly it wouldn’t have any type checking would it?
- qaq 8y agoWhen you are doing interop between languages a lot of things can go wrong. Rust is pretty much doing compile time checks too.
- mattbierner 8y agoNo it doesn't. This may not be a popular opinion for those coming from C or Java, but runtime type checks in JavaScript are usually an anti-pattern. If my function defines a contract that the caller violates by passing in null or undefined, then my function should raise an exception when it tries to access `undefined.xyz` or whatever. Otherwise, embrace the duck! If the function can work with what it was given, it should instead of trying to enforce some Java-esque type system at runtime using typeof or instanceof. But be pragmatic. If there is some critical area of your app that absolutely must never ever throw, typecheck away (although this is JS after all, so it still may throw if it damn well pleases).
- ridiculous_fish 8y agoOne of the strengths of JS is how the runtime environment can be dynamically interrogated and polyfilled, and how it can often gracefully degrade if there's a mismatch between static assumptions and runtime behavior. Is there a vision for how PureScript/ReasonML/Elm etc. can accomplish the same thing? The brute force approach is to place a boundary between everything external and handle it via an FFI. This adds friction: apps built under this approach are less capable and evolve less gracefully. I hope there's a better solution.
- proyb 8y agoLook like this is one of the idea? Build on Crystal programming language with less to code and reduce clutter. https://www.mint-lang.com/ https://www.mint-lang.com/ Mint to Javascript is what any languages to web assembly. I’m not sure who is comfortable to evaluate Real World code example: https://github.com/mint-lang/mint-realworld https://github.com/mint-lang/mint-realworld Along with some Crystal shards (LLVM, assembly, C librares) to validate your data or other structure formats is a breeze to work in.
- alehander42 8y agoNim is another language with a JS backend and it is also kinda good at this: it has a pretty good type system, but it's easy to use it gradually for JS code. You have a `JsObject` type (which is basically `any`) which lets you write your Nim->JS code in a dynamic way and cast to static types whenever you need to interface with static parts of your program. You can still also define the type signatures of a JS API as well and have a fully type safe experience, but you can do it lazily: just for the functions/types you need. Basically you can use a JS api in any way you want: from almost fully dynamically to gradually more and more type safe way
- dsign 8y agoBroken promises in Javascript? ... unsurprising ... Here is an imaginary article that I would read: "New web platform runs entirely in webassembly generated by well-typed languages". Subtitle: "When hand-written Javascript is detected, continuous integration activates subdermal shock-collar that each of the developers is contractually obliged to have".
- Sawamara 8y agoI just love how you managed to come off more elitist than an ivy league school frat boy realizing that there are workers cleaning up his mess after every party they throw.
- stephengillie 8y agoJust make sure the Promise has a Catch to handle errors. Err...you're talking about promises from the community, not Promises in code.
- bad_user 8y agoThe article builds a strawman. It's not the types that fail, but the type system and its misuse. > This is like pretending really hard No, that's not pretending — static typing is literally about theorem proving, type theory being equivalent with math logic. This is the famous Curry-Howard isomorphism, types corresponding to propositions. Of course, you can have situations in which your type system cannot describe the propositions that you need, or situations in which holes are left for pragmatism. Such holes include for example the ability to use Object and then downcast in Java and have the implicit contract that the people using them know what they are doing, being at the same time a symptom of the type system not being expressive enough. In TypeScript in particular you can just use the "any" type, which is there for pragmatism and because JavaScript developers would have had a hard time adopting it otherwise. It doesn't help that TypeScript's generics are unsound either. Of course, you can point at "the outside world" and say that you can't trust it to deliver the types that you expect. But with a good type system, you only need to do that runtime validation only once, at the edge where you receive the data and afterwards the statically typed compiler can take over that responsibility. And indeed, in my experience libraries for JSON parsing in Scala (e.g. Circe) or Haskell (e.g. Aeson) are very well behaved and due to automatic deriving, they make that validation painless in 90% of cases, because the runtime validation is derived from the static types automatically, no work required, something I haven't seen in TypeScript. The whole notion of "optional types" is my opinion flawed. You either work with static typing, or with dynamic typing and choosing a side will change the way you work, it will change your mentality, your approach to problem solving. Working with optional types, which is what people tend to do in languages like TypeScript, brings you the very worst of both worlds. Optional static typing doesn't work. A contracts system, like Clojure's Spec, works much better for dynamic typing and yes, there are fundamental differences, basically the difference between "for all" and "exists", or between compile time and runtime. --- > Convention: Pretend immutability N.B. in an actual FP language, immutability is not a convention, but something being enforced by how data structures are built (e.g. persistent data structures), via the type system or the runtime system — even in Java, you'll have a really hard time to change the definition of a class or of a "final", or to mutate a persistent collection (see vavr.io). --- I like TypeScript overall, I think it's an improvement over JavaScript and it certainly has pretty cool features — but don't use it to judge either static typing or dynamic typing or functional programming for that matter. If you want to see actual dynamic typing and how it can shine, use ClojureScript. If you want to see actual static typing and how it can kick ass, use PureScript. And both are good for giving you a real taste of functional programming. JavaScript and JavaScript++ languages, are really poor at FP, mostly due to the ecosystem's culture, and the very few FP libraries that exist for JavaScript are very unpopular, with only a few gems here and there, like Rx.JS. So if you're doing JavaScript or TypeScript, chances are your codebase doesn't do much FP.
- Tade0 8y agoBut in JavaScript, the fear is always with you. That's why we have both tests and code reviews. Aside from that I'm surprised the author didn't mention checking constructors. It's arguably the most dependable way of checking if something is of a certain type. But I agree that it's a shame that TS/Flow lose their ability to check types whenever partial application or currying is involved - it's not like it's impossible in principle.
- arcticbull 8y agoI did Objective-C work for a decade under such conditions. It's never enough to have tests and code reviews. Static typing is strictly better.
- Tade0 8y agoI worked on a 150k LOC pure JS codebase for almost two years and my experience is different - if tests aren't enough then they're either not written well enough or there isn't enough of them.
- tempodox 8y agoYou would never be able to write the infinite amount of tests necessary to supplant static typing, let alone run them.
- qwer 8y agoWell don't lose sight of the goal. The goal is less bugs. Static typing is just for internal quality. Users don't give a rat's ass if your types are correct as long as the program works. Proof: Many wildly and massively successful programs are duck-typed. Nobody ever cared to use tests to replicate what a type system does. That's a straw man. Tests go straight for the actual goal by testing actual values. Correct values are what are necessary. Types on the other hand are neither necessary nor sufficient.
- 8y ago
- austincheney 8y ago> In a dynamic language like JavaScript, it can be hard to know what the shape of your data is. You don't need static typing to program defensively. Static typing instead ensures a certain defensive position, but as a developer you can choose to program in such a way even in a dynamic language. That being said it is foolish to make a blanket assumption that data is doomed to confusion. The state and shape of data is determined by the authoring application and reinforced by the receiving application. If data is shaped improperly then a defect is present in the system, so fix the defect. If a dose of common sense is still not enough... then program in TypeScript.
- arcticbull 8y agoIt's nothing to do with "a dose of common sense" so much as it is the reality of dealing with humans. Humans make mistakes. Static typing is automated checks to make sure you didn't. When doctors start using checklists their patients do better [1]. Static typing is checklists for programmers. In theory weak dynamic typing works great. In practice humans suck. Hence checklists. [1] https://www.newyorker.com/magazine/2007/12/10/the-checklist https://www.newyorker.com/magazine/2007/12/10/the-checklist
- austincheney 8y agoThis completely ignores my comment in its entirety. Do people make mistakes: yes. Does software ever have defects: yes. Typically, people making mistakes in software is called a software defect. It is absolutely critical in a discussion like this to understand that defects in software are not necessarily defects in data. This conversation is about data, and checks for processing data are already present in various forms. For example when was the last time you complained about weak type systems when working with JSON, XML, or SQL tables?
- Quekid5 8y ago> For example when was the last time you complained about weak type systems when working with JSON, XML, or SQL tables? Well, I don't know about the person you're replying to, but I certainly complain about these all the time. Interestingly and counter to your point, I think, XML grew a "type system" of sorts with XML Schema (which is widely used IME, esp. where interfacing with external services as in SOA), and SQL actually has a reasonably strong type system for most implementations albeit dynamically checked at query planning time. (SQLite seems to be the outlier here.) JSON is actually the outlier here, but even with JSON there's been attempts at a sort-of type system with JSON Schema. Why? Well, because beyond a certain size the "just chuck some data somewhere in there" ceases to be workable when you want stable and maintainable interfaces between software components. Ultimately type systems are invented and used for almost entirely pragmatic reasons.
- cetra3 8y agoI love TypeScript and use it in any new project I create. The author is right though, you need to have team discipline to avoid using the `any` type when it can be avoided, and immutability is not in scope. TypeScript is probably the closest we'll get to my personal ideal of static typing in JS, while understanding that you sometimes need an escape hatch to deal with dynamic typing as JS is a dynamic language. I think that the focus now should be allowing other more modern languages in the browser by means of Web Assembly. Rust is a great candidate for dealing with immutability and unsoundness concerns. I can't wait for wasm to be fleshed out more. It does beg the question though, would web development be as ubiquitous and as popular if the core language in was statically typed? Does the freedom JS offer you allow greater adoption overall, meaning that it's beneficial to loosen the type restriction as the ecosystem will flourish because of it?
- ritchiey 8y agoYou don’t need to wait for web-assembly to solve these problems. TA mentions 3 languages (Elm, Reason & Purescript) that combine sound type-checking and immutability right now. None of these bring the cognitive overhead of Rust’s borrow checker and, in the case of Reason at least, compile at lightning speed to readable (though optimised) JS.
- leshow 8y agoyou can also enable things like "strict", then you need less team discipline, it will be enforced by the compiler. People can still use 'any' explicitly (but not implicitly), however
- jorblumesea 8y ago
- deleted 8y ago[deleted]
- maxhallinan 8y ago> in various cases the types are wrong and the compiler doesn’t care It's disturbing how often I encounter this in TypeScript. Or its inverse: the types are correct and the compiler is wrong. Or a third common problem: the types for a library are incorrect. The unsoundness of TypeScript is not merely theoretical. The compiler is frequently just wrong. For that reason, I am mystified by the amount of enthusiasm for TypeScript that I encounter online.
- Sawamara 8y agoTypescript brings much needed order to the complete (seemingly) typeless nature of Javascript. That is the source of enthusiasm. TS is the direct result of a development team working out the kinks of having to maintain a large JS codebase.
- ItsMe000001 8y agoSame here. I have been using Flow (mostly) and TS (occasionally, including counter-checking problems in Flow to see what TS does in a similar case). Overall I would not go back to the time without a type checker, however, I too am mystified by the enthusiasm. Anyone who wants to check the state of static type checking for Javascript should take some time and read through - https://github.com/Microsoft/TypeScript/issues https://github.com/Microsoft/TypeScript/issues - https://github.com/facebook/flow/issues https://github.com/facebook/flow/issues I do that quite a lot myself and have also contributed a tiny bit to both projects (no core code, things like definitions, small doc improvements, quite a bit of answering to issues and often checking the posted code for myself, often in both Flow and TS). Ignore the issues posted by people who really would just need a forum to ask questions, there are plenty of real issues left. Worst part: Many of them won't be solved (too hard, too much work, too many issues overall). You have to change your coding style and write in a way that the type checker can actually help you with. Also, it's easily possible to end up with types that are far more complicated than the code they are supposed to describe. I spent more time working on the types than on the actual code. What makes it worth it is that one, I get some control over the code other people write using my library, if I insist they too use the type system I can prevent them from misusing the API to a degree. Two, those other people also includes myself in future incarnations. Three, refactoring can be significantly easier, if you have good types the checker will tell you all the places you missed changing. So overall, at least for my situation, mostly for writing a library with few external dependencies (so I don't need the more or less unreliable external type definitions) that the business heavily relies on in many products, adding the very considerable additional effort is worth it. Still, I very much disagree with all the enthusiasm. The type checkers are software trying to understand software, and that software that it's attempting to check is not just an already complex dynamic language, but in addition on top of it is people's code that comes in a million styles. Those type checkers can be valuable, but they are far (very far) from perfect, and they come at a considerable price (mostly in the time it takes to create and maintain the types, I think the additional step to remove type annotations for production code is pretty insignificant in comparison).
- nathan_f77 8y agoThe author talks about Immutable.js, but doesn't mention Records, which have been a real godsend in my application. I use Immutable.Record with types [1] (using Flow), and I feel very confident with this setup. You are forced to set default values for every key, and the code will crash if you try to set a key that hasn't been defined. It's really great to have typed Records (often nested), and I expose the getters as regular properties so that I don't have to call "get()" all the time. I agree that it's not perfect, but it's miles ahead of anything I've worked with in the past (at least in terms of JavaScript.) JSON schemas are awesome, and I like the idea of validating types at runtime on the front-end. I already have a JSON schema that defines my API (I use Swagger), so I could even go one step further and auto-generate my typed Records directly from the schema. I'll get immediate feedback about any type errors in my JS codebase as soon as I make changes to the API. I generate my Swagger API specification automatically from some RSpec tests (using rswag [2]), and I also run Flow during CI, so this would result in a broken build. I could also make this work with the data I'm inlining in the HTML to hydrate the Redux store. Random aside: I also have a one set of data that just gets dumped into a jsonb database column, so the schema for this specific data is only being defined by the front-end code. In my backend I'm always careful to provide a default value, but it's not really critical data. Anyway, I just realized that I should be defining this jsonb column as an actual model, but instead of ActiveRecord, I could use ActiveModel and a plain Ruby class. That's a neat idea. It would give me a schema and validations on the backend, while still having the flexibility and performance of storing everything in a single json column. [1] https://gist.github.com/glenjamin/75a96b45f4bb5c6ac221815d28c548dd https://gist.github.com/glenjamin/75a96b45f4bb5c6ac221815d28... [2] https://github.com/domaindrivendev/rswag https://github.com/domaindrivendev/rswag
- azernik 8y agoProbably the reason is that reasonable Flow types for Records aren't in the mainline 3.0 release, and 4.0 (with good static typing support) has been in RC hell for about a year and a half.
- scns 8y agoWith Bucklescript/Reason you get typesafety, immutability, performance and small codesize: >Now we compare the runtime performance: BuckleScript Immutable Map: 1186ms Facebook Immutable Map: 3415ms We also compare code Size: BuckleScript (Prod mode): 899 Bytes Facebook Immutable: 55.3K Bytes source: https://github.com/BuckleScript/bucklescript/wiki/Why-bucklescript-matters-for-Javascript-platform https://github.com/BuckleScript/bucklescript/wiki/Why-buckle...
- jhabdas 8y agoCountless fear avoidance trick #465: use browser form semantics for ui state and leverage mutation observers and proxies to delegate the rest. nice article
- sbr464 8y agoI agree that defensive checks may clutter code, but in the current landscape of constant security issues, to not use a battery of defensive checks whenever dealing with input is simply naive, no matter the IDE, compile language or runtime language.
- sbr464 8y agoTo help with readability but allowing for complex input checks, I typically abstract the input validation to another function, called near the top. I think as time goes on type checking will be the least of our issues. Checking for different types of abuse (technical, social, semantic), context sensitive boundaries, setting tripwire-esque traps to control abuse etc will make any current concerns over type checking seem elementary. We need to give up on the idea of coding 3 line, beautiful functions that look great on Twitter and realize we live in a time where defense and privacy are a priority, while (some how) simultaneously pushing innovation. I hate it, trust me, but if you talk to typcial corporate/business clients, this is where their thoughts/concerns are currently. I realize it’s a bigger conversation than what I’m mentioning here.
- dgreensp 8y agoStatic types are great because they make certain classes of bug impossible to write, but they have their limits. There are escape hatches, like casts and Object types, even in statically-typed languages, though fewer of them in languages with more advanced type systems. There are reasons to like PureScript over TypeScript, just as there are reasons to like Haskell over Java. TypeScript doesn’t give you a way to ban side effects, for example, being part of the imperative family of languages that it is. So if you are super into banning side effects you might like a language that does. The thing to realize is that at the end of the day, all you are doing is reducing bugs, and while your type system (IMO) really can help reduce bugs, it will never catch all bugs. There is still value in a function declaring that its argument is of type User (and not defensively checking it) even if it is theoretically possible, in the presence of a bug, though unlikely, that the argument is not really a User. The function has said what its precondition is, and (assuming you are validating any data that comes over the network or from an untyped source), this precondition will in practice be checked 99% of the time. If a bug happens to line up with a hole in the type system, you’ll just have to debug and fix it.
- masklinn 8y ago> Static types are great because they make certain classes of bug impossible to write, but they have their limits. There are escape hatches, like casts and Object types, even in statically-typed languages Even those don't have to be escape hatches as such, in the same way e.g. List.head doesn't have to be partial, that was an explicit decision. When casting from rust's Any trait to a concrete type, you get an option. There's no subversion of the type system, there is no runtime error unless you as the developer decide to generate one.
- stephengillie 8y agoI really like the type system in Powershell and wish JavaScript had something similar. Typescript is a mess by comparison. [string]$String = "Dog"; $String = 5; Cannot convert value 5 to type "System.Objects.String"
- int_19h 8y ago
- slifin 8y agoAfter watching the value of values by rich hickey I've become cynical towards complex types, I think my ideal would be maps and scalar types and maybe something like clojure specs for more complex checks
- messit 8y agoIt fails all with the teams opinions. It's the people not the language or tools. All these discussions of developers trying to be correct. You don't need typescript, flow, functional programming, OOP, whatever. These tools have it's place particularly because teams cannot work together and need some kind of rules. In most cases individual developers differ quite a bit from each other, especially when it comes to their opinions. It's dramatic.
- jondubois 8y agoIt's interesting this article makes a very similar point as a comment I made a few days ago about trusting developers: https://news.ycombinator.com/item?id=18255777 https://news.ycombinator.com/item?id=18255777
- gigatexal 8y agoIs having to use something like a json-like schema just admitting that the language is broken? If you can’t trust or use common practices inherent to the language to do things in a typed way if needed then what is the point of a not strongly typed language?
- sbr464 8y agoTo be honest, just having a defined, literal type is not going far enough. Is there a built in language strategy, in Rust for example, to define a complex numerical type that has common properties like min/max range, exclusive min/max, conditionally required logic etc? Of course you can put some custom checks/logic to validate, but not sure why people instantly shame json-schema. From a validation perspective (not tooling) typescript is simplistic compared to a reusable, extended json schema. It may be a matter of time for other js/ts tools to improve. I’ve been learning Rust and I haven’t seen a better solution currently for runtime/user input validation. (Not a c/rust expert by any means)
- steveklabnik 8y agoCurrently, you do runtime checks. We’ve accepted an RFC for integer generics that will let you do compile-time checks instead, but we’re still working on an implementation.
- sbr464 8y agoThanks Steve
- sbr464 8y agoI think shortcuts like the following are fundamentally flawed, at least when dealing with user input. If (user && user.name) { ... While it sucks to have to even check for user etc, not checking that it’s truthy, then an object, then checking that the name is the correct type also during runtime, will cause bugs over time. This type of developer provided shorthand validation is pretty much the cause of all hacks/data breaches currently. It’s more of an attitude or how a developer prioritizes their own wants/needs over maybe a client/end user. Like I mentioned in another comment, checking for more advanced, abusive behavior will make issues with type checking seem basic in comparison.
- terryf 8y agoYes. That's why a good language makes this explicit in the type. For example in Scala, this example would be that user is of type Option[User] and the name property would also be Option[String] for example. Then to safely get the name would be: user.flatMap(_.name) and the resulting type would be Option[String] Which would then carry on the info that you don't know for certain that you know the name.
- nycdotnet 8y agoWill be nice when JS gets the Elvis operator and you’ll be able to do ‘if (typeof user?.name === “string”)’ which will have the added value of type narrowing/interaction with ‘never’ if name is expected to be something else.
- always_good 8y agoI'd say the elvis operator would make things worse, especially wrt the parent post. The problem isn't that `user && user.username` style validation is verbose, but that it's very error-prone. Making it less verbose just means you'll more likely use it before you reach for a better validation strategy, like a declarative validation library.
- maglite77 8y agoMany of these concerns seem to not be limited to Javascript at all - especially once you're sending/receiving data between systems. In Python for example, when receiving JSON/XML payloads you have the same defensive parsing layer at the point of entry, but humans can still mess that up and monkey patch a class that breaks assumptions elsewhere without warning. Did I miss something in the larger argument?
- hoodunit 8y agoNo, that is exactly correct. The brunt of the argument applies to most popular languages- Java, Python, Ruby, and what have you- although the specifics vary.
- nabla9 8y agoThe larger argument is that you can't have the "language level trust" until you push __everything__ trough the same comppiler/transpiler/validation pipeline and can trust that everything has gone trough it and it has not been modified by other means. Using Javascript as a platform where you mix code generated trough different conventions and tools is a halfway solution. Javascript treated as the unmodifiable binary from the compiler creates same level of trust as using other languages. Technically: * In C++ somebody can use different header for the class and break the class abstraction. * In Haskell some library might go trough the FFI into the runtime and assign values to data in non-functional way and break the functional abstraction. * In Python and Java there are ways to access data in ways that circumvent the compiler or the language semantics. The difference is how strong the convention against doing so is and does breaking the abstraction have any benefits.
- ghosthamlet 8y agoJavascript is maybe the language made or suitable for simple app, If you will make a big complex app, There is a simple way to fix this: use more strong languages Compile to Javascript like Elm/PureScript/BuckleScript/ScalaJs/Nim/ClojureScript(with core.specs) and so on[1]. [1] https://github.com/jashkenas/coffeescript/wiki/List-of-languages-that-compile-to-JS#static-typing https://github.com/jashkenas/coffeescript/wiki/List-of-langu...
- Kaveren 8y agoAdditionally, you don't even need to use language implementations that compile to JavaScript. WebAssembly is progressing and is quite arguably ready for production use. I really like Rust personally and think it's a good option for web applications, but there's many options that are becoming available.
- ken 8y agoYes, that's exactly what the conclusion to the article says. As someone who has (almost entirely) given up writing plain JS, my skimming of this article is "2500 words about issues with JS mutability, then: just use ClojureScript".
- snek 8y agoObligatory link to Destroy All Software talk about types: https://www.destroyallsoftware.com/talks/ideology https://www.destroyallsoftware.com/talks/ideology
- kltutor 8y agopretty cool, just learned about destroyallsoftware (long term lurker here)
- Aqueous 8y agoNot sure what this author is saying here. Types have nothing to do with being able to trust other systems and everything to do with the internal formal consistency of the code that you write. It's about being consistent with yourself. If you find yourself writing defensive checks in fulfilled promise handlers because the data you get is sometimes invalid, stop. Get up from your keyboard, go to whoever owns the endpoint, and ask them why their endpoint is returning a 200 OK response with data that doesn't live up to its spec. This is a bug in their system, not yours.
- mlthoughts2018 8y agoBut the gRPC purist who wrote that endpoint while mandating that unit testing private methods is philosophically worse than genocide and using a slackbot to append “don’t write tests for the compiler” to every pull request got a huge payout to leave the company last year before sexual harassment complaints could move forward.
- user5994461 8y agoIf your software crashes on bad user input, that's an error in your software. By the way, there is distinction to make between library code that should exception/assert/abort on error and application code that should verify input and display an error to the user.
- Aqueous 8y agoCorrect - but in the case I described above, you (and your program) are the user.
- apo 8y agoAs developers, we want to reduce fear of our code failing and increase trust in our code working well. ... The author doesn't mention the critical need for any nontrivial JavaScript project to build and maintain an automated test suite.
- karol 8y agoThe author seems to confuse static code analysis with runtime values.
- ender7 8y agoThis article is just a recapitulation of the old pure functional programming vs. imperative programming argument but wrapped up in new packaging. Most of these issues, such as needing to validate types at codebase boundaries, functions that have side effects, and unsound static type systems, could just as easily be leveled at most imperative languages (C++, Java). So yeah, go use a proper functional language (many of which compile to JS!). Complaining that an imperative language isn't a soundly-typed functional language is tautologically true I guess, but who cares?
- deleted 8y ago[deleted]
- KirinDave 8y agoIt's not really, though. Because even pure FP folks read this article and say, "This is just nonsense and ill-considered propaganda." For example, the idea that you can't trust Typescript because it might call untyped code but you CAN trust Purescript is just wrong. It's not even just wrong, it's utterly misrepresentative of what actually happens in Elm and Purescript. It's misinformation.
- uryga 8y agoCould you elaborate? Why is it wrong/misinformation?
- KirinDave 8y agoPurescript code calls into untyped code via its FFI for expediency or async integration constantly, for example.
- adimitrov 8y agoNot OP, but I'd guess the idea is that calling untyped code in TS is basically Pushing the Big Red Button, or Launching the Nukes™. When doing that in TS, you do it knowing it's your own responsibility to test and establish trust in the code. And Elm and PureScript are the same. In other words, Elm and PureScript won't save you from screwing it up the way you can with TS. All pure/statically typed languages have escape hatches. It has been leveraged as a counter-argument to using them. But but but, they will say, Haskell has unsafePerformIO!!! Well, yeah, sure it does. You can subvert the borrow checker of Rust, too. But you have to do it explicitly. You can grep your code base for "unsafe" (literally). You can lint for that. You can code review for that. You can document it. And that's what makes it OKish. In a way, types are like big fat oven mitts, and you're the lead programmer of a bakery, where you pop buns in the oven all day. You can discard the oven mitts for a time, though, and do whatever delicate work you need your actual fingers for. Just don't touch the hot stuff, lest you get yourself burned!
- Nitramp 8y agoIt's a bit hard to follow what the author wants to tell us, so I might be misrepresenting below (sorry!). I don't think the assertions that (I think) they make hold though, or are particularly useful. The author claims "functional programming, types, JavaScript: pick two". He supports that with links to a number of libraries that have incomplete type definitions, and a few patterns that are hard to express in a static type system. I don't think that assertion holds. Some JavaScript APIs are indeed written in a style that's very hard to give static types for, and some libraries have sub-par type definitions. But odd JavaScript APIs != the entirety of functional programming, and sub-par type definitions are just missing features that need to be fixed. You can write perfectly fine, statically typed, functional programming idioms within the TypeScript type system (and presumably within Flow). You can even do that within the confines of Java's type system, and that's a lot weaker than TS' or Flow's. These type systems are optional, and you can subvert them. If you use the recommended strictness flags though, it's hard to do so accidentally.
- KirinDave 8y agoThis article is bizarre. Firstly, the author states the problem that data can propagate and suffer loss/mutation over time. He then cites types (which absolutely do solve this problem), but then dismisses them saying: > Adding types to our example doesn’t solve the underlying problem. It improves trust within the code base by helping to ensure that data is used consistently, but it says nothing about data received from the outside world. But anyone who's used type-based programming knows this is nonsense if you simply write a typed parser (of particular interest to gradually-typed Javascript is packrat-style parsers). Then, your code needs to specify what it wants and what its policy is if it doesn't get it. But if the author was imagining that compile time integrity and correctness checks could EVER perform runtime checks... well... they're not even wrong they're misunderstanding the function so badly. The author then proposes out of left-field: > But it’s like plugging holes in a leaky ship. The problem isn’t just that you can’t trust the types in your system, but that you think you can. You rely on the types to tell you when a change breaks something, but because they were quietly disabled by an any type, or by use of a library, or by a soundness issue, it doesn’t. Types in JavaScript are different from types in most other languages people use: They can’t be trusted in the same way. Which is both true and useless. Yes, it's true that type circumvention methods exist, and you never really can know if you're calling into a library that circumvents your type constraints unless you read the code. But yes, that's useless because nearly every typed language also has this mis/feature, including OCaml, Haskell, C++17 and even Rust! So the author is not actually giving a real recommendation or even novel insight. And then we get the final substantive giving us arguments that the author has themselves just discredited: > Or you change the game and just use PureScript. Or ReasonML, or Elm, or even ClojureScript. These exist today. Production software runs on them. They work with the JavaScript ecosystem, where necessary. And they provide a higher base level of trust in the code that you write and an environment where immutability, functional programming, and types (where applicable) work well and work together. Firstly, I've written a fair amount of Purescrpt and we call into unsafe code ALL THE TIME for the sake of expediency in Purescript. To even pretend otherwise is just... either it's willful lying or someone who has barely interacted with the Purescript ecosystem in any production fashion. If you use a dependency and don't vet it somewhat (or at least trust a crowd to vet it as a minimal effort) then you're in for a bad time. Pretending otherwise, or that this is a unique flaw of Javascript, is just wrong. And if you disagree with that, then I have a challenge for you. I propose to you that you can substitute ANY language and runtime's type and GC as "types" and "assembly" as underlying javascript and not change any underlying axiom presented in this article.
- agentultra 8y agoBut you still have to validate and parse unstructured data in Haskell as well. And what would be the point of having the type checking done at run time? That's what many dynamic programming languages already do. If you're curious about Flow or TS then you must already have a problem with that approach. Type checkers are tools that help us reason about, structure, and correct code. That's all. Flow is decent these days but will probably never be as powerful as Haskell. My only gripe is that it forces my team to write verbose code at times in order to gain the benefits: you lose point-free style, gain a bunch of identity and instance checks... but it's a small trade off to make. One small trick worth remembering and using in Flow to gain something similar to exhaustive type checking is to cast to the empty type in the default case when pattern matching on a type[0]. It's not an elegant type system but if you have experienced a good type system (like Haskell's) then it does make using Flow/TS much easier. [0] https://medium.com/@ibosz/advance-flow-type-1-exhaustive-checking-with-empty-type-a02e503cd3a0 https://medium.com/@ibosz/advance-flow-type-1-exhaustive-che...
- erikpukinskis 8y agoCompanies and coders will always push themselves to the absolute brink, where they no longer trust the code and the system is nearly unmaintainable. Because every step towards that line has not just cash value, but exponential cash value if it’s a startup. And by constantly pushing themselves into unfamiliar territory coders develop the most impressive possible resumes. They either have relevant experience or have experience in a tech even beyond what the company they are interviewing is using. That’s your best position to get hired. It’s easy to write JavaScript you can trust. Heck, you can write a trustworthy web server in Visual Basic. Trustworthy code just isn’t worth much to 99% of the players involved.
- SZJX 8y agoI don't think exact technologies per se are that important for "resume padding". Many companies do use relatively "old" technologies and don't care as long as the job gets done. Slack even uses PHP. Not to mention the big companies which might pay more attention to algorithm problems instead of the particular language you solve those problems in. Do the recruiters really think you are the one if your resume has all the newest hottest things written all over it? Personally I learn new technologies if they are fun to learn and make my life as a dev easier. I'm not sure if anybody jumps on wagons just for the sake of it. For example I tried out Rust, Julia and Elixir, hated the first but liked the later two. I also tried Vue a bit but with Phoenix framework a lot of things can just be done with server-side rendering so I just dropped Vue in the project and felt totally fine with it.
- oldboyFX 8y agoCould you elaborate on this a bit; do you have any interesting real life examples of this mentality?
- erikpukinskis 8y agoSure. I worked on the system which is now the customer admin for Square’s ecommerce platform they bought in Weebly. We were all learning Vuex, while also trying to ship code. Across a half dozen pages we had a half dozen slightly different ways of addressing Vuex data. It wasn’t so much complexity that a professional coder couldn’t keep it straight in their head. But it was a trivial amount of complexity to fix. Two days work, maybe a week. Mostly variable naming. But we chose to ship that feature because we wanted to prove to the company we could deliver. Now those coders will live with that choice for a log* time. New pages will use another slightly ad hoc scheme. Eventually some architect will decide something like “Vuex is too conventional, we’ve got too much glue code, should we try switching to Redux or Vuedux?” And the correct answer for the devs is: yes, it’s good for your resumes. And (speculation) the engineering management will probably never swallow “we have to do three weeks of variable renaming and function moving to fix this now”. If they couldn’t swallow two days of cleanup in exchange for meeting an arbitrary internal target, they won’t swallow three weeks which probably pushes back a user-visible release. * I meant to type long, but log time, pun intended
- jorblumesea 8y agoI'm not really sure what the author is getting at. You need discipline to avoid most of the garbage that can be created by using any language or framework. I've seen some of the worst java apps before, but no one says "never use java, it's bad". It's generally accepted that skilled engineers can make clean, easily maintainable apps, and unskilled engineers make garbage apps. This just sounds like sloppy engineering and a total lack of professional rigor and in that case, why is javascript to blame?
- z3t4 8y agoThis is why I love async code, at every step I get either an error or a value. Which lets me deal with the error gracefully. I don't think error handling is "clutter". Not trusting code is an under-statement. You should be paranoid!
- lxe 8y agoThis article explores a lot of things bundled together. One morsel to take away is the bit about inbound data validation. Type systems needs to work statically as well as at runtime via some kind of reflection API. This way you can unmarshal the data that comes over the wire directly into your language's type system. With JavaScript+TypeScript/Flow, the static type system and the dynamic reflection system is split into two separate paradigms, which in my opinion makes things rather complicated to the point of sacrificing developer productivity.