News for hackers. Stuff that matters.
Re:Making the move from Scala to Go by pka on Wednesday January 25, 2017 @10:01AM (#13481877)
> If I am facing a problem that I have never faced before, I like to start off without any types in my code, and then, as I understand the problem more, I like to add in more contract-enforcement.

This seems to be a widespread sentiment.

In practice, I've found that prototyping anything remotely complex without types is so painful that I'd rather settle for an inferior design than trying to come up with the best possible abstraction.

In Haskell, I come up with a coherent skeleton without having to implement mundane details, hit a wall in the design space because of some case I didn't think of, come up with a better idea and then go back to the code and refactor with confidence. The type system always guarantees that my prototype is coherent as a whole. And I can do that dozens of times.

In Clojure, even with spec, I'd have to implement all my functions fully before being even able to test the design as a whole (sure, testing individual functions works fine in the REPL.) And after hitting a wall, having to reimplement everything every time is just too much work.

Re:Making the move from Scala to Go by abc_lisper on Wednesday January 25, 2017 @05:48PM (#13487012)
> In practice, I've found that prototyping anything remotely complex without types is so painful

Just to offer a counter-point, I have a Clojure project here with 3k LOC, without using spec/schema. All I have is 700 LOC tests. The tests enforce semantic meaning, along with (some) contracts. I miss types from time to time, but it is no where near as bad as you mention. Against me is the fact is that my app is mostly self-contained, and written all by me. I am fairly certain the project would be atleast 30k LOC if I wrote it in Java.

I think value of types only comes into being when there are many people working on a single code base. Repl/tests/integration tests will take one a long way before it reaches its limits.

I don't buy the argument that types are useful for large codebases, because 1. Types don't enforce meaning. Meaning is far complex than type. Haskell style types only work as far as they enforce meaning. 2. Types have limits too. More accurately, humans have limits. Working with large codebases where there are 1000s of types is hard; which it shouldn't be because that was type systems selling point all along.

Cleaner abstractions of code, separated by strongly enforced protocols(types) is the way to go, I think.

Re:Making the move from Scala to Go by pka on Thursday January 26, 2017 @08:24AM (#13491070)
Have you tried what I'm talking about, i.e. prototyping something by specifying the types and iterating on the core design first before implementing large chunks of the required functionality?

It's probably hard to see the benefits without having tried it first.

This [0] is one of the bigger Clojure projects I've done (around 2.5kloc), also without spec/schema, and I really didn't dare refactor much, even when having a clear understanding of how things could be done better. It was just too much work.

With types I'd go through 10 design iterations before settling for something I'm satisfied it, and even halfway through the project changing things radically isn't a problem (I've worked on a 50+ kloc Haskell backend service, and changing core data structures used pervasively throughout the codebase was a 10min job, literally.)

[0] https://github.com/pkamenarsky/tellme

Re:Making the move from Scala to Go by abc_lisper on Thursday January 26, 2017 @01:30PM (#13493885)
I see what you are trying to say. You are saying Clojure is not the right tool to do top-down design. I agree with it. It is however a very good tool to do bottom up design. See https://www.youtube.com/watch?v=Tb823aqgX_0
Re:Making the move from Scala to Go by pka on Saturday January 28, 2017 @12:29PM (#13507551)
I don't think this is top-dow design really, just an overengineered OOP monstrosity.

Top-down doesn't mean that you have to model the universe first before getting to model your problem :)

Re:Making the move from Scala to Go by kazinator on Wednesday January 25, 2017 @08:28PM (#13487962)
Today I caught a bug in a macro-expanding code walker, where it was expanding the wrong form. The syntax being walked is (foo-special-operator x y z . rest). x and z are ordinary forms that need to be expanded; y is a destructuring pattern (irrelevant here). The walker was expanding z in the place of x: that is to say, expanding z twice, and using that as the expansion of both x and of z. That's simply due to a typo referring to the wrong variable.

The type system would be of donkey all use here, because everything has the correct type, with or without the mistake. The code referred to the wrong thing of exactly the right type.

A lot of code works with numerous variables or object elements that are all of the same type, and can be mixed up without a diagnostic.

Re:Making the move from Scala to Go by neilparikh on Thursday January 26, 2017 @11:22AM (#13492637)
So the issue you faced was that you used z twice, and x not at all? In that case, linear types (http://edsko.net/2017/01/08/linearity-in-haskell/) may have caught that.
Re:Making the move from Scala to Go by kazinator on Thursday January 26, 2017 @12:57PM (#13493551)
x not being used at all can be caught by a simple unused variable warning.

In this situation, the variable's value was used somewhere, so that didn't go off.

Re:Making the move from Scala to Go by codygman on Friday January 27, 2017 @08:44AM (#13499719)
If you used newtypes for z and x it would have been caught.
Re:Making the move from Scala to Go by codygman on Thursday January 26, 2017 @11:17AM (#13492587)
> 1. Types don't enforce meaning

Why do you think this?

> Haskell style types only work as far as they enforce meaning.

Doesn't this contradict your statement above or are you saying Haskell style types never work because they don't enforce meaning?

    getUser :: IO (Maybe User)
The above function enforces that getting a user can fail and you must contact the outside world to get a user.
Re:Making the move from Scala to Go by abc_lisper on Thursday January 26, 2017 @01:24PM (#13493821)
Alright.. What I meant to say was,

Types don't enforce all meaning ie., the enforcement of contracts through types go only as far as they mean something to the problem you are applying it to. It does not cover all the complexities of the problem, or the way the code is changed in the future.

EDIT: This is also why it is easy (and nice) to implement parsers in strictly typed functional languages, because parsers are well studied theoretically. The problems in the real world are not studied well enough for contracts enforced via types to work completely.

Re:Making the move from Scala to Go by dllthomas on Monday January 30, 2017 @12:25AM (#13518903)
Types don't enforce meaning, types are a tool I use to enforce consistency of meaning along certain important dimensions. I actually find that even more important when the situation is messy, because I'm likely to initially mischaracterize some aspects of it initially and when I go to change things it's very useful to be told what's now inconsistent.
Re:Making the move from Scala to Go by abc_lisper on Wednesday February 01, 2017 @08:39PM (#13547301)
I get what you are saying. What I'm trying to say is typing takes too much from me, in terms of complexity over-head, that I'm better without. I found this is true in practice now. As I said before, I write tests to do what you say types do - for me, that is enforcing meaning. Types do allow for easy refactoring, and I think that is weakness for untyped languages.
Re:Making the move from Scala to Go by dllthomas on Monday January 30, 2017 @12:28AM (#13518915)
> The above function enforces that getting a user can fail and you must contact the outside world to get a user.

That seems sort of backwards. It enforces that the caller be able to handle failure (and similar for IO). It may well be that "getting a user" doesn't do either (e.g. `pure (pure defaultUser)`)

Re:Making the move from Scala to Go by kazinator on Wednesday January 25, 2017 @06:24PM (#13487239)
Cannot proto-type without type; how ironically circular. :)
Re:Making the move from Scala to Go by Karrot_Kream on Wednesday January 25, 2017 @01:05PM (#13484012)
> In Haskell, I come up with a coherent skeleton without having to implement mundane details, hit a wall in the design space because of some case I didn't think of, come up with a better idea and then go back to the code and refactor with confidence. The type system always guarantees that my prototype is coherent as a whole. And I can do that dozens of times.

Could you go into a bit of detail about this approach? Do you mock functions out and just specify type definitions, and fill in functionality as you go?

Re:Making the move from Scala to Go by tel on Wednesday January 25, 2017 @02:13PM (#13484865)
I'm about 5% into a side project implementing Erlang-like actors in Haskell and it provides an example of this workflow.

https://github.com/tel/hotep

Note that src/Hotep.hs exports a bunch of undefined functions. I've been able to verify that these types all make sense even without implementing a thing. As time goes on I may learn that the implementation drives the types somewhere else and then the compiler will make refactoring easy.

However, already I've gone through about 5 iterations of this design which drove me to debug some structural questions about the whole affair and also dive deep into Erlang documentation to determine how they solved problems. These explorations and their results are encoded into the types.

At this point I'm beginning to consider implementation and I can keep filling out just partial implementations against these types. I'll probably make 2 or 3 toy implementations to test out the ideas again with more strength before moving on the final ones. The whole time the types will be guiding that development and helping it move quickly.

Key to this whole affair is the need to describe types utterly before a completely successful library is made... and also the ability to defer the burden of providing type-checking code for as long as desirable. Haskell supports this wonderfully—even more wonderfully with things like Typed Holes and Deferred Type Errors which enable a really great interactive experience I haven't yet needed to employ.