other-languages

here be heresies and things we have to use for work
cfleming 2016-06-02T05:13:15.001128Z

I just did a straw poll of the 25 top errors in my Rollbar page.

cfleming 2016-06-02T05:14:21.001129Z

18 NPE, 4 CCE and a couple of randoms

cfleming 2016-06-02T05:14:59.001130Z

If I count them by number of occurrences rather than just number of errors, that difference is exaggerated even further.

cfleming 2016-06-02T05:15:17.001131Z

Like 10x further or more, just by eyeballing it.

cfleming 2016-06-02T05:20:25.001132Z

Actually, I couldn’t resist - of the top 25, 70% of the occurrences are NPE, 9% CCE and then randoms.

seancorfield 2016-06-02T06:14:07.001133Z

I find it very interesting that some (Clojure) folks say they virtually never encounter NPEs and other say NPEs are the most common error by far that they encounter...

seancorfield 2016-06-02T06:15:26.001134Z

I don't doubt either set of people. So that suggests that there are two very different types of (Clojure) developers.

seancorfield 2016-06-02T06:17:32.001135Z

I will say that almost the only time I hit NPEs is in Java interop code -- not in my Clojure code. So maybe the propensity for NPEs depends on how much you have to interact with Java?

sveri 2016-06-02T07:17:01.001136Z

Maybe the difference also is in how you make sure your data is valid? I mean, what are possible ways to get NPEs in clojure? Accessing keys in a map that dont exist for instance? (:foo m) -> NPE, so, better use (get m :foo "default") or run a validation function beforehand and handle these? I also think that the interaction level with Java plays a role here like @seancorfield said. @cfleming: Would be interesting to see some examples of your NPEs

cfleming 2016-06-02T07:22:11.001137Z

Virtually everything I do has to interact with Java

cfleming 2016-06-02T07:22:46.001138Z

But I don’t blame Java for the problems, since NPEs were a much smaller proportion of my errors when I wrote Java.

cfleming 2016-06-02T07:23:15.001139Z

The problem, I think, is that once you get nulls into your system, the lack of types makes it very difficult to track them.

💯 1
cfleming 2016-06-02T07:27:38.001140Z

I get a lot of NPEs when I inadvertently pass nulls back to IntelliJ, because I wasn’t expecting some value I passed to be null

cfleming 2016-06-02T07:30:16.001141Z

And again, when relying on nil punning to avoid NPEs, unless you’re aware that that’s what you’re doing IMO you’re just swallowing errors elsewhere in your code.

sveri 2016-06-02T07:40:50.001142Z

Hm...lets say I write a library fro data transformation. It takes its data from a csv file which may lack columns. Part of my transformation algorithm will be how I handle non existent columns, In java you will most probably just put a null somewhere inside (be it a list or a map or your object) and then start null checking everywhere, forgetting some of them. Thats common practice. And there they are. Then your small library will turn into a full application with cloud backend and all hell breaks loose, because your core does not handle NULLs in a way it should. I mean, somehow you have to map the absence of a value in your code and no type system will save you from doing that.

👍 1
cfleming 2016-06-02T09:23:38.001143Z

A good one (like Kotlin’s) will tell me when I get it wrong.

cfleming 2016-06-02T09:24:09.001144Z

Nullable types are distinct from non-null types, and cannot be dereferenced.

cfleming 2016-06-02T09:24:38.001145Z

Essentially, every nullable type is really like a Maybe type.

cfleming 2016-06-02T09:28:17.001147Z

Since Kotlin has perfect Java interop as a main goal, you can get NPEs at the Java/Kotlin border if you don’t handle them correctly, but they cannot spread further into your code.

sveri 2016-06-02T09:37:50.001148Z

@cfleming: I would not argue that it helps with a few cases.But what about nulls in lists or maps?

cfleming 2016-06-02T09:38:38.001149Z

That’s the thing - in Kotlin, you would have to specify a type for the data in the lists or maps, and that type would either be nullable or not.

cfleming 2016-06-02T09:38:46.001150Z

@sveri: ^^

cfleming 2016-06-02T09:39:15.001151Z

So you can't pass null somewhere where you didn’t expect it.

sveri 2016-06-02T09:40:18.001153Z

Ok, just to make sure we talk about the same thing. You have interop with Java and in your java code you fill an ArrayList of Type Foo. But some of that FOOs actually is null, but list.add happily pushes that null into the list. Now in your kotlin code, which knows the list only contains FOOs, how does it handle the null occurrence in that case?

cfleming 2016-06-02T09:41:09.001154Z

I would receive a List<Foo?>, i.e. a list of potentially nullable foos.

sveri 2016-06-02T09:41:31.001155Z

Is every interop type potentially a ? type?

cfleming 2016-06-02T09:42:37.001156Z

Here’s how this works in Kotlin. Types you get back from Java are actually called platform types, written Foo!. But they’re non-denotable, i.e. they can’t be written in the program itself.

cfleming 2016-06-02T09:43:09.001157Z

If you assign a Foo! to a Foo, Kotlin will insert a null check at that point so you’ll get an NPE then.

cfleming 2016-06-02T09:43:22.001158Z

Assigning to a Foo? is ok, of course, since you’re saying you’ll deal with the null later.

cfleming 2016-06-02T09:44:10.001159Z

Since you can’t write these types down and they’re always inferred, they implicitly only ever appear in the function you called the interop from.

cfleming 2016-06-02T09:44:28.001160Z

The collection question is an interesting one, I’m actually not sure what the rules are - one sec.

sveri 2016-06-02T09:45:50.001161Z

Ok, I start to understand the point

cfleming 2016-06-02T09:47:02.001162Z

The doc actually doesn’t talk about the reflection case.

cfleming 2016-06-02T09:47:09.001163Z

I’ll ask in their slack channel.

cfleming 2016-06-02T09:50:20.001164Z

Ok, there’s no runtime check for collection class elements.

cfleming 2016-06-02T09:54:25.001165Z

So you could potentially pass some nulls around in types you thought weren’t null via collections or some other generic object.

cfleming 2016-06-02T09:56:48.001166Z

However not-null parameters of public methods always have runtime checks inserted into the bytecode, so the idea is you catch them ASAP.

sveri 2016-06-02T10:00:37.001167Z

I guess I will have another look into kotlin when I have time again. Thanks for the explanation @cfleming

cfleming 2016-06-02T10:01:25.001168Z

It’s not perfect obviously, but it’s enough to make me consider switching more of my code to Kotlin - NPEs give me a ton of problems, and the fact that they propagate so far into my code makes tracking them down a nightmare.

borkdude 2016-06-02T11:15:43.001169Z

@cfleming: are your NPE problems also due to having to interop a lot with IntelliJ code?

seancorfield 2016-06-02T16:42:45.001170Z

NPEs are certainly hell in Java. Kotlin’s approach is interesting but, as you said, still has holes in it. Frege approaches Java interop by mapping nullable values to Maybe directly (as I recall, it’s been a few months since I did much with Frege).

seancorfield 2016-06-02T16:45:27.001171Z

What makes them such a problem in Java is that a) they popup everywhere b) library functions don’t, in general, accept null as "empty", only "nothing" c) there’s no clean idiom for dealing with them. So you’re kinda stuck with boilerplate if ( v != null ) … everywhere.

seancorfield 2016-06-02T16:47:09.001172Z

Clojure sort of embraces a), treats nil as both "empty" and "nothing" and "falsey", and has plenty of core functions that offer clean idioms for handling nil.

cfleming 2016-06-02T23:16:52.001173Z

@seancorfield: Kotlin used to do the same as Frege, i.e. all types from Java were potentially nullable, but it was unworkable for interop-heavy projects.

cfleming 2016-06-02T23:18:38.001175Z

@borkdude: Not sure, since that’s my main experience. I don’t have many pure-Clojure projects, at a minimum I usually end up using a Java lib somewhere.

cfleming 2016-06-02T23:20:02.001176Z

What I tend to find in the pure-Clojure projects is that I probably get less NPEs, but I have the same number of null-related errors, they just get hidden by nil-punning and the like.

cfleming 2016-06-02T23:20:24.001177Z

Which I’m increasingly thinking I don’t want.

seancorfield 2016-06-02T23:22:13.001178Z

@cfleming: In Frege you declare the Java interop to either return some type t or Maybe t depending on whether it’s nullable. You also declare if something is mutable (or not), and whether it’s side-effecting (using the ST monad).

cfleming 2016-06-02T23:22:59.001180Z

So I guess that’s more or less equivalent to what Kotlin does now, i.e. you can assign your platform types to Foo or Foo?

cfleming 2016-06-02T23:23:16.001181Z

(Kotlin doesn’t have the mutable or effect systems)

cfleming 2016-06-02T23:23:25.001182Z

The problem is if you get it wrong.

cfleming 2016-06-02T23:24:11.001183Z

i.e. the interop boundary with Java is never truly safe unless you make it so safe it’s annoying.

2016-06-02T23:25:35.001184Z

> so safe it's annoying

2016-06-02T23:25:52.001185Z

sounds like most strict environments

cfleming 2016-06-02T23:26:15.001186Z

I’m pretty annoyed with Clojure right now.

2016-06-02T23:26:33.001187Z

me too!

2016-06-02T23:26:47.001188Z

but find me another lisp that does the web stack even a tenth as well.

2016-06-02T23:27:13.001189Z

and for me clojure's power w/r/t webstack comes specifically from its interop to the jvm.

cfleming 2016-06-02T23:27:22.001190Z

Right.

cfleming 2016-06-02T23:27:43.001191Z

But I think there are ways to make that layer better, and helping with nullability would be huge, for me at least.

2016-06-02T23:27:52.001193Z

over my paygrade

2016-06-02T23:28:02.001194Z

i'm just a lowly webdev with pretentions to mathematics and physics.

2016-06-02T23:31:01.001195Z

zero interest in writing even toy programming languages, would rather write 6dof n-body simulators.

jsa-aerial 2016-06-02T23:37:04.001197Z

Frankly, I can't even recall the last time an NPE hit me. I do see 'type' errors occasionally, but NPEs? That's definitely rarer than hen's teeth. @seancorfield is correct - this stuff (NPEs , type errors) seems to say way more about the developers involved (on both sides) than the languages/tools.

cfleming 2016-06-02T23:40:10.001198Z

I guess I’m just null prone.

2016-06-02T23:41:21.001199Z

i have high expectations of my compiler.

seancorfield 2016-06-02T23:45:53.001200Z

I don’t think it’s about the developers — I think it’s about the style of programming: if you do a lot of Java interop, I think you’re way more likely to run into NPEs. I think @cfleming experience supports that and that’s how it feels based on the Clojure I’ve written over the last six years.

2016-06-02T23:46:42.001201Z

i get a lot of NPE's out of Datomic as well.

2016-06-02T23:46:51.001202Z

mostly during refactors, surprise surprise.

seancorfield 2016-06-02T23:47:03.001203Z

Interesting… do you have any sense of what is at the root of that?

2016-06-02T23:47:29.001204Z

null arguments, typically.

cfleming 2016-06-02T23:49:43.001205Z

Yeah, I’m not sure I buy the argument that it’s only interop that provokes NPEs in Clojure

jsa-aerial 2016-06-02T23:49:44.001206Z

OK, yes, that is more accurate - style is much better description

cfleming 2016-06-02T23:50:35.001207Z

I mean, (subs (first xs) 10) is as NPE prone as Java

seancorfield 2016-06-02T23:51:13.001208Z

Well, subs just delegates to String. That’s Java interop IMO.

jsa-aerial 2016-06-02T23:51:21.001209Z

I think this has been brought up before - seems that the style involving less of these 'form' issues revolves around very incremental bottom up development with incremental testing and then aggregating both function and test

seancorfield 2016-06-02T23:52:41.001210Z

I wish Clojure’s thin veneer over String actually handled nil better. A substring of nil in Clojure should just be nil, as should almost every "string operation" on it.

seancorfield 2016-06-02T23:52:51.001211Z

That would make string handling much more idiomatic.

cfleming 2016-06-02T23:52:52.001212Z

In that case, any string work is NPE prone? I mean, every Clojure program ever uses Strings ubiquitously

cfleming 2016-06-02T23:54:19.001213Z

Or any numeric work.

seancorfield 2016-06-02T23:54:31.001214Z

I think it depends on the style of string handling code but, yeah, when I do occasionally get an NPE, it’s almost always in String handling these days (as opposed to string handling 😸 )

jsa-aerial 2016-06-02T23:54:32.001215Z

I suppose that depends on the work - any collection function type stuff should not (in general) have this NPE bomb with strings

seancorfield 2016-06-02T23:55:11.001216Z

Right, and strings occupy a weird middle ground with Clojure — they’re "sort of" collections and they’re "sort of" a primitive type too.

seancorfield 2016-06-02T23:55:56.001217Z

I would love to see a more consistent collection-y approach to strings where it didn’t impact performance too much.

seancorfield 2016-06-02T23:58:42.001218Z

And we already have (str nil) => "" so we have a semantic for nil-as-string so maybe (subs nil 10) should be a StringIndexOutOfBoundsException instead...

seancorfield 2016-06-02T23:59:14.001219Z

…and that’s just a failure to check a boundary since (count nil) is valid and returns 0.