Hacker News new | ask | show | jobs
by kemiller 3670 days ago
This has always been my experience with TDD, at least if you always leave yourself a failing test when you go. A lot more labor-intensive, since you are essentially writing the "compiler" as you go, but a similar effect.
1 comments

I think "gradual typing" like Flow might be a nice middle-ground.

You're still free to quickly sketch out ideas without worrying about type checking, but you can solidify things by just adding /* @flow */ to the top of a file and fixing the errors.

I haven't actually tried Elm though, so maybe a "middle-ground" isn't necessary.

This will sound counterintuitive to dynamic language people, but prototyping is actually much easier with types. I often find myself rewriting 50 to 75% of the codebase in the early stages of prototyping, and without types I'd be much less willing to do that, settling for a less than ideal design. This design will then be the base of a potentially large project, which amplifies early mistakes even more.
For me types make prototyping WAY faster, especially if "throw" or some other keyword satisfies the return type of a function. I can write out the function signatures and data types I need and get things to line up with the help of a tool that tells me exactly what I need to do (the compiler). This lets me quickly find a solution without having to actually implement any of it, and then I have a skeleton ready to fill in with details.
> For me types make prototyping WAY faster

Agreed. I can prototype an idea almost entirely in types, and if the types make sense, then the implementation often follows naturally.

I think it's a matter of personality. Dynamic language people (to the extent there are such people... most of us use both) are generally not scared at all to change things. We're just more comfortable with the intervening chaos. For instance I might want to change some underlying model code without worrying that it breaks every single page in the site except the one I'm using to work on it, or the individual test. Sometimes I want to work on the heart of the matter first and clean up details later instead of working on the compiler's set of priorities.
Sure. In Haskell for example you can turn type errors into warnings with a compiler flag. If you then encounter a type error at runtime the program will crash, much like in a dynamic language. And if it turns out the change was a dead end, just git checkout master and you're done.

However, if not, you'll still have a list of compiler errors you have to fix to make the rest of the program work. This alone lets me refactor with confidence, much more so than every dynamic language I've ever used.

I find it's conductive to rewriting that I can iterate with only one or two codepaths of the remodeled core working before settling on something and fixing the rest of the codebase to match.

(nit: dynamically typed != no types)

This is the promise of type inference. Languages like Elm that do a good job inferring types are able to play out potentially invalid implications of how your code uses values without requiring you to separately specify all your types in great detail. You'll spend some time just addressing edge cases as "safe to ignore" but it's a lot lower than explicit typing while delivering many of its benefits.
Type inference usually makes it harder for a compiler to provide good error messages though. Elm has gone the extra mile on this.
Flow actually does type inference as well.
>You're still free to quickly sketch out ideas without worrying about type checking,

That's a terrible work flow though. Quickly sketching out ideas is so much easier with types. So much so that you do that with just types, you don't even need to write the code. Then once you've worked your intuition into something that could actually work, you fill in the code.