News for hackers. Stuff that matters.
Re:Learning from Ada by pka on Sunday August 21, 2016 @03:12PM (#12332254)
Static languages, in particular Haskell, PureScript, etc, are unparalleled when it comes to fast prototyping or design space exploration. The compiler can show you whether your idea even makes sense before writing big chunks of business code.

How? By building the structure/hard stuff first and letting the compiler verify it while leaving out the uninteresting parts.

A very simple example. Let's say you have some data model with people, addresses, whatever. You want to see what you need to do to get a list of addresses for some group of people.

You start by defining the structure of your data model (this can depend on DB schema, business requirements, whatever):

    data Person = Person { name :: String, city :: String }

    data Address = Address { city :: String, zipcode :: String, street :: String }
Now, go straight to the hard part. No need to fuck around with DB connections, mock data, whatever:

    getAddresses :: [Person] -> [Address]
    getAddresses = undefined
Leave out the implementation for now. So probably we are gonna need a way to get an Address for a Person. Ok:

    addressForPerson :: Person -> Address
    addressForPerson = undefined
Again, leave out the implementation. Now go back to `getAddresses` and see if we can implement it now:

    getAddresses = map addressForPerson
Cool, the compiler doesn't complain. So far we've verified that if we have a function like `addressForPerson` we can safely assume that our design is going to work.

Now we can go back to `addressForPerson` and implement that. But turns out, we only have a city stored in Person, so the best we can do is get back a list of potential addresses for each person. So we need to adjust that type signature:

    addressForPerson :: Person -> [Address]
    addressForPerson = undefined
Leave out implementation, it's trivial so no need to spend time on it now. But now the compiler complains that `getAddresses` doesn't make sense anymore! We are saying that, having a list of people, we can get a list of addresses, but that's not true anymore. We found out that we can only get a list of potential addresses, so we have to adjust the type signature.

    getAddresses :: [Person] -> [[Address]]
Now every use of `getAddresses` will have to be adjusted to the new requirements. Etc etc.

Granted, this is a very simple example, but I hope it illustrates how static languages facilitate extremely fast prototyping. Sure, a Lisp REPL is very very nice, but in my personal experience it's no match for a type system. We didn't even have to mock data to start experimenting!

(And yes, Haskell, OCaml, PureScript, Elm etc all have a REPL too).

Re:Learning from Ada by nickbauman on Sunday August 21, 2016 @03:51PM (#12332441)
I don't see this example particularly enlightening. I'm not really ever seeing problems where I'm stuffing an integer into where I wanted a string in dynamic languages. Or stuffing a Person into a Gorilla. These problems seem important and they tickle programmers warm fuzzies (unless you're like me and you've been programming static and dynamic for more than 20 years).

The small amount of scientific study around what type checking and static typing gives you is that you avoid around 3% of bugs but even those 3% are easier to fix than the effort in writing them correctly in the first place. While I agree that the subject needs more study but what we know now: the real win would be to have a dev process that lowers criticality of failure (like TDD, CI and other Agile processes) and dispense with static typing for most projects.

Re:Learning from Ada by pka on Sunday August 21, 2016 @04:13PM (#12332527)
While I disagree (static typing is much more than catching type conversion errors), my point was that it makes experimentation and design exploration much easier and faster. Without types, you'd need mock data for even the simplest of programs, and changing mock data structure is much more painful than changing a type somewhere and getting the places (all the places) where your program turns then out to be incoherent. Not to mention that types are much more than Persons or Gorillas, they can be higher order functions too (Person -> Gorilla) that are passed around.
Re:Learning from Ada by nickbauman on Monday August 22, 2016 @08:46AM (#12335910)
Sure. But does your static typing system allow you to skip writing automated tests? Pretty sure everyone writes tests these days unless they're spiking something. If you're writing tests, you will have to come up with the mock data anyway. And the kind of exploration you're doing I tend to do from a test regardless. Once my exploration is done I also have a test that captures my work. You still have to write one. The question of whether to static or dynamic is moot in this case. It no longer matters much.

Maybe I need to spend some time in Haskell and come over to your way of seeing it. But the studies show that constructing the type system for your problem is very difficult and remains error prone. People routinely make mistakes with types systems of all kinds. I've found I spend more time making the compiler happy that it does making me happy.

Re:Learning from Ada by pka on Monday August 22, 2016 @01:47PM (#12338007)
The sibling comment points out property based testing (see QuickCheck), which is a good idea in any language. And yes - you don't need to write that many unit tests (but still some) when using a type system, because you can encode a lot of invariants in the types.

But still, my point stands - writing tests during early experimentation is a lot harder than just altering some type and letting the compiler infere what needs to be done or what you broke.

I've spent some time writing Clojure and still use dynamic languages a lot for smaller tasks, so I know both sides. This doesn't mean much, but I'd really encourage you to try some ML language and spend some time with a nice type system. Haskell is probably too big a time investment, so maybe try Elm [0] - it's very beginner friendly and a lot of the people using it come from JavaScript.

[0] http://elm-lang.org

Re:Learning from Ada by dllthomas on Monday August 22, 2016 @10:51AM (#12336697)
> If you're writing tests, you will have to come up with the mock data anyway.

With property based testing, your system will come up with mock data for you.

Re:Learning from Ada by lmm on Monday August 22, 2016 @06:33AM (#12335308)
I don't think I've ever seen a bug that shouldn't've been a type error. When I encounter a bug that wasn't a type error, it's usually a sign that my model is subtly incorrect. (E.g. I recently found a bug where one codepath wasn't filtering a tree that it should have been. This clued me in that filtered and unfiltered trees need to be different types, and actually making that change simplified a lot of code, since it forced me to figure out the correct point at which to do the filtering so that I could do it in one place)
Re:Learning from Ada by nickbauman on Monday August 22, 2016 @08:48AM (#12335923)
The data shows that you see 3% bugs that could have been fixed with static typing. 97% are from other things. Mind you this area has not been studied much and needs more research, but that's what we know now.
Re:Learning from Ada by lmm on Monday August 22, 2016 @10:37AM (#12336595)
I simply don't believe that when it's so utterly contrary to my experience. How did they define a bug as not being fixable with static typing?
Re:Learning from Ada by nickbauman on Monday August 22, 2016 @03:08PM (#12338599)
This seems to be a very common situation with programmers. When presented with evidence to the contrary, we all of us tend to respond in this sort of "it's wrong because it conflicts with my experience". You realize this is essentially the classic flat earther denial. I'm not picking on you: I've done it too. It seems to be an industry hazard.
Re:Learning from Ada by lmm on Monday August 22, 2016 @04:36PM (#12339230)
I think there's a level at which you have to be willing to believe your own eyes over the consensus. And there are levels of how established the consensus is. Perhaps it's particularly common for programmers because the industry moves so quickly, so all of us can quote previous established wisdom that turned out to be utterly wrong.
Re:Learning from Ada by tome on Sunday August 21, 2016 @05:56PM (#12332898)
> I'm not really ever seeing problems where I'm stuffing an integer into where I wanted a string in dynamic languages

Wow, then you are extremely lucky because bugs like that would occasionally consume a whole day of senior developer time at my previous Python job.

Re:Learning from Ada by jlarocco on Sunday August 21, 2016 @06:59PM (#12333121)
I don't think the OP is particularly lucky. I never see errors like that, either. Just because the language doesn't enforce strong types doesn't mean variables don't conceptually have a type.

It seems like a symptom of other problems more than anything, and using a staticly typed language will just make those problems manifest in a different way.

Re:Learning from Ada by jjnoakes on Sunday August 21, 2016 @10:43PM (#12333772)
> I'm not really ever seeing problems where I'm stuffing an integer into where I wanted a string in dynamic languages.

If you are writing all of your code from scratch, then it doesn't happen too often. You usually have enough of the current context in your head to avoid most type level mistakes.

The real strengths come from two situations.

The first is refactoring. There is just no contest here. Having every single place where your change affects the test of the program identified automatically is extremely useful.

The second is returning to old code and either trying to understand it or make small changes to it.

Re:Learning from Ada by cageface on Sunday August 21, 2016 @06:59PM (#12333123)
I see simple type errors like this ALL THE TIME in legacy code bases I get pulled in to salvage. Even in a dynamic language with a fair amount of static checking like Objective C I find people putting the wrong kinds of objects into arrays etc. So when people tell me now that they just don't make type errors in their code I don't believe them. People make mistakes, particularly when tired and under pressure.
Re:Learning from Ada by jlarocco on Sunday August 21, 2016 @07:16PM (#12333182)
If people are writing code, thinking, "I have no idea what else is stored in this container, so I'll just add an object of type Foo because I can!" then the project has bigger problems than static or dynamic typing.
Re:Learning from Ada by cageface on Sunday August 21, 2016 @07:58PM (#12333328)
The reality is that a lot of code is written by average programmers trying to get a lot done in a hurry. A static check is the difference between a subtle bug making it into production and a helpful, specific error at compile time. Most projects do have bigger problems than static or dynamic typing but static typing is even more helpful there than it is on projects staffed only by top-flight coders with plenty of time to do things right.
Re:Learning from Ada by nickbauman on Monday August 22, 2016 @03:16PM (#12338662)
Except that the evidence points to the contrary.

http://games.greggman.com/game/dynamic-typing-static-typing/

Re:Learning from Ada by cageface on Monday August 22, 2016 @10:49PM (#12341041)
In the three years I've been doing consulting work I've had to clean up a lot of legacy code. Believe me, people do make these mistakes in real-world code all the time.
Re:Learning from Ada by dalke on Sunday August 21, 2016 @03:54PM (#12332454)
That may be useful if you have a lot of data types and transformations between types. I don't. I do a lot of work with (molecular) graphs, which have two data types: nodes and edges. If you code up a depth-first search which uses an edge instead of a node, or vice-versa, then even the most trivial of tests will find it in a dynamic language. A static type check adds little.

For an example of a project which I worked on for months, given N molecular graphs, find the maximum common subgraph which is in at least M of them. I did all that in Python. I don't think static types would have made a difference.

Matrix algorithms are an even more clear example. They work with a single numeric type, in a 1D vector or 2D array. It's hard to confuse the types. On the other hand, it's easy to get the algorithms wrong, and choosing the right algorithm can require a lot of design space exploration.

Just think of the number of clustering algorithms available which you might want to try out through prototyping before spending the time to scale things up. How does static typing really help there?

Re:Learning from Ada by pka on Sunday August 21, 2016 @04:05PM (#12332504)
I'm not an expert on type systems, but I don't think it would add that much in your case. You might get more out of it if you wanted by using phantom types, which is just a way to differentiate among structurally identical types, like this:

    reduceGraph :: Graph a -> Graph Reduced
Now you can't pass unreduced graphs to functions where you expect some sort of an already reduced graph, and you could encode as much invariants as you wanted in your types, but yea, in cases where there are homogenous types with little variance (machine learning, statistics, your use case) static typing won't probably bring that much (unless you go nuts with dependant typing I guess).
Re:Learning from Ada by chriswarbo on Monday August 22, 2016 @08:25AM (#12335786)
> For an example of a project which I worked on for months, given N molecular graphs, find the maximum common subgraph which is in at least M of them. I did all that in Python. I don't think static types would have made a difference.

Even with no knowledge of the problem space, I can think of a whole bunch of static types off the top of my head.

You've already given your input type: N molecular graphs, and a number M.

You've also given your output type: the maximum common subgraph which is in at least M of the given N molecular graphs.

Straight away I can spot a problem with this, since there are no programs of this type. In particular, there is no valid output when M > N. Hence I would refine the input type to ensure that M <= N.

Since it appears in your input type, it would be useful to have a type for "molecular graph". Likewise, based on your output type, it would probably be useful to have types for "subgraph of g", "common subgraph of gs", "maximum common subgraph of gs" and "subset of gs".

Naively, I'd give your program a type something like this:

    (m : Nat, gs : Set MolecularGraph, m <= size gs) -> (hs : SubSet gs, size hs >= m, MaximumCommonSubgraph hs)
Programming with type constraints like these would be very different to using Python, where you'd have no guarantee that a value is even a graph, let alone a maximum common subgraph of the input set.

Whether it would be more or less appropriate for your situation is, of course, an entirely separate issue ;)

Re:Learning from Ada by GFK_of_xmaspast on Monday August 22, 2016 @02:15AM (#12334415)
Matrices and vectors can be thought of as typed by dimension[0], and it's easy to get that wrong. (I want to say c++'s eigen can do some compile-time checks if it has the dimensions available).

[0] In some languages, like older versions of pascal, arrays of different lengths were different types, which was a horror for writing generic code.

Re:Learning from Ada by dalke on Monday August 22, 2016 @05:25AM (#12335072)
Yes, C++ is a particularly easy language to get that wrong, where float * can be a pointer to scalar, vector or 2d array. Other more strongly typed languages, like Python, will make those errors easy to spot with any sort of test case.

Beyond strong typing, do you get the sense that static typing significantly improve prototyping ability for matrix analysis code?

Re:Learning from Ada by jakub_h on Sunday August 21, 2016 @03:30PM (#12332342)
> "Static languages, in particular Haskell, PureScript, etc, are unparalleled when it comes to fast prototyping or design space exploration. The compiler can show you whether your idea even makes sense before writing big chunks of business code. How? By building the structure/hard stuff first and letting the compiler verify it while leaving out the uninteresting parts."

Why shouldn't I be able to do this on a meta-level? There does not appear to be any restriction on, for example, Lisp code regarding the use of problem solvers and provers. The same should go for other metalanguages (perhaps even better, without forcing me into specific semantics).

(Likewise, generative solutions might reduce this problem simply by performing a sequence of correctness-preserving transformations from a higher-level specification. That would shift the burden to checking the specification, which would require a custom checker anyway. Haskell's capability to check or automatically derive stuff seemed limited the last time I looked at it.)

Re:Learning from Ada by pka on Sunday August 21, 2016 @03:53PM (#12332449)
Agreed. My comment wasn't aimed that much at Haskell in particular (even if it's my language of choice), but rather the idea of using structure/types for fast experimentation, rather than just a REPL.

Also, using a type checker to wholly check your Lisp code basically makes it a static language, the difference then is pretty much philosophical I'd say.

Re:Learning from Ada by jakub_h on Sunday August 21, 2016 @04:21PM (#12332564)
Recently I've found myself having severe problems with seeing clear borders between many concepts. IF expressions, type-based dispatching, matching, generic functions, predicate dispatching...a lot of it basically boils down to the same thing, only expressed in different ways. The problem is that the same thing goes for type systems in my head. Types define sets of values, but if your language hardcodes what kinds of sets of values you can express, chances are that you'll have to express whatever residual distinction between values there is (that you can't encode into the value type) into one of the above-mentioned decision-making processes, and at that point, they go sort of out of sight of the typing/proving machinery.

I'd rather avoid someone forcing me into using a hardcoded type system, among other reasons because I'm contemplating a (perhaps novel?) search-based program/expression-manipulating system that wouldn't play well with any fixed typing system. For one thing, the types of expression results (being return values of operators applied to input expressions or programs) can easily be emergent (is the resulting expression linear? Is it an odd function? Is it analytically integrable? Etc.) and not easily arrived at in something ML-like. If I had to use something like Haskell, I'd still be rolling out my own type system/prover on top of the existing language implementation anyway. However, types in general (as something you can compute with in the meta-program) would be absolutely mandatory for a system like this (for example, some algorithms or operations can have more desirable refinements that work only on subsets of values - consider determinants of triangular matrices, with triangular matrices being an emergent type - and I absolutely want to find out if such a thing is available for the specific problem being transformed), just not types that any specific existing language offers, to my knowledge.

So it's not that I dismiss the notion of proving things about programs - that is a vital thing to have - but the ability to impose restrictions on a program from the outside seems much more flexible to me than going for some fixed type discipline that could force me to straddle inconvenient fixed walls in the structure of a complicated program (such as this transformation system that would search the tree of possible transformations for a result with some desirable properties, with the transformations concisely expressed in some very-high-level DSL source code).

Re:Learning from Ada by PeCaN on Sunday August 21, 2016 @03:30PM (#12332337)
While I agree, getting used to that mode of thought takes time. IME you have to write a lot of Haskell (or OCaml) before it becomes a very fast prototyping language. Of course, it's 100% worth the investment, and I prefer prototyping in OCaml or Mercury to basically anything else, but it certainly wasn't like that from the first time I picked up OCaml.
Re:Learning from Ada by agumonkey on Sunday August 21, 2016 @03:53PM (#12332451)
I used to do this with .. Java 5. Eclipse live typechecking allowed me to design in Interfaces until I had what I called the parametricity right. The thing is, I stumbled upon this out of paranoia (I hate being wrong down the road). Never was I invited to think or design this way. Maybe with embedded contracts in comments; but not in a real time, live repl prototyping way.
Re:Learning from Ada by ufo on Sunday August 21, 2016 @06:00PM (#12332910)
I think that you are comparing static vs dynamic typing but the grandparent comment is more about strong vs weak typing.

If instead of having separate Person and Address types you just use strings for everything (even "lists" can be strings - just keep the data serialized into HTML, or XML) then you can your program is likely to "do something" if you just compose a bunch of functions together. Sure, your code might have a bunch of logic bugs (like HTML injection / XSS) but at least it does something!

Static typing is great once you leave this "stringly typed" world and start separating your values into strongly-typed types. When you get a type error from passing a value to the function then its nice if you can be warned about this at compilation time.

Re:Learning from Ada by dllthomas on Monday August 22, 2016 @10:41AM (#12336624)
Right, exactly. I want guidance on whether code (new code or a modification to existing code) can make sense before I've written all of it. With modern Haskell I can start to ask those questions before I've written all of the term-level code and before I've written out all of the relevant types. I can start working with whichever is more convenient at the time, and feel ahead to where things might break down before I get there.