5 ms·
> The type system gets in your way I think that anyone saying this suffers from a fundamental misunderstanding of how to use a type system like Haskell's. I en
by merijnv 16y ago
> The type system gets in your way
I think that anyone saying this suffers from a fundamental misunderstanding of how to use a type system like Haskell's. I encounter it often with people coming from C/Python who start writing typeclasses, data types etc. and only afterwards start adding functions and then struggle trying to get the right types for their functions.
Fundamentally, programming is a search problem. We are searching for a sufficiently good solution (program) in the space of all possible solutions (all possible compiler inputs). I am of the opinion that the type system can significantly help in searching for such a "good solution". In the words of Conor McBride: "Sometimes it's easier to search for good programs in the space of well typed programs, rather than in the space of ascii turds."
Personally, I start by writing down type names (just names, no implementation or anything) and function names of what I want to do with said types and most importantly what the type of said functions should be (inventing new type names and functions as I discover I need them).
Say, for example, a game would start with GameWorld type and an update function of type "GameWorld -> some events -> GameWorld".
This way I start fleshing out the skeleton of WHAT I want to do, without any regards yet as to HOW I plan on accomplishing it. Once I think my skeleton is somewhat complete I define all my functions to be "undefined" (actually, I do this as I write them down, of course). undefined has all possible types in Haskell, so it always typechecks, but throws an exception when evaluated.
Then I start replacing the undefined's with actual code that matches the signature and start implementing typeclasses/datatypes as required while writing the functions. I will occasionally compile the code to see if everything so far actually type checks.
- dododo 16y agothat's how you write haskell the first time. that's fine. easy, nice, simple. you've written a lot of code; it compiles, it works, no problem. then you realise that you want to change a data structure or a function needs some information that you want to weave through it or you used the wrong abstraction. then it's a pain in the ass.
- ezyang 16y agoThere are two points to be made here: 1. With a static type system, you can make the key change at the data type declaration level, and then chase down the compiler type errors to figure out all the place you need to change it. You need to do extra legwork in dynamically typed languages to get the compiler to tell you this sort of info: how many times have you changed some variable name in Python and forgotten to update the reference somewhere obscure? 2. Yes, if you have some pure, functional code, and then you decide you want to have a feature that uses state, fixing things up can be a bit tedious. I think there's an interesting space here for refactoring tools that help take out the tedium. I will also note that this restriction is a good thing sometimes: you know how you add a global here and a global there and suddenly you have an unmaintainable blob of global state? Haskell asks you to "slow down" and re-think about what you actually want to do. It encourages you to use the minimum amount of language features to get the job done. This leads to more maintainable code.
- tel 16y agoI think there's a space for partial compiles that Haskell doesn't take advantage of well. Correct me if I'm wrong. Assume that you've got a simple dependency tree on defined names in a program and you want to refactor the type of one of the leafs. The type system is great here in that it'll track those mismatches straight up to the root. The trouble appears if you want only an experimental type change at the leaf. It would be excellent to get successful partial compiles threaded up the tree. In other words, it should be possible to make a cut in the type dependence tree in order to test a functional subset of your program under more rapid experimental changes. Once you've iterated enough to decide on a new formulation of that branch of the dependency tree, you can continue attempting typechecking up to the root node.
- ezyang 16y agoOne thing I've seen done here is copy paste the method into a new name (usually with a leading underscore) and then make the appropriate changes. It's not hooked up to the rest of the system (but that wouldn't have worked anyway), and you can still compile it and poke at it.