78 comments

[ 4.1 ms ] story [ 164 ms ] thread
Small thing but I dig the new --eval flag.

To do something like this in node, I used to do:

  (echo 2+2; cat -) | node -i
Which would generate some stdin via echo, then use cat to feed stdin to node. --eval 2+2 is a blissfully elegant upgrade to this.

As a comment points out, node has a '-e' (and a -p which prints the evalled thing) but does not open a repl after. This shell receipt, like --eval, leaves the repl open.

(comment deleted)
1) node has a -e option

2) why the cat? It just pipes the stuff through

3) you can use <<< to pipe something to stdin (`node <<< 1+1`)

They wanted to keep feeding stdin that’s why. Their 2+2 is just a placeholder for something bigger, which could even be several lines of input that they wanted to prepend, followed by manual input.

In other words they are talking about initiating a REPL session with some initial input followed by manual input.

Just to be clear, `deno eval "console.log(5)"` has existed for some time which is similar to `node -e "console.log(5)"`. In this case, the deno repl subcommand's --eval flag provides a cross-platform way to evaluate some setup code before launching into the REPL. This is useful if you alias the command in your shell's initialization file allowing you to easily launch a REPL with everything already setup to your liking.

For example:

  $ alias add_repl='deno repl --eval "const add = (a, b) => a + b"'
  ...
  $ add_repl
  Deno 1.13
  exit using ctrl+d or close()
  > add(1, 2)
  3
Node's `-p` option will print the result of the evaluation.

    $ node -p 1+1
    2
The equivalent in Deno is:

  $ deno eval -p "1 + 1"
  2
Is anybody using Deno at work?

I feel like there was this rush of excitement when it first came out, and that's mellowed to a steady stream of updates and some sustained low-medium interest. It seems like everybody's following it from the sidelines, waiting to see what happens. That's my situation at least.

I'd love a world where I can write everything in TypeScript with minimal building hassle, but I'm curious to what degree that's panning out in industry.

We're using Deno for some small projects at Hack Club. We open source everything. Code for them should be somewhere in https://github.com/hackclub/hackclub.

In particular, we've been using it to replace some bash and Ruby scripts we had.

We are, in a limited fashion.

Can't give too much details (mostly because I'm not directly working on it), but the ability to run in a constrained sandbox and the ability to easily do go style imports from github is seriously useful for our use case.

Don't know how it's going to pan out, but from a high level deno seems to be very well designed. I'm looking forward to see the various niches in which people use it.

Not that I know of. TypeScript has had some major adoption over vanilla JavaScript over the past few years, I used it a bit when it was relatively new and now it is pretty common to see it advertised on job specs but I have never seen Deno once, maybe they use it in "ployglot" shops where everybody uses more esoteric languages but I haven't seen it advertised anywhere.
I know at least one person on the Snowpack/Astro team has expressed[1] interest in dual Node/Deno support.

I’m not currently advocating for supporting Deno on my professional projects. But this isn’t for lack of interest, it’s just lower priority than other changes I want to make first. I absolutely want to switch when I can justify it, if for no other reason than fast TypeScript ESM without a build/transformation step.

1: https://twitter.com/n_moore/status/1423672410486755332?s=21

Edit: forgot link

Yes we are building an API of serverless services
We have a customer running about 300 million requests per day through a Deno app to generate image variants.
Why did you choose Demo over Node.js? Is this making performances improvements?
Much faster builds, easier to write code for, etc.

It's probably similar performance to Node, all the heavy lifting is done outside v8 (just like it would be in Node). The JS environment is largely just accepting HTTP requests, doing a bunch of logic, handing off instructions to lower level image manipulation libraries, and then streaming the response back to users.

I'm using Deno for developing my isomorphic libraries (e.g. structurae[1]). It comes with a built-in toolchain (formatter, linter, test runner, compiler) that significantly reduces headache when compared to jangling prettier, eslint, jest, and tsc.

[1] https://github.com/zandaqo/structurae

I find doubt/skepticism like this unimportant & irrelevant. I am far more interested in a Tim Orielly style "Follow the Alpha Geeks" mentality, of mining the scene for what is interesting & empowering & novel, understanding contributions & possibilities.

Deno brings two main things that seem undeniably critical & without peer:

1. Fantastic rust integration. Node has neon & napi-rs, but neither of these present as rust-first & readily available extension point, to replicate what node did: be the incredibly super successful bridge between the native platform & the javascript language. Whatever success Rust has will strengthen & alloy Deno's position.

2. Unparalleled web platform support. Always bet on the web. The web is everywhere, it's platform is excellent & healthy & robust, some last cracks of missing capabilities are being filled in. Deno presents as a possible server-side desktop-side web environment in a way nothing else does. Whatever success the web has will strengthen & alloy Deno's position.

Fretting about where in the adoption curve something is is uninteresting to me, a geek. I would rather focus on the thing, know it, assess it's potential, see others assessments, & make my own projections of usability & adoption, than look about for signs. Hopefully the brilliant things will have their due day.

I'm not fretting or skepticizing, I'm just trying to get a pulse on where things are and where they're going.

Adoption isn't irrelevant, because it determines things like ecosystem maturity, long-term support, etc. I wouldn't feel comfortable recommending that my company build a critical new system on a technology until I feel confident that it has a future. So I'm just trying to get a read on where we are with that when it comes to Deno.

(comment deleted)
Yes. We also don't use it with typescript (and would actively advocate for its removal from deno when asked), best JS runtime I've ever used.
Question from someone who rarely uses TypeScript: why don't you use TypeScript and why do you advocate for its removal?
I can tell you why I don’t like it. Everyone’s subtle OCD seems to come out with Typescript. Instead of cutting down on the number of function arguments, people don’t give a shit. They add more and more args, and maniacally define the types for every little thing.

It’s like some odd version of list-making (many people get a kick out of compulsively making these detailed lists).

It used to be that you think very carefully about how to keep a function simple, but Typescript is bringing out this dark side in people that allows them to just define 15 argument functions, so long as is has a nice exhaustive Type.

Sometimes you have to save people from themselves. I’ll stay hyperbolic and say in several years time, we will all be drowning in type-inflated code bases. JavaScript people always find a way to make things hard.

Somehow people have been programming just fine in statically typed languages for decades without turning their code into a shitshow. It just appears that JS attracts a lot of poor programmers due to the low barrier to entry.
Poor programmers everywhere. Java, C++, PHP, JavaScript. I have seen them all. Like C in JavaScript is so flexible that it is hard to enforce certain (company) standards. That makes it a bit more prone to issues. But many great examples of good plain js programs
One defense of this point is that I see developers writing Typescript code with the constant hinting/intellicode from IntelliJ and somewhat blind to the readability of the naked code.

You get this tight coupling of code and editor/IDE with Typescript that I didn't see coming. The benefits from the coupling are very real and probably outweigh the costs, but there is a price to be paid.

It means that you have to manually go line by line through new code in your editor instead of scanning only with your eyes. It also makes reviewing the code anywhere else harder -- GitHub, etc. This is made more painful when types are necessary to understanding code, but the types are found in a file far away.

But this is coming from someone who sometimes gets nostalgic for the days of printing out code, moving over to a big empty desk with pen in hand. Then going back to the terminal to make those changes.

I also have seen ballooning function signatures that would have otherwise resulting in refactoring to an options object or otherwise refactoring.

but the types are found in a file far away.

Don’t even get me started on that. It seems, now more than ever, we need another ‘JavaScript the Good Parts: 2021 Update’. A lot has been introduced in the last decade, and not all of it should be used to the degree we are using them.

I just don’t know who has the credibility to pull it off at this moment.

Something I've found that really helps is putting a soft-ban on named types with generic type parameters. You almost never truly need them if you aren't writing a library, and they add a whole lot to inscrutability.
I am a strong believer that typed languages in general should have a compiler annotation mode that with configurable level of verbosity formats explicit types and structural info (dead code, exhaustive matching) into structured comments.

This could maybe also be a pre-commit git hook transformation so that you could see how type changes propagate.

The essential feature must be that the compiler overwrites them often and that they must be informative only.

This is a good idea. Languages with advanced type inference have only become mainstream in the last 5 years or so, so I bet we're going to see a lot of innovations like this crop up over time
We don't use it because we found it to be too brittle. The type system is really bolted on. We've hit super weird bugs, in the compiler. And any kind of preprocessing/compilation introduces layers that make in-process debugging much harder.

If browsers actually spoke a strongly typed functional language a la ML/Haskell natively we'd probably switch in a heartbeat. But otherwise it's either vanilla js (no jsx either, simply htm) or Rust compiled to WASM.

And we'd advocate for its removal because it incurs opportunity costs. The deno team is small enough as is, and we don't think that TS is a selling point for it. The great tooling it provides is however so we'd rather see more of that.

TypeScript support is Deno's main selling point for many people. That was the headlining feature, that's the first thing most people know about it, and I think that's the only reason it's gotten significant traction. The other stuff has value, but it's a huge thing to ask an entire ecosystem to pack up shop and move. You have to offer more than just incremental improvements to performance, tooling, and permissions-management. You have to offer a paradigm-shift. I think Deno would be dead in the water if it didn't offer TypeScript as a first-class citizen, despite all the other ways it improves on Node.

As for the type system itself: it is bolted on, yes, and I don't think that's a weakness. Flow tried to be a JS type system that wasn't bolted-on, and it was horrible to use because it was constantly fighting against the base language and all of its idioms. TypeScript is empathetic to JavaScript programmers: it knows things will never be perfect, so it gives you the tools to make them really quite good but gets out of your way when it reaches its limits. I think this is a wonderful thing and a huge driver of its success.

Don't let perfect be the enemy of good.

You act as if the typescript compiler and source-maps suddenly stopped working once they're out of the core.

I think you're hugely underselling the value that deno provides as a tool and attribute too much virtue to TS.

Maybe the advertisement for deno had it backwards. If people knew how much better it is than node, it'd gain significantly more traction. Instead they just think it's "that typescript" thing and ignore it. If deno dies it's probably because of that typescript shadow, not despite of it.

> Instead they just think it's "that typescript" thing and itgnore it. Iff deno dies it's probably because of that typescript shadow, not despite of it.

I don't know what planet you're living on but this is completely backwards from everything I've seen people say online.

> You act as if the typescript compiler and source-maps suddenly stopped working once they're out of the core.

I know that I personally don't bother using TS for Node projects until they reach a certain size. I'd love to use it for every tiny script, but the overhead of setting up a tsconfig, figuring out where the build files will go, adding them to the gitignore if there is one, combining the build step and the run step into a little script for development, etc, just tips the equation. On top of that, I can't really ship TS on NPM because that assumes everyone downstream will be willing to do the same config + build process.

Deno solves all of this by making TS a first-class citizen. That's huge.

(comment deleted)
What made you hate typescript so much that you'd deprive others of it given the opportunity?
No hate, but everything has a cost. Denos strengths over other JS runtimes don't come from its native typescript support, but from the much simpler and streamlined tooling compared to the rest of the JS ecosystem.

JS is the smallest common denominator for that tooling. If people want typescript support they can run the compiler themselves. TS is the odd feature that's there because Ryan likes it, but why favour TS over Reason for example?

I'd rather see good WASI support and a more finegrained sandboxing model, and have the community take care of transpilers.

One little deno feature I didn't realize until digging in to play with it more was that it can 'compile' code and the V8 runtime into a single standalone executable. IMHO this is really nifty and feels very go-like in building tools with zero other dependencies. Obviously the V8 runtime adds a lot of size (seems like a hello world is 90MB or so in quick testing) but I like the potential for building tools that are easy to give to others to install and use.

It seems great for internal use where you have analysts and such using a mish-mash of scripts and one-offs with lots of dependencies and little documentation or time to help people setup and use them. Just hook up your CI to spit out new executables and be done with walking people through how to troubleshoot their broken homebrew node, python, etc. environments.

Personally I weap for the death of shared libraries. It's great developer convenience to statically bundle everything, and ever so manageable...

But it makes me so sad thinking not just of the storage footprint of dozens of copies of the library floating around (be it v8 for deno, or chrome for electron), but the memory cost too of having that near-to duplicate library loaded multiple times too.

And then there's the maintenance cost to the user, of needing to update each package independently to get updates. With something like the web, I as a user would far prefer having bleeding edge shared libraries/v8's/browsers underfoot, and carry the expectation that each app can load & run atop this most recent version.

The trends have been very much in the other way. One of the main Go champions tout is it's static compilation, I think rust too. What are containers but entire static system images, as opposed to just having some static libraries? Part of me is willing to acknowledge that the larger footprints aren't that impactful, aren't really a problem, but it contravenes the deliberate & elegant simplicity that something like an OS distribution used to represent: I look at Debian as thousands of different pieces, all wonderfully integrated & interlinked, a cohesive system, that one ought to be able to bring additional projects onto to compile & extend. But our appetite for such has waned. The convenience for you & your users of just bundling everything you personally need to ship your stuff is quite high, at least when you're not updating dozens of oversized apps or running janky old versions because of this easy-to-get-started convenience.

Honestly the memory cost isn't that much.

Code size has not grown as fast as memory and storage capacity has. One is constrained by manufacturing progress, the other is constrained by programmer output.

Deno is sub 50MB

For any non trivial app, your assets and working set data will likely dwarf the code size itself.

EDIT: Was looking at an old bug, deno is closer to 100MB. Starting to feel a bit large but still.

The problem seems to stem from library developers making should-be-stable APIs and later fixing/updating them in a way that shouldn’t break compatibility but does, all without a major version change. While this doesn’t happen to the majority of libraries out there, every developer has encountered this issue at least once, and the easy way to fix this is to statically link all dependencies, thus enabling Uber-compatibility as future dependency updates won’t be automatically introduced in your program. When these dependencies are updated, the app developer can perform a full suite of validation that guarantees the update doesn’t break anything.

The only way to fix this is with either an extremely stable API (eg. libc) or a mostly-stable api that developers know they have to validate often, and that’s going to be the web with Chrome/Firefox PWAs.

If it's an internal tool used by 10's of people I really don't care one bit about memory use, the elegance of shared dependencies, etc. The kind of optimizations debian is going for are great at scale but it's the kind of last 10% of work that will take 90% of your time. This is optimizing squarely for fastest agility with lowest developer and maintenance time.
>contravenes the deliberate & elegant simplicity that something like an OS distribution used to represent

That simplicity is also the root cause of an incredible amount of issues. Want to install a program that needs a different library version than is already installed? Well, too bad, guess you'll have to manually patch it (assuming the source code is even available). And there's countless such stories, 'dependency hell' is still an issue to this very day for dynamically linked libraries. Docker and the like didn't come into existence for no reason but because the experience of simple package managers is just not adequate.

Now, dynamic libraries on their own aren't at fault of course. If more complex package managers like Nix were common this wouldn't be an issue, but simplicity 'won', so static linking it is.

> Want to install a program that needs a different library version than is already installed?

You can install libthing.1.1.so and libthing.1.2.so and link mybinary to v1.1 if you need it?

Sure, then you're essentially back to manual dependency management though and I don't want to waste my or my users time with that.
Not necessarily, you can have packages and automatic dependencies using yum or apt?
How would you install two different versions of the same package using apt?
apt install python3.10 python3.8 ? If packaged, those can be installed alongside. Or libsdl1.2 and libsdl2 by example.
I don’t agree with this comment, because I think these tradeoffs have mostly been shown to be worth it, in most cases.

But I also think those of you downvoting it are Doing HN Wrong.

Static linking does not bundle the entire library into a binary. It only bundles what's necessary. When you load a shared library, you load an entire copy of the library into memory; when you load a statically linked binary, you load only the relevant bits.

Static linking also enables much better link-time optimization as well as several additional hardening measures; Clang's CFI sanitizer in particular works much better with access to the whole program.

Furthermore, the shared global state between all programs on a traditional *nix box is a limitation, not an ideal. Compartmentalizing through sandboxing is the way forward. Namespace isolation, separate users, and cgroups are already widespread. Static linking makes this much easier as it avoids having duplicate copies of a full library loaded into memory.

Dependency updates should be managed by a package manager. When a library updates, the package manager will handle dependency resolution.

This only leaves two concerns: disk space and network bandwidth of software updates. Depending on your situation, these may or may not be worthwhile tradeoffs. In the face of the benefits to security, portability, and simplicity that come with static linking, I'd argue that it should be our default preference.

Thank you for the thought out reply. I'm going to write a reply here again, but know that in many ways I agree with you. I'm not convinced either way, but I do definitely see that we are losing much more than your reply has been able to confess to ("disk space and network bandwidth of software updates").

> Static linking does not bundle the entire library into a binary. It only bundles what's necessary.

Fair enough. Static linking is only a partial extra copy.

> When you load a shared library, you load an entire copy of the library into memory; when you load a statically linked binary, you load only the relevant bits.

I doubt that the entire library is loaded into memory. I'm not sure which regions of a library are fully loaded- some are loaded most likely- but my belief is many regions (including the code) are not automatically paged in, they are simply mapped in, should they be needed.

I'm not sure that functionally there's any win here for statically built binaries.

> Static linking also enables much better link-time optimization as well as several additional hardening measures; Clang's CFI sanitizer in particular works much better with access to the whole program.

This sounds like a good real win. I wonder what it would take to try to claim back some of these wins for dynamically linked libraries, if that is at all possible.

> Furthermore, the shared global state between all programs on a traditional nix box is a limitation, not an ideal. Compartmentalizing through sandboxing is the way forward. Namespace isolation, separate users, and cgroups are already widespread. Static linking makes this much easier as it avoids having duplicate copies of a full library loaded into memory.*

This is opinion stated as fact. Yes containers & cgroups & isolation are eating all the systems & it feels like the wheel of destiny is turning & perhaps it is. Stating that fact like it implies this path is 100% equal to or better in every way than what we do is, to me, harmful to understanding the real nuance & complexities of this change we are undergoing.

To me it feels more like it has been convenient & expedient. I feel my operating system has done a better job & been a viable platform, having some inherent complexity but at the benefit of consistency & legibility that running dozens of different unalike containers can not replicate. While we ignore problems like user namespaces in containers initially, those problems have a tendency to creep back in in new forms.

I would have liked to have seen an alternative world where more companies did things like build Debian packages to deploy their releases. (I've seen a couple but has felt exceptional to me. Reaching a critical mass adoption of any OS would have been interesting.) Containers allow everyone to do what they want & not care or think about how to enmesh or integrate- isolation is freeing and liberating that way, and there are huge advantages to that, but I also recognize & appreciate that systems, historically, used to operate with more sympathetic coherent & consistent singular nature.

Modern Linux is gaining many of the composability advantages that made/make containers so attractive, with plans for the /usr merge nearly coming to fruition (an upcoming Debian Bullseye objective) and ongoing progress adopting "Revisiting How We Put Together Linux Systems"'s composed filesystem (http://0pointer.net/blog/revisiting-how-we-put-together-linu...), which permits for modular system images, runtimes, & frameworks to be composed or pluggable-ized together when making Linux systems. This brings many of the management wins of containers, with their aggregate of overlayed layers, to Linux.

> Dependency updates should be managed by a package manager. When a lib...

I wasn't necessarily referring to containers; I was referring to sandboxing tools like Bubblewrap and Minijail, which isolate many parts of the host system, the namespace, and filter syscalls. This approach combined with restrictive SELinux policies should be much more widespread. By default, most *nix systems don't really have any security model besides user accounts.

When programs run with some more isolation/sandboxing, they won't be able to access the same copies of shared libs loaded by other programs so they'll re-load them. In most benchmarks I've seen, a much greater amount of memory is used by a program loading a single shared library than a statically-linked program, even excluding the overhead of the ld interpreter.

I was also referring to the OS package manager, not bleeding-edge programming-lang-specific package managers.

bytecodealliance's efforts in the space look super promising for serverless ("nanoprocess") computing especially. Have you looked into those?

    const libSuffix = { darwin: "so", linux: "so", windows: "dll", }[Deno.build.os]
oh no, what a bad example. Hope no one ever actually copies that…

(it's bad because it unnecessarily limits the list of platforms to just three. great way to make BSD users hate you. just do: if (windows) "dll" else "so")

(comment deleted)
Seriously, HN, is Deno going to hit the front page with every single point release??
Bartek from deno.land here, happy to answer your questions should you have any.

Deno 1.13 is one of the biggest releases to date - we're especially happy with stabilization of native HTTP server bindings. We plan to focus on squeezing the last bits of performance out of it in the coming weeks.

Personally I'm most excited about type checking example code in Markdown files. It's critical to keep your documentation up to date and doing so for code blocks embedded in JSDoc comments or Markdown files was very challenging. With this release we got you covered - just run "deno test --doc" on your codebase.

Hi Bartek, thanks for jumping in.

I am wondering whether one could forego Typescript and use one of the Ocaml/F# to JS converters for the frontend and backend code. Would it even make sense, and would it be convenient to use? How would one set up such a project?

+1 for Ocaml/F# and also the new ReScript. Would you be open to support these on Deno?
If the tooling you're describing outputs JS sources there's nothing preventing you from using it. Currently there's no way to hook them up directly into Deno, so you'd have to resort to separate build step (via some kind of script, I presume this is just a matter of calling appropriate binary).

If those tools help you write better code and you prefer to code in Ocaml/F# then it definitely makes sense, but of course this is personal preference.

Drop by at our Discord server [0] to get some tips on how to set up such project.

[0] https://discord.gg/deno

I haven't been following the Deno community super closely; do you know if people have converged yet on a go-to web framework, similar to Express? Does this stabilization play into that progression at all?

Last I checked there were several promising-but-fragmented options, so I'm hoping the community has or will pick a "standard" answer for "I want to make a web service", since that seems like one of the headlining (the headlining?) usecases for headless JS

I'm also, and this is more off-topic, but I'm wondering if there's an answer for NextJS out there on its way. The latter is eating (a portion of) the world a little bit right now, and could singlehandedly keep some parts of the industry on Node.

Oak [0] is the go-to web framework in Deno ecosystem, it has Koa-like API. There's also Opine [1], Drash [2] and some other that are also highly spoken of by folks at the Discord. AFAIK Oak used native HTTP bindings since their inception (if you were running with --unstable flag); and now that API is stabilized it will use it by default.

As for the NextJS; there are also a projects that try to mimic its API like AlephJS [3] and Fresh [4].

[0] https://github.com/oakserver/oak [1] https://github.com/asos-craigmorten/opine [2] https://github.com/drashland/deno-drash [3] https://github.com/alephjs/aleph.js [4] https://github.com/lucacasonato/fresh

Thanks for the info! Glad to hear that the ecosystem is coming along. I look forward to the day I get an opportunity to use Deno at work
I'm still not a fan of the async iterator API example for server handlers:

    for await (const conn of Deno.listen({ port: 4500 })) {
      (async () => {
        for await (const { respondWith } of Deno.serveHttp(conn)) {
          respondWith(new Response("Hello World"));
        }
      })();
    }
It's hard to reason, and ugly with that async iffe in the middle adding layers of nesting and bf-like `})();` junk.

I think the event subscription API is much easier on the eyes and the mind.

I'm curious why the IIFE is even necessary in this case. There is already a top level await in the `for await` right?

Looks fine IMHO once the IFFE is removed:

  for await (const conn of Deno.listen({ port: 4500 })) {
        for await (const { respondWith } of Deno.serveHttp(conn)) {
          respondWith(new Response("Hello World"));
        }
    }

Edit: Oh is it so it runs in concurrently and avoids the synchronous handling?
Yes I think the IIFE is there to avoid the inner loop blocking the outer loop.

I think it looks better with the IIFE factored out into its own function:

    async function serve(conn) {
      for await (const { respondWith } of Deno.serveHttp(conn)) {
        respondWith(new Response("Hello World"));
      }
    }

    for await (const conn of Deno.listen({ port: 4500 })) {
      serve(conn);
    }
Any reason why I would use this over expressjs?