Unfortunately, there isn't a really great language that enforces working like this, while being simple enough to push onto a big team :/ (if there is, please let me know)
Unfortunately, there isn't a really great language that enforces working like this, while being simple enough to push onto a big team :/ (if there is, please let me know)
I've done a lot less statically-typed code than dynamic (mostly Python) in the last few years, but I was playing with Unity3d recently and had a chance to write a good deal of C# with Visual Studio.
I hit a hairy problem some time along and had to do a big refactor to support a new feature. I deleted a single line of code, followed an error trail for 10 minutes, and suddenly everything was just done.
It was a pretty interesting moment for me. I realized that statically-typed languages can really have the potential to be as or more productive than dynamically typed languages paired with a good enough IDE. (And Visual Studio with C# is about the best pairing you can get).
I've had to reconsider my thoughts on these things a bit. Sure, there are things that statically typed languages can make much harder to test or work with. (Want to stub an external provider? Okay, you're going to need an extra interface, then you'll need to create a new stub version that implements that. Want to read/write pretty-arbitrary JSON? Good luck). But there are other places where you get huge wins by bugs just disappearing by the boatload.
I'm still not on board with heavy OO/inheritance, and love the pattern of simpler struct-style constructs with just functions in functional programming, but the static typing can give a lot of wins.
I think something that gives an inherent advantage to OO languages in IDEs is that SomeThing.<tab autocomplete> makes a lot of sense and is easy to compute! I can take an object and know what I can do with it at a glance. I haven't seen a functional language with enough structure to support that simple feature yet (though maybe I'm just not looking hard enough). This is really where statically typed languages can make the most of an IDE. For some reason, that's the big thing I think of when I'm thinking about the downsides of functional programs I'm working with. The editors just seem to help a lot less (though I haven't written any FP professionally-speaking, so have less experience in general with tooling).
F# using Records with member methods looks like it might be able to get that sort of benefit though, I'll need to try that. It looks like they're just pure functions declared on immutable structs, which I think is the perfect middle ground.
A lot of object-oriented languages have taken tips from functional languages lately (map/reduce/filter is the new hottie), but I think there's a lot of benefit still to get in the opposite direction.
F# (as well as OCaml) offers something similar in that you'll use a lot of functions that are within modules with the same name as the type you're working with. So you can write "List." and get a list of functions (map, reduce, etc.). I'd prefer something like Idris which will disambiguate functions based on the relevant types, but at least it makes IDE support easier.
> A lot of object-oriented languages have taken tips from functional languages lately (map/reduce/filter is the new hottie), but I think there's a lot of benefit still to get in the opposite direction.
Something in particular I wish F# would add it general non-linearity of definitions. All files and definitions in F# must be strictly ordered (either type A can reference type B or vice versa, but not both) except for specific, contiguous blocks. It presents a challenge for type inference, but I think just punting it back to you for the tricky cases would be fine (and it often has to do this anyway).
If you do need to get around it though, you can have mutually referential types in F# if you use the "and" keyword (although the definitions of the types have to be right next to one another). And in the next update to F# you'll be able to have mutually referential types and modules within the same file which is often good enough for most other things you might need that sort of thing for.[1]
[1] https://blogs.msdn.microsoft.com/dotnet/2016/07/25/a-peek-in...
The break-and-follow-the-errors approach is very powerful. It's usually not hard to find the exact break that will show you all the things you need to change, and then just work through them. My record is 5 days without buildable code, working in C++; once I'd worked through all of the errors, the program worked, and without any non-obvious problems.
I miss this a lot when working in a dynamically typed language.
(Thing by Jonathan Blow that touches on this: https://web.archive.org/web/20140929232443/http://lerp.org/n...)
Parsing JSON in Java was one of my worst programming experiences, so I have to agree with you here.
But in Rust, using the `rustc-serialize` library (and Serde, but I haven't tried that yet), parsing and writing JSON is really pretty painless. The really nice part is that you can declare the structure of your JSON data as a completely normal Rust struct (just with a derive annotation that makes it Encodable and/or Decodable), and with a single function call turn an instance of that struct into a JSON string. And in reverse, you can just parse() a string and it will return either an instance of your struct or an informative error if the JSON is malformed or doesn't match your structure. Makes JSON really easy to work with.
In golang recently I had to take some json (that I only knew part of the structure of), and modify just that small subpart of it without touching the rest. It was a really painful thing to develop, and the code ended up very messy.
There were a few golang libs for reading arbitrary json, but none supported writing to it that I could find.
I'm surprised this myth perdures.
Reading arbitrary anything is trivial in a statically typed language: use a hash map.
There. You're merely emulating what a dynamically typed language gives you, of course, but it's trivial. And at least, statically typed languages give you the choice: you can be dynamic or static. You don't have such a choice when you don't have types.
Indeed, and C# isn't even a particularly safe typed language. It still has pervasive null, for instance. When you get into F#/OCaml/Haskell/Rust-type languages, it's a real eye opener.
> Want to read/write pretty-arbitrary JSON? Good luck
Not sure I see the problem. Just deserialize JSON into a JsonValue which provides dictionary semantics like JavaScript.
I'd point out, though, that is still is only part of the picture. You're holding on to a value and you want to know what you can do with it:
widget.<tab>
will show you everything of the form widget.someData
widget.doSomething()
but you're still missing out on other structures like freeFunction(widget)
handler.handleWidget(widget)
hammer + widget
widget[part]
// returns a widget, does this one count?
widgetFactory.buildWidget()
In nearly any language the first two will be common, and where available the others are critical usages, too. I want to be able to tab complete them!It's hard to continue hewing to the tab as activation with these other structures, which may be why IDEs and REPLs don't really try.
I actually dislike tab complete in usual forms most times. Auto import is nice, but i feel that auto complete is a form of searching the code base. And, when I am coding seems a poor tube to be searching for the answer.
When debugging, however, jump to symbol and quickly listing alternative methods helps. And sometimes I am just searching. So, good feature. Just not something i want to rely on.