Hacker News new | ask | show | jobs
by zylepe 2261 days ago
I’ve been using Scala as my main programming language for over 5 years. I’ve found my “plateau of enlightenment” by sticking to a subset of the features: case classes, for comprehensions, futures, immutability, pattern matching. By avoiding things like implicit, overly complex types, defining/overloading operators, and being dogmatic about FP/OOP I find the language stays out of your way and lets you write code that reads like what you’re trying to do.
3 comments

I've done the same by being lazy, mainly treating it as a better Java and only learning new stuff when necessary or obviously beneficial. I want to write good programs, not fancy code.

My advice:

- Options, immutability, pattern matching, and list comprehensions are all awesome and better than the default Javaesque way

- Scala concurrency abstractions all better than Java, but concurrency is still hard. Proceed with caution

-- Except parallel collections. Use those liberally

- No Scalaz or any other FP-crusader stuff ever

- Macros and implicits should be rejected by default in code review. Amazing justifications required

- Enforce coding rules with linters. This applies for any language, but especially important for dynamic and non-straightjacket languages

OK, here's my question then. You're embracing Option type and yet presumably avoiding monads, applicatives, and functors (avoiding "FP-crusader stuff"), right?

So what happens when you are dealing with multiple option types, like you two optional ints you need to add together? Or you have an optional field in an optional object? This happens all the time in code that heavily uses Option types.

So do you match on every one, and nest match expressions inside match expressions inside match expressions? That can get very, very messy and even confusing.

For comprehensions should alleviate the examples you have given with regards to readability.

Although yes there are other more complex instances in which nested matches might come up. Generally I would think to handle them by creating a function that contains the next layer of matching instead of trying to come up with an uber function. Adds a bit more verbosity perhaps but I find it is fairly easy to follow and show intent.

My question was poorly phrased, since if you're using Option types, you're obviously already using monads, applicatives, and functors.

What I should have asked was: if you're using monads such as Option, then how do you use them effectively without using integral tools such as flatMap?

And you've answered: you can use for comprehensions. I primarily have used Haskell and OCaml, so I sometimes forget about Scala's very nice for comprehensions, which I agree is a great solution. But note that for comprehensions are just syntactic sugar for `flatMap`.

I agree that if you do use Option types in Scala, for comprehensions are a very elegant way to interact with them.

But I'll end with a question. If you're already using option types with for comprehensions, why not also throw in other useful monads like bifunctor IO, which is a great way to deal with async code? I understand the dislike for the tendency in the FP Scala community to use custom sigils, but I do think most people who are using options and for comprehensions would also be comfortable and happy using other useful monads with for comprehensions.

> but I do think most people who are using options and for comprehensions would also be comfortable and happy using other useful monads with for comprehensions.

You seem to vastly underestimate the gap there is between using a for comprehension and understanding what a bifunctor is.

Nothing wrong with that: once you gain a certain advanced knowledge, it's often very hard to put yourself in the shoes of someone who hasn't gained that knowledge, and even harder to explain it to them in terms that they will understand.

Which is why we have so many unhelpful monad tutorials.

> concurrency is still hard. Proceed with caution

> No Scalaz or any other FP-crusader stuff ever

That stuff which some people might consider FP-crusader vanity is something which can make concurrency manageable, maybe even joy.

I encourage you to open your mind, at least for a bit, and have a look at ZIO or Monix.

> Enforce coding rules with linters

Absolutely! Scalafmt and Scalafix for the win!

> By avoiding things like implicit ...

Implicit parameters are nothing more than a terminal argument list which the compiler attempts to provide from "the implicit scope." If you would rather it not, then simply provide them yourself at the point of invocation.

To wit:

  object HitchhikersGuide
  {
    implicit val answer : Int = 42;
    implicit val question : String = "the universe";

    def foo ()
      (implicit a : Int, q : String)
      : Unit =
      System.out.println (s"${q} = ${a}");

    // This
    foo ();

    // Is the same as this
    foo () (42, "the universe");
  }
It is left as an exercise for the reader to verify (hint: javap[0] is your friend).

HTH

0 - https://docs.oracle.com/javase/7/docs/technotes/tools/window...

This, and traits too!
As I've been learning Scala, traits is the feature that grabbed me and is what convinces me to keep learning. What a brilliant feature that cuts through all the OOP clutter that can come from various patterns to build up functionality. it is the killer feature, I think