Hacker News new | past | comments | ask | show | jobs | submit | dangarbri3's comments login

The old internet is actually still around. There's just so much noise and Google isn't going to direct you to it.

Neocities is a platform that promotes those quirky personal homepages.

There are chat room/irc platforms like matrix and libera chat that still go on.


AI recommendations are a blessing and a curse on YouTube.

More satisfaction from word of mouth (hand curated search index, effectively) hits than AI lately for me.


For me they're annoyingly self referencing. I rarely ever get recommendations outside my recent watch history so it feels like there's nothing to discover on-site outside of stretching the "my mix" playlist until it starts improvising.


A lot of Neocities pages are full of JS...

It's better to seek out pages at http://wiby.me


I find myself missing the creativity and passion of the old internet, rather than the specifics of how they were built. Is it really so bad that these sites have some scripting involved?


I miss Netscape on Windows 3.1 on a 4:3 CRT monitor in 256 colors with red on yellow Comic Sans bold italic H1 blink tag marquee with an unnecessary trail of ellipses scrolling left to right with 3 iframes with Java and Flash applets with a redirect to goatse.

But seriously, I miss Pythonline, gopher, finger, IRC, and sunsite.


IRC is still up, from Libera.chat (ex-Freenode) to efnet and so on. Gopher has lots of usage at gopher://sdf.org, gopher://magical.fish and gopher://floodgap.org.

On Sunsite, Ibiblio it's still up. And Slashdot.


Without VRML97, we wouldn't have PNG. Ah the bad old days. Netsplit!


Why is JS mutually exclusive to the old web?


You could look into some git GUIs, I'm only aware of gitk and github's app. But I'd bet if you look around you could find a gui that abstracts "git" away from git.


How to get leverage


Gain experience, conviction, credibility, and wisdom.

Make decisions.

Drive initiatives.


One of Cal's earlier books is ALL about this:

https://www.calnewport.com/books/so-good/


Be the one holding the purse strings.


reduce dependency on other people


Leverage, by definition, is having others depend on you.


We may also associate you with information collected from devices other than those you use to log-in to the Platform.


Same here, books on specific technologies are useless to me because you can learn more from the official manuals and playing with it than from someone else's interpretation of it X versions ago.

The tech books I do get are more meta, books about programming in general or design in general, things that you can't just find in a manual.


Learn a new stack, doesn't matter how simple the project is, it's for you. Then slap the new stack on your resume and apply. You can honestly tell the recruiter about a project you did in the stack they're looking for and then interview with the team where you can also talk about your project you did with their stack and the work you've done in the past.

I recognize the problem, this is how I managed to go from embedded systems firmware to sys admin/web engineer.


So PlantUML


The abridged UML history shown in Wikipedia never mentions these two authors. The scope seems to overlap sometimes, of course.

One proposed graph type reminds me of petri nets.


NFTs are digital, not physical. So you need to make sure you can associate the NFT with the product. An NFC tag isn't enough, because like others mentioned, you can move the tag to a different bag so it doesn't actually prove authenticity.

But NFTs are more collectibles, so arguably you could sell the bag WITH an NFT as a bonus (not as proof of authenticity) and I think people into NFTs would like it.

Or make sure the NFT metadata has something to associate it to the physical product so you can prove that it hasn't been tampered with.


I think valid points are bad about typescript being bad, like without a real debugger it's going to be hard to debug anything.

But I firmly disagree with how the article begins.

> In effect, we are shifting complexity from end-developers to library developers.

That is the whole point in creating a library. To hide complexity in a nice easy to use interface/API.


Yeah, the author appears to be complaining that Typescript is forcing them to stay honest even while they perform Mad Shenanigans like dynamically constructing types :/ if TS didn't check it, it would be down to your users to report the bugs in production!


I don't think it's very fair. As a library developer myself, you try to make you user lives easier, which implies being flexible in what you accept when possible. A couple of examples I struggled with recently:

Documenting https://umbrellajs.com/documentation#addclass. The way I documented it is by opening with a code snippet with many of the possible options (which can also be combined!):

    .addClass('name1')
    .addClass('name1 name2 nameN')
    .addClass('name1,name2,nameN')
    .addClass('name1', 'name2', 'nameN')
    .addClass(['name1', 'name2', 'nameN'])
    .addClass(['name1', 'name2'], ['name3'], ['nameN'])
    .addClass(function(node, i){ return 'name1'; })
    .addClass(function(){ return 'name1'; }, function(){ return 'name2'; })
This is pretty easy to do in plain JS, and of course if you are writing code and using it you just read the first 1-4 lines and know what to do for 99% of the cases, while also noticing there's few "advanced/flexible" ways of using it. How would you even do that in TS?

Then there's a classic initializer in JS that works like this:

    function myLibrary(arg) {
      if (!(this instanceof myLibrary)) {
        return new myLibrary(arg);
      }
      ...
    }
This is very useful to create a library like jquery that you can initialize straight away without needing (but also being able to use) the `new` keyword, just calling it like a function and always ensures it returns an instance. To this day I haven't found a way of doing this in TS.


Yeah I kind of disagree that "being flexible in what you accept when possible" is a benefit to the user. It's just more complexity pushed down to the user that is unnecessary.

In this example, I'm not sure why it's the functions responsibility to support all of these options when the user is perfectly capable of manipulating strings and arrays.


While I agree with you, I have seen practical cases where sensing an array of items in the get parameter of a web server is handled differently, similarly to what the parent comment mentioned.


You can list variants of a function's signature in typescript, but typescript won't help you much with "stringly typed" things (like `.addClass('name1,name2,nameN')`).

Different languages have a grain like wood does. And that subtly directs you by making some things ergonomic and some things difficult to express. I love typescript, but I definitely find it changes the resulting code.

Typescript makes "jQuery style" javascript much more awkward to write, because its harder to type. This is good and bad. I write less scrappy code in typescript - which I think makes it a worse language for quick prototyping. But the tradeoff is that I think its a better language for larger teams / longer lasting projects where functions are read a lot more than they're written.

The actual typescript answer for your API is "don't make your API look like that". Its not always the answer you're looking for.


With "template string literal" types TS has gotten incredible at "stringly typed" APIs (more powerful than just about any other type language in existence in this arena, from what I've seen). People have done incredible things with it and its Turing Complete possibilities (including entire games playable in TS types). With great power comes great responsibility, and just because Typescript can do a lot of it now, doesn't mean that you should do it in Typescript.


Honestly, that's just silly. There's absolutely no reason to accept that many different call styles. Why not just take in an array? As a user I don't find things like this convenient, I find them to be confusing footguns. It's a one liner to split your comma separated string into an array as an end-user, but once you add that complexity to the interface in the library you can never ever take it out.


I thought in the js world it was normal to break compatibility whenever.


    declare function addClass(...classes: (string | string[] | (() => string))[]): void;
It's pretty straightforward in Typescript. And when you go to implement it, tsc will make sure you cover all the types your function claims to support.

> This is very useful to create a library like jquery that you can initialize straight away without needing (but also being able to use) the `new` keyword, just calling it like a function and always ensures it returns an instance.

Avoiding having to type "new" is not a very compelling reason to avoid Typescript, especially because Typescript won't let you make the mistake of calling the function without it. It's just not a problem.


That's an order of magnitude less clear in what the function expects than the examples I gave IMHO


Actually, it's not. With the type signature I understand what arguments the function can take. With your examples I have to _infer_ that, and there could be other restrictions that I wouldn't know. Like, can you pass a function and a string? Or do all the arguments have to be functions or not? The type signature tells me right away.


How are literal examples that are strings less clear than saying "string"? How do you know with just "string" the separator method, the format, etc? In my example you have type information AND string format information AND examples, while with TS you'd only have type information


Again, TS does not forbid you from having examples. Your docstrings don't replace Typescript, and Typescript doesn't replace good docstrings. But your docstrings are very unclear on which types you can mix together, and that has to be inferred. And if a user passes in something else by accident, it will fail at runtime rather than warning them the moment they write it.


Who's stopping you from giving examples in a docstring?


You can use recursion and template types to type some pretty complex string values now - I've seen cut down parsers for both SQL and TS written just via TS types which is madness but does show what can be done.

Whether the effort is worth it is, however, a totally different question to whether it's possible.


>To this day I haven't found a way of doing this in TS.

Just make a static method that does initialization if needed and returns a new instance?

https://www.typescripttutorial.net/typescript-tutorial/types...

I'm trying to not be too hard on people as I read this thread, but it's baffling to me that web devs are getting filtered by features that have been in other languages since the 80's and 90's.


There's "be flexible in what you accept", and then there's… "take a comma-separated string which you could parse into your arguments" :)


May be I want to pass JS string to be eval-ed. Flexible, huh.


Your problem would be solved pretty easily by making two changes to your API:

* Only accept an array of strings. Not a single string, not several strings, not several arrays of strings, and certainly not a space/comma-separated list of classes.

* Add another single overload where you accept a function that takes two parameters. That function can ignore its parameters if it wants to, and it returns a list of strings, so you don't need to accept several.

You have the same functionality, it's not harder to use for an end-user, and it's infinitely simpler to type.


If TypeScript steers authors away from either of these patterns then all the better in my opinion.


I don't design my APIs that way unless the language lets me write each (addClass) version as a separate function 'head'. I.e., Elixir and Haskell.

For the other 99% of programming languages, that kind of interface makes the addClass implementation too complicated and forces unneeded branching into it. Consider: when the app developer is calling addClass, they _know_ which interface variation they're using. So they can easy write e.g. addClasses instead of addClass ... completely removing the branching from the code altogether.

Your input and output types are much simpler and static analysis is much easier as well.


I don't agree that library developers should make user lives easier. I want from library to provide only `addClass('name1')`. I can write array iteration, it's not hard. I need library to have a stable interface, as simple as possible. And I need library to have quality implementation. I don't use libraries for fancy APIs. I use libraries for tested implementation code. If I need fancy API, I'll write it for my use-case which will be better for my application anyway.


A lot of times making the user's life easier is about having a single correct way to do something.


It's true. A type specification also has the role of documentation, quickly telling the user how to use the thing. A ridiculous typespec - to support an "easier" API - has the effect of making it impenetrable.


> To this day I haven't found a way of doing this in TS

You use an ambient declaration to declare the missing classish part of the type.

    declare function myLibrary(arg: Type): myLibrary;
    declare class myLibrary {
      member: Type;
      constructor(arg: Type);
    }

You see this pattern pretty often in DefinitelyTyped.


> being flexible in what you accept when possible

I disagree with this. When working with rxjs I wanted to emit a single string in an error handler. Since strings are iterable I ended up with the characters being emitted.

The library authors added this "flexibility" (implicit conversion from values to observables) which caused a subtle bug that took me a while to figure out. A type error (expected observable, got string) would have prevented this.


the type definition of the same is clearer imo and not only that it is enforced by both the ide and the compiler. Libraries typically DO NOT write a bunch of code examples of all the legitimate arguments that can be passed. Also in your example above,

> .addClass(function(node, i) {return 'name1'})

^ what is node? What is i? Seems intuitive to think it must be a dom reference and an index. But in different domains it's not always gonna be so clear. Like I am not familiar with umbrella js, but maybe node could be a jquery object and not a plain dom ref? With typescript you can just say

type GetClassName = (node: HTMLElement, i: index) => string | string[]

// see how I added string[]. So I don't have to add yet another example

// of a function returning a string array instead of a string

and then add it to the union of types that can be passed into addClass. Great, no more guessing based on the domain knowledge I have (or don't), it's crystal clear. And it forces the lib developer to have the discipline to make it crystal clear, which they usually don't I'm afraid.


I agree. I am the creator of the data table lib datagridxl.com and I like to make my methods as flexible as possible. Example:

grid.selectRows(2) // index grid.selectRows([3,5]) // range grid.selectRows([[1,2],[4,6]]) // multiple ranges

It fits in the JavaScript spirit of "we will make it work" which I love.

Other major thing that made me decide to develop in es6 instead of typescript was compilation times. After a ctrl+s it had to compile ts to js for 10 seconds, which is annoying for me, as i like to check & test every minor code change.


> It fits in the JavaScript spirit of "we will make it work" which I love.

Do you also use == and != for comparisons by default?


Check out the library, it's not that bad ;-)


> Other major thing that made me decide to develop in es6 instead of typescript was compilation times. After a ctrl+s it had to compile ts to js for 10 seconds, which is annoying for me, as i like to check & test every minor code change.

Typescript has a --watch mode that compiles as you work. Most test runners also often have a --watch mode. Test runners that support Typescript directly don't even need Typescript's --watch to be running as they'll do both, compile and test in a single step as you save. Anecdotally, the time it takes to run tests dwarfs any Typescript compile times and in a --watch mode of a test runner there's almost zero difference in the time it takes to watch ES2015+ tests or Typescript tests.


> That is the whole point in creating a library. To hide complexity in a nice easy to use interface/API.

Totally agree. Minor ergonomic changes in a library have a wildly disproportionate impact on developers. If I save a developer 5 seconds with a better type, and I have 1000 developers using my library, that's 5,000 seconds I just saved - and that's assuming they only ever use my type a single time! With this in mind, I'm totally OK paying a tax on making libraries a bit harder to write, if it means that the benefits of stronger types fan out. After all, writing that type could take 4,995 seconds for me to write and still be a net positive.

And honestly, 5 seconds isn't even close to accurate. Good type definitions have saved me hours.


Thanks for reading the article!

> That is the whole point in creating a library. To hide complexity in a nice easy to use interface/API.

I think that's a fair point, but generating types that satisfy all use cases is very challenging to get right -- disproportionately so. I could see a world where -- without proper tooling and growing complexity -- typescript libraries becomes so difficult to maintain that people give up or burn out. Maybe that's a pessimistic outlook but I already feel that way some days.


In strongly-typed programming languages, which includes Typescript, figuring out the types *of the interface* is not something that's done after the fact. `@types/*` is an exceptional project meant to back-port JS libraries to TypeScript, but that's the exception, not the rule.

If you write a library in TypeScript, determining what types are present as part of the interface is one of the very first thing that should be done.


> In strongly-typed programming languages, which includes Typescript, figuring out the types is not something that's done after the fact.

Too broad a statement. There's loads of value in having a compiler figure out types for you after/when you write the code.


Hard disagree as far as the portions of it that are part of the user-facing public interface are concerned.

But granted, as a general rule you are correct. I was referring specifically to API interfaces.


If you're talking about type-inference, sure, I guess it's fine.

If you're talking about figuring out what types you're going to accept, you should absolutely be defining that on your own up-front. If you don't even know what your types are how is an end user going to figure it out?


Often libraries are bootstrapped from application code. Having a step function in complexity is not helpful in fostering an ecosystem with a broad range of maturity.


Counter story, when I was a senior I found I missed a freshman requirement. The teacher saw me in the class, I was fine with having the blow off class but the teacher recognized me as a CS student and said I shouldn't be there since I was a senior. Had me schedule and take the final for the class, I passed, and I was able to skip it for the semester.

Doesn't hurt to ask either.


The difference is that you still paid for the class. The university wants the money.


I believe at most 4 year universities, in the US at least, you pay by the semester or quarter and not on the number of classes you take. So the university won't generally make more money if you test out.


They will make more money if you do NOT test out because you have to pay for the credits, not pay by the semester.


Join us for AI Startup School this June 16-17 in San Francisco!

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

Search: