Live data from Hacker News

My thoughts after using Clojure for about a month

acdw.net

201–204 of 204 posts

Re: My thoughts after using Clojure for about a month

#201
post #62
post #6

With respect, this topic in particular has been beaten to death. I too liked Clojure when I tried it some years ago (agreed on the composition and data structures; both are _great_). But the real value-add is in the runtime, not the syntax. Java has a solid runtime but it's not yet as good as Erlang's, maybe even not up to the standards of Golang -- I am talking concurrency / parallelism here (for memory management I…

> Programming language syntax scarcely matters. Certainly it matters much less in the modern era. However, certain fundamental decisions of a language can be dealbreakers. Requiring declarations on your functions and giving those declarations sigils so that they can be parsed quickly is an important syntax decision. Almost every modern programming language has converged to this idea. Or take, for example, Lua. For me…

> I LOATHE the fact that you traverse lists and vectors in completely different ways

In Racket:

    (for ([x xs]) (displayln x))
This works for `xs` being a sequence, which includes lists and vectors.

Making the type explicit generates faster code:

    (for ([x (in-list   xs)]) (displayln x))
    (for ([x (in-vector xs)]) (displayln x))

Re: My thoughts after using Clojure for about a month

#202

Earlier quoted context omitted.

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 li…

> 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…

> 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 =
        Omit & Record;

    function assoc(
        m: M, k: K, v: V): Assoc {
      return { ...m, [k]: v } as Assoc;
    }
(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 = {[K in keyof T]: T[K]} & {};

    type Assoc =
        Simplify & Record>;

    function assoc(
        m: M, k: K, v: V): Assoc {
      return { ...m, [k]: v } as Assoc;
    }
(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 = {[K in keyof T]: T[K]} & {};

    type Assoc =
        Simplify>;

    type AssocValue =
        K extends keyof M ? (V extends M[K] ? V : never) : V;

    function assoc(
        m: M, k: K, v: AssocValue): Assoc {
      return { ...m, [k]: v } as Assoc;
    }
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?

Re: My thoughts after using Clojure for about a month

#203

Earlier quoted context omitted.

“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…

> 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.)

Re: My thoughts after using Clojure for about a month

#204
post #62

Earlier quoted context omitted.

> Programming language syntax scarcely matters. Certainly it matters much less in the modern era. However, certain fundamental decisions of a language can be dealbreakers. Requiring declarations on your functions and giving those declarations sigils so that they can be parsed quickly is an important syntax decision. Almost every modern programming language has converged to this idea. Or take, for example, Lua. For me…

> I LOATHE the fact that you traverse lists and vectors in completely different ways In Racket: (for ([x xs]) (displayln x)) This works for `xs` being a sequence, which includes lists and vectors. Making the type explicit generates faster code: (for ([x (in-list xs)]) (displayln x)) (for ([x (in-vector xs)]) (displayln x))

Yes, this is the kind of thing why I like Racket and Clojure and so much of the Lisperati don't.
Post reply on HN