Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Hi Haoyi,

Great write-up regarding good software engineering practices in general, and ones applicable to Scala development specifically.

Two things I submit to be clarified/added to Strategic Scala Style are:

1 - Explicitly declare the return type of all public methods.

Doing so ensures that type inference does not propagate implementation types to the caller (often eliminating compilation errors) and, in my experience, greatly assists in reduction of errors when working with higher-kinded types. IMHO, it also assists in understanding a system due to an explicit contract being expressed.

2 - As "Joe Clueless" wrote in the comments, why isn't Either presented as a viable alternative to Option?

Again, IMHO, while Option is an exceptionally useful type, when indicating errors at the "public API level" it proves to be insufficient. The distjoint union type Either allows a collaboration to be both explicit that a failure may occur as well as indicate what the failure was (if any).

Hopefully these comments help and, again, kudos to a great write-up.



I really dislike Either personally. I much prefer Scalatic's "Either with attitude" Or. It's also the only natural use of infix type notation I'm familiar with so that's kind of neat.

I frequently see Either abused to represent the lack of Union Types. But then I also see it used for errors. And it's not really a perfect fit for either (no pun intended).

http://www.scalactic.org

Every (a non-Empty List) is also nice. I like Scalactic. It adds just a couple of small useful things. Very lightweight.

Option (Some or None), Or (Good or Bad) and Try (Success or Failure[T <: Throwable]) cover the bases for me.

Just in case others were unaware of scalactic. (sister library to scalatest, so if you're already using scalatest, you're already using scalactic under the covers I believe)


I don't type-ascribe all public methods. Maybe I should but I'm lazy. I don't even type-ascribe my implicit vals or defs... i probably should but i don't

As to Either, I didn't mention it because I don't use it much, and don't see many other people using it either. Sure you can use it and it'll work, but it's not the style I use or the style I see most open source libraries using.

As for Scalaz, yeah \/ is great, or cats' Xor. But this doc is all about Vanilla Scala: for every library out there, they will have their own best practices, that are totally uninteresting to people not using hat library. If someone wants to write similar documents for Scalaz, Cats, Akka, Play, Finagle, Play, they would each probably be just as long as this one, and probably just as valuable!


I get why you didn't go over Scalaz or any other of the awesome libraries out there. Which is why I didn't mention \/ and friends except as part of a tangential sub-thread :-).

IMHO your scope is spot on for Vanilla Scala and will help many.

Regarding the similar documents shout-out, here's one for Scalaz which I found very well written:

http://eed3si9n.com/learning-scalaz/index.html

Perhaps others know of write-ups similar to yours and this one for some of the others you mentioned. Could be interesting to compile them into a "see also" kind of thing.


I rarely use Either. If logic is internal to my program, often the cause of the deviation from the happy path is implicit, and I can just use Option. But if I do want to propagate the cause, usually I prefer to model directly within my types as a sealed trait, because then I can easily model multiple causes. Of course, this comes up short if I want to reuse the same cause for multiple distinct outcomes. Either could be nice for this, but it comes up short for multiple causes, because you have to resort to nested Either. Consequently, I very rarely use Either.

What I'd really like is a simple type union syntax, so I could union the happy path result with any number of failure modes, where there isn't necessarily a common supertype for anything.


Using `Either` instead of `Option` would violate "Use the least complex approach that solves your problem".

If all you need is represent an optional value, why use `Either`, which needs a second type parameter representing the failure? If you don't care about what caused the failure (because there was no failure, an absence of value is a valid result), no need to represent it.

Besides, these two classes still represent fundamentally different concepts and they are not interchangeable at all.


My question was implicitly framed in the context of the "Error Handling" section of the paper. I agree that Option is an ideal choice for 0/1 cardinality situations and use if frequently for that purpose.

Sorry for any confusion I may have introduced when re-stating the question originally posed by "Joe Clueless" in the comments section.


> Explicitly declare the return type of all public methods.

+1

This is very helpful for quickly scanning code.

>Again, IMHO, while Option is an exceptionally useful type, when indicating errors at the "public API level" it proves to be insufficient.

Agreed. Either, or even better, \/ (Scalaz either/disjunction) is much easier to work with and more expressive.


> >Again, IMHO, while Option is an exceptionally useful type, when indicating errors at the "public API level" it proves to be insufficient.

> Agreed. Either, or even better, \/ (Scalaz either/disjunction) is much easier to work with and more expressive.

I, too, recommend using the Scalaz \/[0] Either type as it is "right leaning" and lends itself quite nicely to processing due to it being both a model of Bifunctor[1] (making handling either a success or failure situation quite clean) and contributing nicely to defining an EitherT monad transformer capable of abstracting both latency (the Future part) and errors (the \/ part).

For example, I often have the following defined in projects:

  type FutureEither[+T] = EitherT[Future, DomainError, T]
Where "DomainError" is a top-level type representing an error encountered during processing.

All this adds up to being able to work with "happy path" logic, deferring error handling until it can be handled/reported and network request/response latency absorbed until it becomes an error (addressed as any other error would be).

0 - https://github.com/scalaz/scalaz/blob/series/7.3.x/core/src/...

1 - https://github.com/scalaz/scalaz/blob/series/7.3.x/core/src/...




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: