Worried about types + Clojure Elitism


#21

I agree with @sekao that Clojure is unlikely to be anyone’s first language (except, perhaps, folks who come into Clojure via ClojureBridge, which is very specifically trying to reach beginners?) and so it should be expected that the vast majority of conference attendees are programmers with some level of experience who have now “discovered” Clojure. The community is certainly still growing so it’s not like the “cranky old programmer” market has become saturated yet :slight_smile:

If the question was instead “How many of you have been using Clojure less than a year?” I think you’d see quite a lot of hands go up. I’m pretty sure the vast majority of attendees are not yet using Clojure in production – so that’s a lot of people who are enthusiastic enough about a language that is not their day job to attend a conference! That’s a pretty healthy sign.


#22

Clojure is not my day job, but part of the draw for me personally has been the excellent tooling around the developer experience, particularly with handling changes and reloads.

I’d rank Clojure (and Clojurescript) among the best, with tooling delivered by clojure.tools/repl, Component/Mount, and Figwheel. I for one do not think I would have come around to enjoying the language as much without these tools(!)

I sink a lot of time into what makes a good development experience on this side, and I watch developers with less experience fiddling with a lot of manual reloading because that’s what they’re used to. I feel it takes a little bit of exploration and introspection in one’s own workflow to develop the drive to improve it, which might be why something like Clojure isn’t picked over too often at the start.

I was showing some coworkers the kind of reloading developer experience that Figwheel offered this week, and it’s really not until they can compare it side by side in a non trivial application that the “why they should care” is apparent.


#23

@danielcompton yes, the title is inspired by The REPL. Whenever the flames of the eternal static vs dynamic debate flare up again I’ve come to think to myself “oh, people are worried about types again”.

It’s not meant to be dismissive of types themselves here any more than it is in The REPL, I take it more as a reminder to keep a bit of lightness in the discourse. We have had this debate since the dawn of time. It can be really interesting, and occasionally an actual new insight pops up, but there’s no need to get too worked up about this stuff.


#24

I think them saying “Not relying on type safety is unethical” is kind of a jackass thing to say, because it’s an opinion. It’s not a pure fact.

I saw something on Reddit (I’d have to dig a bit to find it) that projects that use languages like Scala, Kotlin, Clojure, Elixir, Rust, Nim, and Crystal (not 100% sure it included those last two) have less buggy code. If you noticed there are 3 (or 2) dynamically typed languages on there. Yes, a majority are statically typed, but if a dynamic type system was truly “unethical” then people wouldn’t be writing such decent code with them.

When people gripe about dynamic type systems, I honestly think it’s really JavaScript’s fault because what a massive joke it can be sometimes, and how doing a lot of things in JavaScript can be a giant pain in the ass: so one thing people can blame that on is the dynamic type system it has, and not that it’s just not that great of a language a lot of the time.

EDIT: I found this Reddit post but it wasn’t the one I was looking for…I’ll keep digging.

Ah ha, it actually is! However, if you look at some of the comments on the post to r/programming, you’ll see that this is research is clearly flawed.


#25

Hey everyone,

The elitism of Lispers is legendary and predates Clojure by a lot. The phenomenon has been given a name: Smug Lisp Weenie.

So this is just my opinion, but I think Clojure is having trouble “crossing the chasm”. There’s a book by that name that describes a theory of how products make it to the mainstream. The theory is called “Diffusion of innovations”. Clojure is a well-designed language and so has a lot of natural appeal. It easily got the first 16% of the “market” with innovators and early adopters. Rich Hickey’s talks, a lack of other options early in its life, and a lack of “practical” Lisps gave Clojure a huge, early migration of people ready to learn the warts and brave the lack of tooling.

The thing is, those people run out. If you want to get to that giant hump where most people are, you have to really appeal to the more conservative decision-makers. The early majority are looking for practical evidence that this is a good decision for them. They’re not concerned with features, and they won’t ever learn the language just to find out all the wonderful things we love about the language. They may already be interested in Clojure, but they’re waiting for a critical mass of their peers to jump in, too. It’s a chicken and egg problem. I believe this is where the excuse of fear of not being able to hire comes from. You can read more here.

The biggest issue is that Clojure programmers are all early adopters. We can’t sell Clojure to the majority with the same ideas that attracted us. And the only way to figure out what will convince others is to hit the pavement and talk to people who are kind of interested in Clojure but have not adopted it for some reason. Empathize with them, figure out what they want, and then give it to them.

About types: seeing all of the flamewars online, I try to elevate the conversation and transcend the differences most people fight over. That doesn’t always help others or the discussion, but it often helps me. It’s therapeutic and helps me develop my views of things. Another way it helps is to avoid getting caught in the repetitive back-and-forth of the flame trench warfare. I guess I’m like a war journalist on the Maginot line.

As an example, I wrote a post comparing the perspectives of static and dynamic typists. When I diagrammed it, I realized that we have fundamentally opposing assumptions. Static typists believe that untyped programs are a subset of typed programs, probably because that’s how you would implement a dynamic solution in a static one. On the other hand, dynamic typists see typed programs as a subset of dynamic–they’re programs checked for type correctness.

The reason I like this is every time I see a discussion, it’s very clear where the arguments come from. It’s in the nature of this type of discussion–the ones that get nowhere but people are fighting very hard–is that people are talking past each other. It is so clear when someone is stuck in either Church or Curry thinking. When they’re stuck, I have very little desire to argue with them.

Rock on!
Eric


#26

Hi Eric,

I read your article. That was interesting, although I don’t fully comprehend the implications.

When you write in Haskell:

f :: Int -> Int
f = id

g :: Bool -> Bool
g = id

then g and f are different functions, while in Clojure they would be the same, because extrinsic typing is sort of an “add on”, that you run separately as e.g. a linter, but could also do without?

Does this mean that Java/Scala/Kotlin are also intrinsically typed? What are good examples of other extrinsically typed languages?


#27

He @borkdude,

Java is a really interesting case. The JVM’s semantics are very dynamic. There’s an object model with methods, etc, and the types can be hinted but it’s mostly dynamically dispatched methods. But there’s very little type system in the semantics of the JVM. However, the Java language is intrinsically typed. Type correctness is an important aspect of the semantics of the language. Kotlin and Scala definitely are intrinsically typed as well.

It’s a good question. The way I see it, extrinsically typed means the types can exist, but outside of the semantics of the language. For instance, they can just be in the programmer’s mind, or in a comment, or in a type system imposed externally.

So in that sense, I’d say that any dynamically typed language (or if you prefer, untyped language) is extrinsically typed. A Python programmer does think in types and has some notion of type safety and reasoning about types. So does a JavaScript programmer.

Eric


#28

Here’s my experiences. I don’t interact much with programming communities anymore, so if you don’t agree, maybe you know better than me.

Maybe it’s hard for new Clojure programmers to find mentors. If you work at a Clojure company, people would help you set up your computer and IDE. I hope ClojureBridge has an alumni mentor program. Saying something wrong in public is an easy way to feel unintelligent.

The tech industry’s elitist in general, and I had to swim against a current to use tools like Clojure. Other programmers, schools and companies have been hostile to my interests in things like Clojure and Lisp Machines. Even newer programmers are incentivized to be glib and argumentative.

When programmers ask why I like Clojure, I’m suddenly expected to play a salesperson and haggle. (For most other topics, they try to charitably interpret my words instead of firing back with the first half-baked retort that comes to mind. But not this topic.)

Maybe Clojure-interested programmers get filtered out by the wider tech industry?

FWIW, I use Clojure at a “hip” entertainment corporation and for activism. Yep, I work on a small self-managed team. If I were part of a conventional programming team, either I wouldn’t have much autonomy – precondition of adulthood – or be one of the few adults.


#29

This is a super interesting take Eric, thanks for sharing. I never looked at it that way but you’re absolutely right, and it explains a lot.


#30

Found this edifying post in the “People are worried about types” section of The REPL:

The author takes Rich Hickey up on the challenge: to think of every parameter to and the return value from every Clojure fn as being of type EDN.

He then proceeds to define the EDN type (in Haskell) and then writes a map fn that uses it:

clmap :: (EDN -> EDN) -> EDN -> EDN

Along the way he makes a case for why you might want to use Maybe and Nothing to make the EDN type more tractable.

I’ve always thought of the static typing argument from the standpoint of “what would Clojure look like with static typing”. Seeing it from the other side: “what would Clojure typing look like in Haskell” provided a valuable shift in perspective.


#31

I definitely think more effort could go towards making Clojure accessible. The language itself is easy to learn and fun to use once you develop a good workflow. However, lack of tooling and documentation around it make it unreasonably difficult to get up and running on your own.

I think the problem is also compounded by the fact that a lot of the tooling is not well known outside the Lisp community. Using Clojure without the REPL and paredit/parinfer can feel downright painful. If nobody shows you that these tools exist, or how to use them effectively, then you can spend a lot of time getting frustrated.


#32

That Front Row Ed article is very biased and makes its “point” by misrepresenting EDN and Clojure and skimming over (i.e., ignoring) a lot of details about semantics. It gets many function signatures completely wrong, omits arities, and is really rather self-serving instead of being suitably enlightening.


#33

Hi,
I just signed up so I could reply to this as it is an issue I feel strongly about and perhaps could shed some light. I haven’t read all the replies on this thread but many here seem to be experienced with Clojure and programming in general and there seems to be a variety of opinions on why new programmers aren’t taking up the language.

I consider myself a beginner Clojure programmer and somewhat of a beginner programmer in general too, though I have enough experience to kind of know what I’m talking about. As background: I completed a BSc in Computer Science at a university in New Zealand in my early 20s having not done in any programming prior to that and did not get a job in the tech field after that and have not been programming since but recently got back into it. So I don’t really have much practical programming experience only undergrad programming experience so in that regard I am a beginner.

I started learning Clojure programming some months back using Clojure for the Brave and True tutorials online (I think that was it) and I found that while it can be easy to get started with toy stuff doing these tutorials using tools like LightTable, online sites that let you type and run Clojure code on the site page and just using Leiningen and the REPL then at some point you want to graduate to more practical real-world programs and want a more ‘professional’ dev environment. This is where it gets really, really difficult for me as now you’re looking at something like IntelliJ IDE with Cursive or Emacs with the associated Clojure plugins or whatever you call them.

Learning how to use Emacs is like learning a new lang in itself and I tried what I thought was the easier option in IntelliJ - as soon as I try to build or run my program or whatever, instead of my program building or running some menu screen pops up with no helpful error message or anything. I suspect I have some Java build configuration setting or what not misconfigured. I can build and run Java programs and other langs just fine though.

After looking at various IntelliJ tutorials and searching for help on that, after a couple days, I just gave up, despite Clojure being my favourite programming language out of what I looked at so far. I still am planning on giving it another go and have already but each time I get stuck on getting a decent dev environment set up.

Learning a language full of parentheses, Lisp weirdness and a different way of thinking about things than OOP was not such a problem for me as a beginner. In fact it makes it more exciting and inspiring to learn because it’s fresh food for the brain. Getting stuck on build configuration issues or whatever it was while trying to get a bare minimum decent dev environment up and running is really not a fun, inspiring problem for a beginner. That’s the kind of problem that makes a beginner go “Ah! Screw it! This isn’t worth the trouble.” Especially when for just about every other language under the sun you can download, install and have a nice dev environment up and running in around 10 minutes. So given how long it’s taken me to get a dev setup going with Clojure which I still haven’t managed then I am thinking Clojure is worse than other languages in that area by many orders of magnitude.

So in conclusion, speaking as a Clojure beginner, I would say Clojure’s number one problem with attracting beginners is the dev environment setup. It’s simply monumentally painful for me. Any other reasons that may be mentioned here in this thread I would say are very secondary issues to this one here. Some batteries included IDE that can make useful real-world programs with fairly professional tooling but is still easy on beginners would go a long way towards fixing this situation I would think. And it would need to be promoted by the community as a clear choice for beginners.
I think Zach Oakes has something new out. So I will check that out and see if it helps :slight_smile:


#34

I think you’re right about this. We need an integrated dev setup solution. That means REPL, testing, and dependencies, all set up for you, without requiring any particular editor.


#35

I love Zach’s idea to start with a lot of hand holding but allow users to export their projects into a local setup when they feel ready.


#36

Okay, I’ll bite.

I think it is useful to look at the type signature for something like clmap that @billburcham mentioned. That article definitely shows the mindset of the statically typed (Church types) crowd. Clojure data is untyped, so obviously it would all be one big algebraic data type.

If you were going to go all the way with Clojure, functions are a valid data type, so you actually need a case for functions in your EDN type. The clmap type would then be:

clmap :: EDN -> EDN -> EDN

How absurd this type is! What’s more, nearly every function’s type signature would look like this. There’s no information in it except the number of arguments. And that’s true from a certain perspective. This is exactly as much static type safety we have in Clojure–none.

However, we, as Clojure programmers, know that not all EDN values are valid for the arguments to map. We know it, even if it’s not written down. Even if no checker is watching over our shoulder. We know that the first argument should be a function. We know the second argument should be something you can call seq on. And we know it will return a seq.

But the article does not acknowledge that. It just says “look at this silly type” to dismiss Clojure. Such an obvious straw man. The challenge is to do what Clojure does but in a Haskelly way, not to do what Clojure does in Haskell. Why use Haskell at all if you’re going to throw out type safety?

So let’s start to build out the types. The mental types for Clojure’s map are very similar to Haskell’s, but perhaps a little richer.

clmap :: Seqable L => (a -> b) -> L a -> Seq b

It’s something like that, except it’s only as strict as you want to be. That is, a and b can be precise types that a Haskell compiler would understand, but that’s up to you. You could do anything you wanted. That’s actually a huge difference between the static and dynamic views. In static, what you can do is primary. In dynamic, what you actually do is primary. For instance, you could say that a doesn’t have to be any particular type, just that the values in the Seqable are printable. The Seqable could be heterogeneous, but that’s okay because we’re only going to call print on them. It’s not possible to do that in Haskell without building a new type and all the ceremony that entails. To me, though, it seems perfectly reasonable to build a List of printable stuff to print out later. Making that possible seems like a good thing and I have ideas about how to make it possible in Haskell. That would give you the type safety of Haskell with some of the flexibility of Clojure.

Let’s look at Clojure’s hash maps. When we use maps in Clojure, there are a few possible scenarios.

  1. We know (or assume to know) that the map contains some known keys and potentially others that are unknown.

    In this case we treat the hashmap like an “entity”. We can dig values out and update them, leaving the keys/values we don’t care about in place.

  2. We don’t know what keys are in it.

    We can treat the map as a generic container of key-value pairs. That means we can iterate over the keys, merge two of them, etc.

  3. We don’t know the keys and values, but they’re of homogeneous types.

    This is like the index pattern, where you’re using the hashmap not as an entity record but to provide a constant-time lookup for values based on an index. group-by and frequencies provide hashmaps like this, and it’s common to build them yourself. Think of a hashmap used to look up something by name.

Hashmaps in Haskell are really only meant for #3, where things have easy types (Hashmap String Int). In Haskell, they use data types to represent entities like #1, but those have fixed keys. And you can’t do #2 with them, that is, treat them as generic containers. I know people mention Row Types. That’s great! I think they’re really interesting. But it’s just one feature that kind of solves for case #1 – it lets you talk about minimum subsets of keys and treat them somewhat generically. But #2 can’t be solved by them.

When I say “Row Types are just one feature”, what I’m trying to get at is the challenges of “situated software” require a wholistic view on things. Row Types could play a significant role in a proposed design solution. But there are countless other decisions that need to be made. It’s like saying you can use Immutable.js in your JavaScript, so why use ClojureScript? Sure you can use it, but is it integrated in? Does it cause more impedance mismatch than it solves?

You can’t point at Row Types as an answer and then give your examples in Haskell because Haskell doesn’t have Row Types. You can’t point to a bunch of features and tell me to put them together however I want. That’s a bit like someone asking what web framework to use in Clojure and I tell them to put together a bunch of libraries themselves. It might be how we do it, but it’s not a satisfying answer to the question.

If the existing tools of Haskell (or any language) do give you what you need to write “situated programs”, then where are the guides for how best to do that? Or if they are cumbersome to use, perhaps there are best practices around how to reduce the cumber. Clojure isn’t perfect and we have to do a lot of the same. Or maybe people know it’s possible but they haven’t quite found out how yet. Just be honest and say that. I mean, Rich Hickey really did just give that talk. No pressure to answer right away. Or maybe the whole enterprise of “situated programs” is not relevant. But they’re not saying that, either.

Eric


#37

Yes I think so. I’ve done the exercises and Clojure tutorials and stuff, it is not so difficult to get started, though certainly the lack of clear cut choices there doesn’t help much either. It is now when I want to take the training wheels off and graduate to a more full-powered dev setup that things fall apart. There is no great dev setup with first class Clojure support. No Visual Studio for Clojure. And I think that really hurts it for gaining new people. It has certainly proved a major frustration and roadblock for me.

Just having IntelliJ with Cursive integrated already and everything pre-configured would certainly help. I believe they may have mentioned on their site that they were going to do that at some stage unless I’m mistaken.

I’m still not sure that would be the best solution though as their business model seems more geared towards upselling people to the paid IDEs and you need to sign off on that agreement thing they have to download Cursive. I imagine that would put off a fair number of people getting started too. If they had a decent, free, fairly full powered, batteries included Clojure IDE that would be pretty cool though. I think that is what is needed to get more people to adopt the language.


#38

A critique of types as entities

Rich Hickey has a really nice talk called Clojure Made Simple: https://www.youtube.com/watch?v=VSdnJDO-xdg&t=49m53s

At around 50min, he shows this slide:

The question is, how many key-value mappings are there? It’s actually really hard. Because there are some that are explicit, like getAttribute() that returns the value of an attribute given the name of the attribute. And there are other similar ones, like getHeader() and getProperty(). But then, if you squint, you see that all the get... methods are really just keys for their respective values. So getLocalPort() is just going to return the port. The method is the key, the port is the value. And most of the other stuff is like that. But they’re all ad hoc and different. He then shows how a Ring request map can represent the same stuff but through a unified interface, the map.

Now, to be clear, you lose something. Specifically, you lose static type information. That Java class did encode a lot of static information that we’re now throwing out. And you get static checks on the names of the methods.

But we gain a lot, too. We can iterate over the keys. We can serialize it as we serialize other things. And generally, we know how to operate on hashmaps because it’s common in Clojure. We don’t have to learn any new patterns. It’s at this higher level where a lot of the magic happens. Reuse. Decoupling. Composability. Higher-order constructs.

I think Rich is making a similar claim with Haskell’s types. Many times, you’re just creating ad hoc key-value data containers. And each of them has a name and writes in stone what keys it accepts. You get safety. But if you put them in a map, you’d get the other things.

Clojure separates out defrecords from deftypes. deftypes are for low-level implementation entities. They have a fixed set of fields. These are equivalent to Haskell’s records. But there’s nothing like defrecord in Haskell. There’s nothing to flexibly and consistently represent domain entities. Haskellers use the same construct for both. At first I didn’t understand why Clojure had two things. But after a while I saw that it was a very profound design decision. In the end, though, using hashmaps instead of defrecords won out. It turns out you didn’t need a third thing except in very limited cases.


#39

I haven’t tried that one but I did try the pre-cursor nightcoders and liked it. Indeed I made it my main tool for learning Clojure at the time. My main issue is that it is nice to start off with these environments for beginners but then when you want a more fully tooled environment there is no easy entry point in the Clojure world. There is no fully tooled environment with first class Clojure support.

Other languages have their Visual Studios and Netbeans that are fully powered and easy to get up and running.
Clojure has a variety of choices none of which are particularly ideal, especially for someone just getting started having come off something like Nightcoders or LightTable.

I did hear a little while ago that Zach had something even newer than nightcoders.net which sounded like it was aiming to be more fully tooled, batteries included but I only read a couple sentences of description quickly so I may have misread. I plan on checking it out as soon as I can :slight_smile:


#40

I believe you’re referring to Lightmod. Thanks for your feedback on your early Clojure experiences!