Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Far more important is instant feedback and that’s getting worse all the time: with lisp, smalltalk, Delphi, forth things were instant. With typescript or rust etc, if the project is larger than hello world, the feedback is well, far from instant. Bret victor talked about feedback, not necessarily visual programming and for some reason we are making things worse instead of better. When I show a Common Lisp dev session to young people, they think I am somehow cheating. I am, because I am not using the garbage people produce now and we are always faster (sbcl is incredible; sure you can do less latency and more performance with rust or c but then you don’t have the debugger and feedback while it takes a lot more code aka bugs and work), less bugs and not depressed while at work. We also don’t have to hire ‘devops’ to waste our profits; I like profits and I like not needing VCs.


With strong, expressive type systems such as those offered by Haskell, Rust, TypeScript, etc... I find that you front-load all of your debugging to compile/typecheck time. Instead of needing to experiment with your code at runtime through either manual or automated (TDD) tests, you are instead having a conversation with the compiler/typechecker to statically guarantee its correctness. There's just as tight a feedback loop with a typechecker as there is with a test runner; you in fact get your feedback even sooner during compile time, instead of during the subsequent runtime.

Where static verification against a typechecker shines over runtime verification against a test suite is that a test suite can only demonstrate the presence of bugs; type systems demonstrate their absence (presuming of course that you understand how to encode certain invariants into your types and don't do some foolishness such as simply asserting all values as `any`).


While I prefer expressive type systems by a long shot, I would be much more careful about it "guaranteeing correctness".

Types can act as good documentation and as a safeguard for stupid mistakes. But the worst bugs are due to logic mistakes, wrong assumptions or non-foreseen corner cases. Here, either types do not help, or designing the type system is so difficult it is not worth the effort, and makes many future changes more difficult.

In my previous company we used Scala (with and without Spark) for everything, and this setup pretty much allows you both extremes. There was always a middle ground to be found, where types were expressive enough that they were useful, but not too much that they came in the way.


> While I prefer expressive type systems by a long shot, I would be much more careful about it "guaranteeing correctness".

Yeah, you're not guaranteeing correctness. There's a quote from automated testing discussions that applies here...

> You're not proving the system works. You're proving that system isn't broken in specific ways

Likewise, for a type system, it's guaranteeing the system is correct for the specific subset of "ways it can be incorrect" that the type system covers.


Just encode your business logic in types first! Coq, Idris, or F* will certainly get the job done for you!

/s


Yes, you do if you want to make money on decades timescales instead of some grifter vc 2 year thingy.


Unfortunately you end up selling your stuff to people building missiles and bombs that way—witness CompCert and Frama-C.


Not us, but yes, you have a point.


It guarantees certain correctness it is having conversations with you about - this way more correct


Type systems in languages like Haskell or Rust are very very very far from being able to "guarantee correctness". They can only realistically be used to specify extremely basic properties of your program ("doesn't have side effects", "doesn't write memory concurrently", this sort of thing).

For any more interesting properties (say "this function returns a sorted version of the input list", or "this function finds the smallest element in the set", or "this transaction is atomic"), you need something like dependent types, and that comes with a hell of a lot more work.


I would like to see improvements in the speed of feedback - particularly from language servers - but the value of those 'basic' guarantees is more than worth the current cost. Unexpected side effects are responsible for almost every trip I've taken with a debugger in any large Java or C++ project I've ever worked on.


I can remember about 20 years ago a colleague getting quite frustrated that a bug he had been looking at for quite a long time came down to someone doing something bizarre in an overloaded assignment operator in C++.


I've seen methods with names like "get_value()" have extensive side effects.

No type system can fix bad programming.


Of course I think we have all seen horrors like that - what I remember was his completely exasperated response not the technical details of the bug.


Complexity is mostly exponentiellt worse in the unknowns and you can not graph what you already know.

The point in the article is that when we read code we need another visualization to change or mental model. I can scan code and find most bugs fast, but when you are stuck a complexity by row/column sure would be handy to find overloaded assignments.


You're missing the most basic utility they provide... that of making sure other code is calling the function with the right types of arguments. That's a lot of coverage over a language without a compile type checked type system.


That's not a utility in itself, it depends on what the types represent to know if this is a useful property or not. For example, a Cfunction which is declared as "void foo(int a)" does ensure that it's called with an int, but if it's body then does "100/a", calling it as foo(0) is allowed by the compiler but will fail at runtime. It's true that that the equivalent Python function (def foo(a)) can fail at runtime when called as foo(0), but also foo("ABC"), but it's a matter of degrees, not kind.


Fair.

However, most people are using stuff like JS and Python. For them even the non-dependent type systems are an improvement.


I agree that one should refrain from ever using "guarantee correctness" in context of type systems outside of Coq & co. But "extremely basic properties" is IMO similarly exaggerating in the other direction.

Take the "basic" property "cannot be null" for example - Considering the issues and costs the lack of that one incurred over the decades, I'd call that one damn interesting.

And Rust? C'mon, its affine type system is its biggest raison d'etre.


I do not consider neither TDD or tests being about finding or solving bugs. They are about regression and refactoring safety. They are my guardrails for when I must change or add things, or need to discover how something works.

The rest of your comment I found to be a really good point in terms if feedback justification. The IDE checking your code before compile or runtime is faster than both. Good point.


Tests for me also help me write better code. When writing tests, I'm forced to switch from "how do I implement the behavior I want" to "how can this fail to do the right thing". Looking at the code from _both_ of those mindsets helps me end up with better code.


Where I see this fall down, is when you aren't able to learn from the partial code along the way. The sooner you get an end to end setup running where input to the system causes a change to the output from it, the better you are for this sort of feedback. Note, not the soonest you get code to output. The soonest you get users giving input to users getting output.

If you are able to internalize everything, you are constantly simulating expectations in your head on what you are coding. Seeing where your expectations fall down on outputs is a valuable thing.

So, yes. If you fully understand everything already, the "paying it upfront" cost of exhaustive types is good. Amazing, even. Until you get there, you are almost certainly pushing off the feedback of learning where you do not fully understand the system.


I feel like you can partly get around this by slowly increasing type specifically over time. With strong type checking the risk of refactoring is low.


Certainly, but that goes a bit against the idea of incredibly strong types that people often visualize in their mind.

Irony being what it is, most strongly typed programs that I have been introduced to were incredibly tight knots that were not easy to refactor. Many of the restrictions in the types would be far too strong from what was needed by the program, and refactors grow in difficult to explain ways.

This is all to say, the discourse here is fraught with nobody acknowledging that "well done" programs of near any paradigm/style are well done and work. Evidence is often used that languages that allow looser ideas are more numerous than those that don't. This ignores that lack of existing programs in the strongly typed world could also be lack of ability for many people to deliver using those practices at all. Which, in turn, ignores that that may be a trade off that is worthwhile in some industries. (I suspect it goes on.)


CL is a strong and often statically typed language; there are more expressive, even Haskell like implementations (https://coalton-lang.github.io/20211010-introducing-coalton/) with instant feedback, robust (old; underestimated how robust things get when some kid didn’t roll your npm yesterday). And yep, an expressive type system, like ts, is often turing complete so it can hang, but that’s not what I am talking about; trivial ts is incredibly slow even with type checking off; now non trivial ts is a joke. Why so many fans while no one can show even 1 example that is not slow?


Eventual or gradual typing could leave everyone happy.

On the premise of the article, maybe the key to representing a program visually is a very expressive (and strong) type system. There could be a way to derive some Visual Types from good old regular types, and diagram the visual types in any level of granularity one desires.


Instead, gradual typing seems to always make everybody as unhappy as they can get.

Just like visual programming, it looks like we are doing gradual typing very wrongly.


If Typescript counts as gradual typing, which i think it does, then many seem to be very happy using it. I have skimmed over more than 1000 blogs of HN, didn't see any posts about disliking Typescript, and many people use it.

Doesn't Typescript offer seamless interoperability with vanilla Javascript?


I am very much of eventual static typing and even proofs for some parts; cl is pretty good and we have a gradual type system in our company for cl. But it’s just faster and easier to build it first and add types later we found (our company is almost 40 years old now).


If you use ocaml you get near instant compile times and types, which is excellent for quick feedback.


As projects get bigger, things might get sadder. I worked at a certain large SF company that uses a lot of ruby, so most development was repl-based too. But this wasn't a boon but a curse, as the total lack of data format guarantees on the very large, critical monorepo meant a lot of uncertainty. What does this method really do? I guess we have to run it! It worked for this specific input... but will it work for any and all inputs that get to this data path? Let's hope so, because we aren't sure! The company spent massive amounts of money on servers for parallel testing, just so that the suite could run in less than a few weeks. And when you need a large test suite to have a chance, most of the advantages of the REPL vs a compiler have been lost.

Eventually they did the same thing you can do in common lisp: Add so much metaprogramming that doublechecks invariants, it might as well be a compiled language.


Common Lisp has always allowed you to specify types anywhere you want. It's not a statically typed language but it supports types (and SBCL and other impls do static checking where possible).


So, paraphrasing:

Any sufficiently complicated Ruby program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Java


I think that OOP done right can address this problem through encapsulation of the logic.

One can make the case that a function can do the same. I agree, but a class is just a container for a set of related functions and state.

The problem with OOP seems to be that as a whole, devs are not that great at encapsulation and isolating domain logic.


The number of codebases I've worked on where developers automatically added getters and setters for all class members is too damn high.


I guess the upside is that you end up with a "compiler" that checks the invariants of your project and that domain rather than strict type safety. I love static typing, but I did try Elixir w/ Phoenix recently and was impressed that they were able to implement compile time checks like verifying that all redirects are valid routes, etc. Depending on what you're building, having a few small domain specific checks like that could be more valuable than strict type safety.


I don't know what TypeScript projects you have worked on, but every one I have worked on is instant reload all the way down. Rust, on the other hand, is pretty miserable.


I don’t know; I am a programmer but more of a trouble shooter (it makes far more) and projects passing 100k LoC in ts which I see 100s a year, are not instant, in any way. I would love to see one, but I contribute to open source projects, and it’s all slow, very very slow.


I have a 1,5M LOC game codebase, where both the server and client builds and starts nearly instantly. Probably < 3sec total iteration time just because of typing stuff in the terminal. Build system is just a casual ccache + mold. But you have to do a lot of stuff in the background during startup.


Let’s see it. To not be a total gobshite, go download these things and see what ‘instant’ really doesn’t mean. Instant means not wait 1 second; it means when I type code, it has results in milliseconds. None of these have that.

Or show me a non trivial open source ts project that’s instant; doesn’t exist and I have literally no clue why people keep defending this stuff; you didn’t make it right? I would be embarrassed but definitely not defending it.

https://www.kirandev.com/open-source-nextjs-projects-open-fo...


While I totally agree with you, I'm looking for a counter example. The only one which comes to my mind, is non trivial and quite fast for js/ts is this : https://github.com/tsoding/raycasting An implementation of raycast in a 2D canvas The dev tried to implement hot reloading, and you can see all the process there https://www.youtube.com/playlist?list=PLpM-Dvs8t0VZ08cYW6yqN...

Even though I didn't measured the loading time, an effort has been made to accelerate everything that could be accelerated.


Even the simplest type inference can cause typescript to stall for minutes. The problem is in the inference. That problem goes away when you're actually specifying types. Tooling should add typing automatically (not implicitly)


Minutes? There’s no way you’ve seen a minutes-long stall caused by TypeScript inference..I need to see some evidence on this one


I'll look into it.. But yeah, had that multiple times (older versions though). Specifying the type solves is, and I was not the only one after some googling


I'm working on 250k LoC TS project. It's instant during dev. We don't bundle during dev so the server just has to compile which ever files changed.


Well, I would love to learn how it’s possible: you have a blog or YouTube or something?


Our front end is ~200k LOC of TypeScript and all changes are instant (<1s).

TypeScript compiler is too slow for hot module replacement so it’s used only for IDEs. During development, all transformation happens via esbuild/swc and is abstracted away with Vite https://vitejs.dev/


esbuild does not do type checking. You must invoke tsc explicitly to do that.


Type-checking is helpful in your IDE (for developer hints) and in your CI (for verification), but you don't want type-checking in your hot-reloading dev loop.


I pointed that out because your previous comment could be misinterpreted to mean you do full type checking on your dev cycle, which you probably don't.


On what hardware? I have a m3 and yeah, it’s terrible with ts. Instant (milliseconds) with cl (of el even). Go is not terrible.


Same, M3. The DX within a modern frontend stack is indistinguishable from Bret Victor’s ideas (even if a decade late).


Ok, when can we meet? I have never seen it work, and, as said, I review 100s of project a year; everything ts is super slow so far. Maybe you have something.


Their hot reload cycle is fast because esbuild doesn't type check the code, it just removes types from Typescript so it turns into JS (it may do minification and tree shaking but in dev, they probably remove that). I've written some esbuild plugins and can confirm that on incremental builds, esbuild will probably never take more than a few ms even on larger projects (because it doesn't matter how big your project is, esbuild will only rebuild what changed, which usually is a few files only).


No one wants to show me though. Is that not weird? Fanbois say it is, but not one person even sends an open source project that demonstrates it. I don’t understand that? Please show me a non trivial project on GitHub that does this in ms like you say. All I try are slow af. Emacs is notoriously slow as a lisp; it is always faster, for me, than anything ts and definitely not trivial. Sbcl/cl blows it all away. Please an example in ts or it didn’t happen.

Download some horror show like Unkey and show a video of its millisecond hot reload…


You are asking for someone else to do a demo of something trivial that takes 15 minutes to setup yourself (with Vite). Nobody is biting because it’s a strange (and, frankly, lazy) request.

If you tried and still think such a setup is not possible, send me an email (in profile) and I can do a 10-15 minute show&tell.


Typescript is pretty fast no? I've worked on the VSCode codebase a fair bit and the intellisense seems pretty fast. Like under a second, which is fine.

You don't need to resort to Lisp to get instant feedback. Try Dart - it's basically instant.

Rust I will give you...


Agreed, I just prefer lisp. We use flutter a lot, but we do it in cl -> dart; it makes everyone here (again taste, not gospel) happier.


What do you use to compile CL to Dart? Or you compile to JS?


As a fellow ancient person my only consolation is that the browser-based development experience most people now learn on is (with some path-dependent ugliness) basically the experience Smalltalk promised us in the 1980s and never quite delivered.


I'm confused? Smalltalk promised image based development where you could change things on the fly. That is far from what I typically see in browser based development.


I think that's also true, but not the thing they're writing about.

My experience as an iOS developer has been mixed between places that use Interface Builder and those who create the UI in code. Something like Interface Builder is obviously a great idea for UI creation, which is why it survived so long and why Figma exists, but the actual tool itself (IB) isn't really useful for desigers who want one thing that works on all platforms. (Complaints like "xib and storyboard are hard to use with version control" miss the point; a UI designer shouldn't be seeing git-style text diffs anyway, they need a graphical diff).

Interface Builder is at least 5x faster than making the UI in code; one place in particular, I was making a native app by myself while a whole team was making a web app, and I wasn't simply keeping up with them, I also cought up the headstart they had built while I'd been assigned to a different project. The next place I joined a team and their app was a coded UI, and development was correspondingly slow. (Though how much of this is "teams are slower than solo developers" vs. my hypothesis is unclear to me).

My first coding job was before iOS existed, I was industrial placement student* in an academic research lab, and for that, my guess would be the best option mayhaps have been a mathematical formula WYSIWYG editor that output both latex and IDL.

* does this term get used outside the UK? What's the overlap between this and intern?


Haven't worked with the iOS interface builder but i worked with Delphi ages ago.

Question: how well does the interface builder thingy mix with interface in code?

Can you easily lay out the basics of an UI graphically and then add code where the builder is too limited for what you need?

Or it's the kind where if you start with the graphical builder you're stuck with just what the graphical builder can do?


It mixes well.

Apple has two UI frameworks, UIKit and SwiftUI, and Xcode's Interface Builder handles each differently. The Interface Builder for each is built into Xcode, but the UI they present when editing is quite different — I'm unclear if they're both officially called "Interface Builder" or if people like me stuck with the same name for the new one because it's a thing for building interfaces.

The older system is UIKit, where Interface Builder produces some XML files — .xib or .storyboard — and once those are loaded, they result in objects which are fully manipulable in code.

The newer system, SwiftUI, the source code is the source-of-truth for a WYSIWYG editor — any change made in the editor immediately updates the code, any change in code immediately updates the editor. That said, in my experience at least, this editor falls over quite often if you do that.


Mixes Well is an understatement. I couldn't imagine writing any more than a simple toy app without it. I've experimented with building an iOS UI purely in code, and while it is possible, it is a painful, masochistic, and slow way to develop.


The idea is to do everything trivial via the interface builder thing of the platform but not get limited by it when you want to customize the last 10%

> any change made in the editor immediately updates the code, any change in code immediately updates the editor.

Delphi memories :)

> That said, in my experience at least, this editor falls over quite often if you do that.

Not-Delphi memories.


A quick overview of Interface Builder is Steve Job's demo for NeXT --- perhaps:

https://www.youtube.com/watch?v=dl0CbKYUFTY

where they discuss how dragging/drawing allows one to make 80% of the app, and the balance of 20% is one's own code.


> What we found a long time ago was, the line of code that a developer can write the fastest, can maintain the cheapest, that never breaks for the user, is a line of code the developer never had to write.

> The goal here is to literally eliminate 80% of the code that every developer has to write for their app - because it's in common with every other app. And let them focus on just the 20% of their code that's unique and value-add to their app. That's what this is all about.

---

A video of historical interest - OpenStep's Interface Builder in 1997, the year Steve Jobs returned to Apple.

It shows how forward-thinking NeXT was. Many of its innovative user interface concepts are relevant and in use today in different guises and interpretations.

> Every single app on NeXTSTEP was built using Interface Builder. It is the frosting on top of this object-oriented cake that we have called NeXTSTEP.

In the demo, Steve seems to use the word "object" with a depth of meaning closer to what Alan Kay explained, like independent (or rather interdependent) "cells" of software that communicate to each other via messages.

> OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things. It can be done in Smalltalk and in LISP. There are possibly other systems in which this is possible, but I'm not aware of them.

On the Meaning of “Object-Oriented Programming” - http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/doc_kay...

It's interesting that the Interface Builder was considered a "frosting" or visible representation of the underlying objects, which the users more or less directly created, edited, and connected together - without writing a line of code.

That ideal of user experience still hasn't been fully achieved to satisfaction, it feels. The ease and naturalness of creating software visually, as well as with other modalities, senses, medium of expression beyond text.


I'd give a lot to have a graphical development environment which:

- allowed drawing a user interface as naturally as I used to use Altsys Virtuoso (or Macromedia Freehand which I moved to when my Cube stopped working)

- allowed programming the UI as naturally as HyperCard (and to a lesser extent Lisp) "clicked" for me

- was as visual as Google's Blockly (which as BlockSCAD: https://www.blockscad3d.com/editor/ I've used a fair bit)

- exposed variables in a mechanism like to OpenSCAD's Customizer: https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Customize...

Currently plugging away with OpenSCAD Graph Editor: https://github.com/derkork/openscad-graph-editor but hoping that: http://nodezator.com/ will become a viable option (still a bit bummed that I rolled and crashed w/ https://ryven.org/ though in retrospect, maybe I should try to tie that latter in to: https://pythonscad.org/ )


If you haven't seen it, the Interface Builder got its inspiration from this Interface Builder written in Lisp: https://vimeo.com/62618532 The version in the video actually ran on a TI MicroExplorer Lisp Machine NuBus board in a Mac. There were other ports which ran directly on the Mac in Lisp.

The thing was shown to Steve Jobs and he hired its main developer, Jean-Marie Hulot.


Ah yes, I remember the funny guy facing various dramatic software difficulties in the first part of the video.

This builder does look like a precursor to NeXT's and HyperCard too. I like that the "toolbox" appears next to the mouse, instead of having to go to the top menu or sidebar to select a new tool and back to position to use it. I missed that it ran on a Lisp processor, very cool!

And that the main developer later worked at NeXT and Apple.

> Jean-Marie Hullot authored important programs for the original Macintosh, NeXTSTEP and Mac OS X platforms. These include SOS Interface for the Mac, which later became Interface Builder for NeXTSTEP (1985), and later still evolved into an important part of Mac OS X.

> He also came up with the idea of the iPhone and led the iCal and iSync development teams for Mac OS X (2002).

https://en.wikipedia.org/wiki/Jean-Marie_Hullot

..Searching for "SOS Interface for the Mac", I guess it's Sophisticated Operating System, "the primary operating system of the Apple III computer" released in 1980.

https://en.wikipedia.org/wiki/Apple_SOS


Let's not do the usual thing of allowing Jobs to get away with claiming other people's advancements. VB and Delphi by '97 already had that sort of interface builder - arguably much better ones. NEXT was trying to catch up with what Microsoft had already done on Windows by then.


That's great that VB and Delphi had one in 97, but the Interface Builder for NeXT was released in 1988.


Oh definitely NeXT was standing on the shoulders of giants, prior art, and historical precursors. He did present it well though, with a quotable and catchy demo.


Honest question as a java lover-- do rust and TS have slow compile times or something?

Because java has a robust type system and yet I've never had any issue with it's compile times. And the parsing in the IDEs is so fast and good I can pretty much get all the feedback in real time.

Ah, that's the luxury of using a battle tested language that other people have invested billions of dollars of effort into optimizing.

Do you guys not have that in Rust and TS? Bummer man. Hope you get there someday. This is exactly why I abandoned scala btw.


Yes - Rust has pretty slow compile times. It is perhaps my biggest gripe with it.

You might define java as having a robust type system, but I would rate Rust's as significantly better. Several things in Rust I miss when working in Java:

* Monomorphized types

* Sum type and product types. Think sealed classes in java, but with better ergonomics

* A really clever type system that prevents things like ConcurrentModificationException and data races (not dead-locks or race conditions generally though).

Though, IIUC, Rust made early decisions about module structure that have really hindered compile speed, not necessarily tied to the type system.

Another big factor that makes rust slow would be optimizations & slow system linkers; it doesn't have a JVM that can warm up to optimize stuff.

Source: work with both Rust and Java on a daily basis.

Edit: The way the type system works out in general makes me far less worried about making sweeping changes in a Rust codebase than in a Java codebase, but there are still logic bugs that I miss occasionally. Still, it moves quite a big bug finding from "run the program and see" to "make it pass typechecking", which is quite a bit faster than compiling; you can typecheck Rust without compiling it.


I find using cargo watch really helps with my perceived compile times - it runs a check then build on every file save. By the time I open a new terminal to test things out it is ready to go!


In my experience Rust still compiles as fast as I need it to (other than for the very first fresh compilation) and using language servers / rust-analyze I get literal instant feedback in vscode as I hit save. Not to mention autocomplete...


> Ah, that's the luxury of using a battle tested language that other people have invested billions of dollars of effort into optimizing.

Making your compiler fast is easy when it doesn't do any work. The Go one seems to be even faster than Javac.


Point well taken, golang feels painful to use compared to java due to its weak type system (to be fair, I have NOT used it since generics were added, so I don't know if that helped a lot -- that was always my main pain point, it made collections kinda useless -- it's not a coincidence that java added generics (1.5) soon after it added collections (1.2))

Anyway, I'm not sure if this is a situation where the extra power of Rust's type system could not possibly be made more performant and it's just a natural tradeoff, or whether it's a thing where it's a matter of time before Rust (or some competitor) catches up and we can have it all


I’ve got a 500 file, 100k sloc typescript project with instant feedback which makes me wonder: what is a large project?

Are most people somehow working on giant repos?


So far, no examples of an instant typescript while a lot of fans for some reason. Come on people; 1 non trivial (few 100k LoC) ts project on GitHub that hot reloads instantly. If no one can produce even one, something is off no? Making claims is easy; not cl but emacs is non trivial and notoriously slow, still ‘instant’ compared to whatever ts (not hello world, but including hello world; it’s super slow) that I found so far on the same hardware. Sbcl is cl and it’s very fast, almost always instant, obviously depending on what you are doing. CLOG is a frontend joy to work in unlike ts. But many people here say ts is instant (we use the same tooling as peeps here for ts); one non trivial example please. No one produced one and all I try is dog slow. Not even comparatively, just spinning balls on a modern laptop. Some of the most popular devs make projects that ‘hot reload’ on my and my colleagues laptops in 20 or so seconds.




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

Search: