News for hackers. Stuff that matters.
Re:A Year in Clojure by ff_ on Sunday October 07, 2018 @10:25AM (#18160675)
This, so much. After some 3y spend doing Clojure, having a Haskell codebase is a totally new level of stress reduction.

All of a sudden you can stop worrying about "what am I getting in this function" and actually concentrate on the business logic.

(No, tests can't cover everything. Yes, we still have to write tests, but now they are all useful. Yes, the thing that types hinder you from doing changes is a lie, it's quite the opposite, much easier to change things since the compiler covers your ass)

Re:A Year in Clojure by yawn on Sunday October 07, 2018 @10:57AM (#18160858)
> All of a sudden you can stop worrying about "what am I getting in this function"

I think what you're saying is something like "this function guarantees that the thing I get here conforms to this shape" and I do like that. Something I don't see a lot of people express is just the simple "I know what the shape of this thing is by looking at the signature". Whenever I find myself reading other people's Clojure code, it feels like I end up spending more time figuring out what is being passed into a function. I have to look around to find the origin of a value. I wanted to tear my hair out the first time I tried reading Ring's source code to figure out how it worked. With F#'s Suave, I can tell what each function gets and what can be done to it--it's self documenting. In Clojure, as the original author you can play around in the repl full steam ahead because the types are in your head at the moment. Those who come after you have to assemble the puzzle with less information.

Re:A Year in Clojure by gered on Sunday October 07, 2018 @11:11AM (#18160931)
I think you've really summed it up well here. I have exactly the same concerns when looking at large Clojure codebases. It gets incredibly tiresome having to do this and even at the end of it, I'm still left with that nagging doubt "did I miss some detail somewhere?". Hopefully there's a unit test or three to help put my mind at ease but, at least at my work, that's really quite the pipe dream.
Re:A Year in Clojure by zmmmmm on Sunday October 07, 2018 @08:51PM (#18163455)
Couldn't agree more, but what I don't understand is that Python is plagued by this problem and nobody seems to have any issue with it. Trying to understand large Python codebases drives me crazy.
Re:A Year in Clojure by camgunz on Monday October 08, 2018 @12:33AM (#18164158)
I think a lot of Python engineers don't yet realize they have this problem. It's a classic issue in predominantly OO languages because you often get so caught up in defining objects you lose sight of control flow and data flow. Toss multiple inheritance and metaprogramming on top and it's basically hopeless.
Re:A Year in Clojure by StreamBright on Sunday October 07, 2018 @11:34AM (#18161054)
I usually put these into the description so the next engineer has a better time tracking what is what.
Re:A Year in Clojure by StreamBright on Sunday October 07, 2018 @11:33AM (#18161041)
It is funny, I usually know what I am getting in this function in my Clojure code. OCaml (or Haskell) gives entirely different comfort to me, it tells me what makes sense as input and output type for my function. The downside is that I need to define different functions for different types.

utop # let avg a b = (a +. b) /. 2.0;;

val avg : float -> float -> float = <fun>

utop # avg 2 4;;

Error: This expression has type int but an expression was expected of type float

utop # avg 2.0 4.0;;

- : float = 3.

user=> (defn avg [a b] (/ (+ a b) 2))

#'user/avg

user=> (avg 2 4) 3

user=> (avg 2.0 4.0) 3.0

I think it is a matter of taste which one you prefer. With property based testing and specs you can go very far in term of pre-runtime checks and there was some data about the number of bugs per line published by Github where it turned out that Clojure did not do too bad in comparison to statically typed languages.

Re:A Year in Clojure by ff_ on Sunday October 07, 2018 @11:43AM (#18161105)
I don't know about OCaml, but Haskell has typeclasses (which look like "polymorphic interfaces"?) so you can define functions generically and put a typeclass constraint to make them work on a certain class of types. E.g. your avg function would be of type:

avg :: Num a => a -> a -> a

Where your "a" is a number (that is, a type that implements the Num typeclass)

This would work out of the box on floats, ints, etc

Re:A Year in Clojure by StreamBright on Sunday October 07, 2018 @01:03PM (#18161552)
The signature is not really helpful. Can you show me how do you implement the actual function?

If you have operators that are typed like + and +. how can you make this work with Num?

Re:A Year in Clojure by ff_ on Sunday October 07, 2018 @01:16PM (#18161603)
Also the + operator is typed with Num; from a Haskell repl:

  λ> :t (+)
  (+) :: Num a => a -> a -> a
In case of average we need /:

  λ> :t (/)
  (/) :: Fractional a => a -> a -> a
So our type is going to be constrained by Fractional instead of Num). So an implementation could be (from here [1]):

  import Data.List
  average :: (Real a, Fractional b) => [a] -> b
  average xs = realToFrac (sum xs) / genericLength xs
[1]: https://stackoverflow.com/questions/2376981