Hacker News new | ask | show | jobs
by jarrettc 4070 days ago
Strong typing on the web has been an intractable problem for me so far. Sure, I can have strong typing in my server-side code. But so many errors result from the interaction between the server, CSS, HTML, and JS. For example, you define a route at the path `/apples` but send an AJAX request to `/oranges` instead. Or you write `<div class="apples">` but query it with `div.oranges` instead. These are very much like type errors or name errors, except they occur at the boundaries of languages and processes.

Have you worked out a way to catch these sorts of things at compile time? If not, do you think it's possible in the framework of the future?

5 comments

The examples you give don't seem to be typing problems, they seem to be wrong-value problems. They might incidentally also involve typing issues (e.g., "/oranges" might not exist or might be an endpoint with a different signature than "/apples"), but that doesn't seem to be the central problem in any of the examples.

> Have you worked out a way to catch these sorts of things at compile time? If not, do you think it's possible in the framework of the future?

To the extent that they are typing problems, it would seem conceptually possible to catch them through a strongly typed language and framework that abstracts all the underlying technologies and compiles to a combination of backend executable(s), and front-end HTML, JS, and CSS, and includes all the routing for both ends.

Actually building such a beast would seem to be a non-trivial engineering challenge.

> The examples you give don't seem to be typing problems, they seem to be wrong-value problems.

They're like type or name errors because the "apple" and "orange" here are like identifiers, not data. Sure, to the browser, they're data. But in terms of the structure of the web application, they're identifiers like variables, function names, or types.

For example, the HTTP endpoint "/apples?count=5" is like a function "apples(int count)."

> Actually building such a beast would seem to be a non-trivial engineering challenge.

It certainly would. That's why I'm wondering if you consider it possible.

"[I]t would seem conceptually possible to catch them through a strongly typed language and framework that abstracts all the underlying technologies and compiles to a combination of backend executable(s), and front-end HTML, JS, and CSS, and includes all the routing for both ends"

That would certainly do it, but I think all you need is some definition of interface that you can check your code against on both sides. This could be generated by one side and consumed by the other, or produced directly by the programmer(s) and consumed by both. You would need some means of actually checking your code against the specification on the consuming side(s), but they needn't be part of some broader framework (beyond the trivial sense in which they already are).

Sure, you can do that; but the problem is that you then have to worry about type system mismatches between the interface definition language, and the back- and front-end application languages.

There have been lots of things that do something like this: SOAP and the associated WS-* standards are probably the best known.

Very true, and certainly still a big undertaking, depending a little on how well the type systems at either end line up.
Haskell has some typesafe template languages. I'm not a huge fan of them, tbh, as they're kind of rough at the moment.

More promising in my opinion is the fact that Javascript is becoming an increasingly popular backend for Haskell via GHCjs which will give a great space for building type-checked front ends which have all the guarantees you like. For instance, type checked routes already exist which prevent you from writing the wrong endpoints or sending invalid typed data to them... these can be transparently extended to the frontend without much more difficulty.

While a bit rough around the edges, the full type safe server-client stack can be done in Scala with Play[0] + Scala.js[1] + ScalaCSS[2]

I say rough because despite Scala.js' fantastic performance characteristics, you're looking at 100kb file size off the bat; from there generated code size is reasonable, but that's a pretty big hit, particularly for cache challenged mobile clients.

Otherwise, being able to tie Play's type safe reverse routing into the client is a big win. Previously with GruntJS + Coffeescript approach I'd get small file size, but complete lack of safety; just winging it with `routes.user.maybeNotExist(id)`.

[0] https://github.com/playframework/playframework/ [1] https://github.com/scala-js/scala-js [2] https://github.com/japgolly/scalacss

> errors result from the interaction between the server, CSS, HTML, and JS

this may be true, but I'm not sure spending resources trying to solve those problems, are the best use of resources?

I would rather be happy with a strict separation between the front-end and the server than try and deal with such an impedance mismatch and the framework cruft that generates.

I guess it just seems overly ambitious to me.. finding the right abstraction for the server is difficult enough without polluting it with the front-end.

It seems to me people are very productive in other languages that don't tightly bind the front-end code to the server; why spend time solving problems are are more incidental than essential?

> I guess it just seems overly ambitious to me.. finding the right abstraction for the server is difficult enough without polluting it with the front-end.

Certainly. That's why I'm skeptical that this will ever happen.

> why spend time solving problems are are more incidental than essential?

I wouldn't characterize these kinds of errors as incidental, inasmuch as they account for a very high percentage of the web app bugs I've encountered.

Designers of languages like Rust and Haskell noted that null pointer dereferences were the single largest class of errors in other languages. Thus, the designers chose to make null pointer dereferences impossible at the language level. With that choice, they turned a huge number of run-time errors, which developers often miss, into compile-time errors, which developers cannot ignore. This has proven itself beneficial to productivity and software quality.

So too here: If I'm correct that client-side type and name errors constitute a large fraction of all web app errors, then catching them at compile time will be a big win.

But again, I don't know how feasible this is. Nor do I know whether it would involve compiling from a type-safe language to HTML/CSS/JS or just static analysis of raw HTML/CSS/JS.

the logic of your analysis is sound. I suppose I'm just not sure that client-side type and name errors constitute a large fraction of all web app errors.

Anecdotally, the team I am on doesn't have these issues (we certainly have other issues), but I could see them being important to prevent on certain projects.

When using Haskell it feels like I more clearly have to deal with those not-strongly-types-environment issues. I have to write some interface code (which surely takes some "extra" time) to pull un-strong into Haskell; but then the unstronglyness is represented strongly in Haskell types and has to be dealt with accordingly. This reduces funny bugs that may otherwise arise when overseeing corner cases.

Main hometaker: it's like in Haskell I have to do more work up-front, to enjoy much better productivity down the road.