Hacker Newsnew | past | comments | ask | show | jobs | submit | perarneng's commentslogin

I would rather own a used MacBook AIR than a new MacBook Neo. I usually don't like used computers but I just can't stand the anxiety of having to only have 8Gb RAM. Sure, it swaps, it compresses memory etc.. but still.

According to Dell, 8GB is perfectly fine, even under Windows, since that's what its Neo competitor ships with.

I spent a week with Clojure and coming from other functional languages my problem was not Clojure, it was dynamic typing. I got strange bugs in the standard library because I accidentally sent in nested incompatible instances of objects and it was really hard to figure out what was wrong in a quick way. With typesafe languages you are stopped at compile time.

Clojure was explicitly designed to be dynamic. It’s a feature, not a bug.

https://clojure.org/about/dynamic

Until you get better at not making mistakes that the training wheels of a static type system “protect” you from, lean into the REPL as a means to build up small correct expressions into larger ones.


"Until you get better" is such an arrogant take.

It's not just about skill. It's about maintainability, ease of refactor, and modeling invariants in your code in a way that they can be checked by the machine (the compiler) without every single developer having to maintain them in their head.

Clojure even knows this is an issue and many people use `spec` to sort of retrofit static typing.

Dynamic typing was, is and always will be a mistake. There is nothing you can do with dynamic typing that you cannot do with a sufficiently powerful static type system - and it doesn't have to be something absurd like Haskell's. You basically just need structural typing and type inference and some type-level programming constructs.

The worst part about Clojure is the community. Rich Hickey has cult-like status and the only thing clojurians can do is parrot his inane commentary.


I too agree that "until you get better" isn't a good take. To err is human, and even the most experienced developers make mistakes.

That said, you don't get static typing for free. As with many things it's a trade-off: you catch some errors at compile time in exchange for working within the confines of the type system. The ultimate hope is that the time you spend fiddling with types is going to be less than the time you spend debugging type errors.

> There is nothing you can do with dynamic typing that you cannot do with a sufficiently powerful static type system - and it doesn't have to be something absurd like Haskell's. You basically just need structural typing and type inference and some type-level programming constructs.

Haskell doesn't have a complex type system for no reason; it's necessary to encompass everything it wishes to do, and even then it's not as flexible as a dynamically typed language.

For instance, how would you statically type Clojure's `assoc` function? It's not at all trivial if you want to retain the type information of the keys and values.


The problem with your counter-argument is that it hinges on a false premise: That you need or even want a function like `assoc` which is polymorphic over everything. It's an extremely overloaded function which does a lot of things at once, and in many circles and arguably in general within the realm of software design, this is considered a smell.

In practice, what you want is something that allows you to do this safely for the concrete type you're working with. If you want an abstraction that covers all of it, there are ways to achieve this in a type-safe manner, such as traits/type classes. Even in clojure, you're not working with everything at once all the time. You are working with a record, or a vector, or whatever. The fact that you can use one function for all of them is mostly just needless cleverness. In Clojure, you have to keep the type of the data you are working with in your head at all times, because even though `assoc` "just works" for many cases, that's not true in all cases. It will happily insert an integer key into a record without issue, which may or may not be waht you want. But you can also try to insert an atom key into a vector, which then crashes loudly. This is clearly an asymmetry in the abstraction.

Moreover, pointing out that Haskell cannot do what you'd want to do in this case doesn't make a lot of sense. I mentioned Haskell precisely because its type system is extremely powerful and complicated to understand for a lot of people, but still doesn't achieve the kind of flexibility we are looking for - it lacks row polymorphism.

To answer your actual question: Typing a function like that for the individual cases is bordering on trivial in a language such as typescript. For the record case, you don't even need it, because in practice, you get the correct type inference for free by just spreading one object into another.


I don't see an asymmetry in the abstraction. Both vectors and maps are associative structures - you can assign a key to a value - the only difference is that vectors have a more constrained keyspace (i.e. ordered, consecutive integers starting from zero).

But that wasn't really my point. Even if we limit `assoc` solely to maps it would still be difficult to type effectively.

For instance, suppose we have some code like:

    (let [m* (assoc m :number 3)]
      (:number m*))
We can see that the return type of this expression is obviously an integer, but what is the type of m*? How do we type m* such that (:number m*) can be inferred to be an integer by the compiler?

Most statically typed languages sidestep this problem: instead of using an open data structure like a map, a closed structure like a record or class is used instead, and these structures must be explicitly typed by the user.

The problem with this approach is that now every record is specific and bespoke. You lose access to all the general-purpose functions that operate on generic data structures, and as records and classes are closed, you also lose the ability to extend them.

This is the ultimate problem with static type systems: you're trading capability for safety. If you're programming within a static type system, there are options that are simply not available or feasible to use.


> I don't see an asymmetry in the abstraction. Both vectors and maps are associative structures - you can assign a key to a value - the only difference is that vectors have a more constrained keyspace (i.e. ordered, consecutive integers starting from zero).

The asymmetry lies in the fact that it's an overloaded function that's supposed to do the right things every time, but in some cases, it does what is arguably the wrong thing, silently, and in others, it refuses to do the wrong thing and fails loudly. It's better that it fails loudly, of course, but the point is that the ergonomics of the abstraction is lessened because you can't just assume it will work. You effectively have to keep the types of all the things involved in your head and/or trace them to ensure that you don't run into a crash.

> We can see that the return type of this expression is obviously an integer, but what is the type of m? How do we type m such that (:number m*) can be inferred to be an integer by the compiler?

This is trivial in TypeScript. You can see it in action here: https://www.typescriptlang.org/play/?#code/MYewdgzgLgBAtjAvD...

  const m = { name: "weavejester", active: true };

  const mStar = { ...m, number: 3 };
  //    ^? const mStar: { number: number, name: string, active: boolean }

  const x = mStar.number;
  //    ^? const x: number
> This is the ultimate problem with static type systems: you're trading capability for safety. If you're programming within a static type system, there are options that are simply not available or feasible to use.

This is just not true. It's true for some certain specific static type systems, but not true in general, and that brings me back to my original thesis: You just need a sufficiently capable type system with the right properties - structural/row polymorphism, ish, plus type inference. And also my Haskell point: it doesn't have to be an incredibly complicated type system that is beyond mortal ken. TypeScript is already doing this and it's arguably one of the most used programming languages on earth.


> You effectively have to keep the types of all the things involved in your head and/or trace them to ensure that you don't run into a crash.

You make this sound difficult, but in practice type errors are rare in Clojure and generally caught in the REPL or by tests, since the moment you go down a branch with a type error an exception is thrown.

Contrast this to errors caused via mutable state, which are usually far harder to track down, because the failure condition is more specific.

> This is trivial in TypeScript.

In the example you give you're omitting assoc entirely, which defeats the point. I'm using assoc as a minimal example, but the same principle applies to more complex functions, so replacing assoc with the equivalent expression doesn't tell us whether or not we can effectively type a function that deals with maps.

So lets try doing this properly. At minimum we need something like this:

    type Assoc<M extends object, K extends string, V> =
        Omit<M, K> & Record<K, V>;

    function assoc<M extends object, K extends string, V>(
        m: M, k: K, v: V): Assoc<M, K, V> {
      return { ...m, [k]: v } as Assoc<M, K, V>;
    }
(Note that we need to perform an explicit cast in order to inform TypeScript of the type of the key.)

However, this produces some rather messy types consisting of nested Assocs. In order to get back to something a human can read, we can use an additional Simplify type to force the type system to reduce it back down into an typed object:

    type Simplify<T> = {[K in keyof T]: T[K]} & {};

    type Assoc<M extends object, K extends string, V> =
        Simplify<Omit<M, K> & Record<K, V>>;

    function assoc<M extends object, K extends string, V>(
        m: M, k: K, v: V): Assoc<M, K, V> {
      return { ...m, [k]: v } as Assoc<M, K, V>;
    }
(The empty `& {}` intersection forces normalization, providing a cleaner reported type.)

We're still not done, though, as if we want the same type checking that a class has, we need to ensure that a key cannot be overwritten with a value of a differing type. So we'll type the value argument as well to ensure it matches the type of an existing value within the map:

    type Simplify<T> = {[K in keyof T]: T[K]} & {};

    type Assoc<M extends object, K extends string, V> =
        Simplify<M & Record<K, V>>;

    type AssocValue<M extends object, K extends string, V> =
        K extends keyof M ? (V extends M[K] ? V : never) : V;

    function assoc<M extends object, K extends string, V>(
        m: M, k: K, v: AssocValue<M, K, V>): Assoc<M, K, V> {
      return { ...m, [k]: v } as Assoc<M, K, V>;
    }
So this is possible to type in TypeScript (to its credit), but is it "trivial"? And is this type signature significantly less complex than one might find in Haskell?

“Until you get better” at pedaling, training wheels can help.

It’s not an arrogant take; it’s arrogant to think you know static typing is a requirement for developing software well.


It's not about "knowing" anything. It's about admitting that humans are fallible meat computers that can't hold invariants in their head across thousands or millions of lines of code and possibly an exponential number of interactions. It's using the technology we are capable of building to help us because it's the obvious thing to do. The notion of dynamic typing as an attractive programming model hinges entirely on the hypothesis that it lets you somehow express things that you need or want to be able to express that static typing prevents you from doing, and that is demonstrably false. The `assoc` example above is a perfect example.

> that can't hold invariants in their head across thousands or millions of lines of code and possibly an exponential number of interactions.

There’s your problem. And static typing won’t save you either.

The skill is not that, it’s the ability to compose and evolve systems such that you don’t have to hold so much state in your head.

(Btw that property can hold for a million LOC codebase.)


It’s both.

No thunderbolt is a big no for me. Its one of the greatest feature of MacbookPro that makes it dockable and expandable as a desktop with a good thunderbolt dock.

Thats also possible with usb-c.

With some caveats, you wouldn't be able to connect two 4k monitors to a dock without TB5.

USB 4 v2 has the same display capabilities as TB5. In fact, TB5 gets its display capabilities from USB 4 v2

> Raspberry Pi Zero 3W with proper sleep states to reduce sleep power consumption, lower idle power while awake, and 1 GB of RAM even if it doubles the price.

Yes, that would be kind of a dream device, perhaps also if it could suspend the os when asleep so it doesn't have to boot every time but I guess that might the standard way of doing it.


The dangers is videos that slip through the cracks, they get an indirect seal of being non AI.


Exactly, people want to talk to AI when they choose to, as a tool, but not reaching out to other human beings. There is no easy way of solving this sadly


"a small walking pad" - which one did you end up buying and are you happy with it or secretly wished you would have bought a different one?


I bought one called "Zipro New Lite", but it seems to me this one, along with many others, are just "brand" variations from the same chinese manufacturer. No regrets, happy with it, has an app (fitshow) integration with myfitness pal and google health. It has a display for number of steps, calories, duration, etc. on the board itself so you don't even need the app. The only thing I keep track of are steps. Trying to hit at least 8-10k a day.


I had one briefly but I think it's important to think through the logistics of using it. Even the small ones are somewhat heavy and cumbersome. I ended up returning mine because it was too much of a pain to set something up and tear it down just to get steps in. Maybe different if one has a dedicated place for it.


I just bought a WalkingPad M2 and my first day on it was yesterday. I usually sit on my couch while working. I walked about 6 miles yesterday instead! I woke up with sore calves and I think I'm getting blisters but these are probably good signs.


Take magnesium, my friend. Malate in the morning, glycinate in the evening and you're good to go!


at work there is a small conference room with a unSit. Not small - it is shorter, but also wider. I like it and often reserve that room for an hour to get some steps in. However I've never tried any others and so I can't say if I'd be unhappy with the others. I did find a free craiglist treadmill a few years ago, but the setup I had with it meant I was rarely using it.

I don't think I could stand/walk at a desk all day, but I still want one because I could easily do several 1 hour blocks during a day and it would be better. However some ability to get a chair seems important if it is at your only desk.


I bought the DeerRun Q2 last year and like it, although the pad itself is small so it’s only good for somewhat shorter people.


100% governments needs to crack down on these manufacturers and basically say that if China does not stop this insanity then importing them in to the US or EU will become illegal since its a national and personal security risk.


Not going to happen. Governments are very complacent. The UK only recently banned Chinese IP CCTV cameras from sensitive government sites!

Its impossible to do with anything that gets updates. You never know what the next update will bring. Obviously things that connect to the internet and do not get updates are hopelessly insecure. its also pointless for anything controlled by an app through a server (anything from cars to fridges these days) as you cannot control what the server does.

Governments will also find excuses not to do this as its expensive and inflationary. They will play down the risk, point out the Americans can do it too, etc.


Nobody is even trying to make the trust better either which is odd. If you have a baseline distrust as soon as people ar are going to get unemployed most people will feel unsafe in their job position. This will quickly escalate the distrust.


> Nobody is even trying to make the trust better either which is odd.

The average working class Joe opinion does not count anymore. Corporations do not care about consumers opinion, the money is in other big corporations. In an unequal world with such high wealth concentration, power concentrates around that power.

The discourse is around growth for CEOs to get big bonuses, the trust comes from the promise of increased profits and reduced employee count. Your or my trust does not matter, so they do not even try.


As long as there is high demand for it. AI will reduce that current demand.


That’s a bet and remains to be seen.


AI automation will shift the labor demand curve. Once jobs that were previously done by humans are done by AI, this directly reduces human labor demand.


That's also a bet and also remains to be seen


Labor substitution is math, not a bet.


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

Search: