
Strategic Scala Style: Principle of Least Power - rspivak
http://www.lihaoyi.com/post/StrategicScalaStylePrincipleofLeastPower.html
======
tome
This is full of great advice. After a brief skim I think I agree with
everything except the advice on mutability.

Haoyi says "if you are actually modeling something which changes over time,
using mutable [variables] is fine". I think this is absolutely wrong. You
should even use immutable variables to model things which change over time
(after all, almost everything does). Only use mutable variables when
performance means you can't get away without them.

Other than that the guide is pretty much how I would program (as a Haskell
developer, rather than a Scala one).

~~~
lihaoyi
I like immutability too. It's just sometimes rather inconvenient.

I would agree with you if you could write something like

[http://www.lihaoyi.com/roll/](http://www.lihaoyi.com/roll/)

in a pure immutable style and show me how it's better. You will start off with
lots of `.copy`s, maybe transition into lenses and other optics, and soon
you'll be on the bleeding edge of research and still not have a working game
that's maybe high-school difficulty if written in a mutable style

Research is cool and immutability is cool but if I can get what I want only
needing some highschool kid instead of a PL-PhD that's cool too

~~~
tome
Ah, but that's a different claim. The claim in the article is "You can use
mutability to model things that change with time.". Your claim here is "You
can use mutability if it makes programming easier.".

~~~
lihaoyi
How on earth are those things related? Because using mutability to model
mutable things makes programming easier, while using mutability to model
immutable things makes it harder. It's not rocket science...

~~~
tome
My intention is to try to tease out the finer distinction between where
immutability should be used and where mutability should be used, because I'm
personally very interested in the tradeoffs.

I'm genuinely sorry if my comment touched a raw nerve. As I said, I think it's
an excellent article.

One thing I got wrong in my original comment: use mutability where performance
means you can't get away without it, but _also_ where code complexity means
you can't get away without it. (My personal view is that there are many cases
of the former but few of the latter, but that's an argument for another day.)

~~~
lihaoyi
You didn't touch a nerve, I'm just being snarky because it's the internet and
if I don't the internet police will arrest me.

"code complexity means you can't get away with it" seems very common. Five
nested `.copy` calls is probably "too much". Lenses, in most of the
incantations I've seen so far, are probably "too much". I'd love to see your
solution for making

    
    
        a.copy(b = a.b.copy(c = a.b.c.copy(d = a.b.c.d.copy(e = 1))))
    

As easy as

    
    
        a.b.c.d.e = 1
    

Closest I've seen is QuickLens, which is promising. Still pretty new and
experimental, but definitely something I could see myself using:

    
    
        modify(a)(_.b.c.d.e).setTo(1)
    

Still slightly clunky, but less clunky than the other implementations I've
seen e.g. in Monocle

~~~
acjohnson55
The apparent impossibility of a simple lens syntax is one place where Scala is
letting me down. I hugely appreciate all the work that's going into things
like Dotty, but I wish people were equally as enthusiastic about basic
language usability improvements.

One of the most important things a language can do, IMO, is make basic
operations easily expressible. Lensing is one of those things.

------
chickenbane
An excellent read, Haoyi is a great Scala engineer. Additionally, if you love
using the Scala REPL, he has a entertaining and compelling video here:

[https://youtu.be/dP5tkmWAhjg](https://youtu.be/dP5tkmWAhjg)

------
lihaoyi
Neat to see this appear here. I wrote this. Ask me anything

~~~
AdieuToLogic
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.

~~~
lihaoyi
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!

~~~
AdieuToLogic
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](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.

------
davnn
Guys please have a quick look at Concepts, Techniques and Models of Computer
Programming, it's incredibly valuable. Basically the main point of the post is
the principle of least expressiveness. It really makes sense after you know
more about multi-paradigm programming.

Disclaimer: just a fan of the book :-)

------
kailuowang
The fun part of developing in scala is that it supports multiple completely
different styles and paradigms, each has its own strength and weakness. You
can pick the most suitable one for the functionality you are working on. On
other hand it also make learning more complex. To be most effective, you need
to learn these styles and paradigms and the subset of the language features
you limit yourself to when in that style. Otherwise, scala is definitely one
of the languages that is most capable of producing messy programs.

~~~
leonroy
> The fun part of developing in scala is that it supports multiple completely
> different styles and paradigms.

Putting on my management hat that thought fills me with horror.

For example pull requests become a nightmare when you have extremely
experienced Scala devs using every trick in the book and producing code which
no one else on the team understands except those with similar levels of Scala
knowledge.

Honestly, it's greek for the rest of us and furthermore it seems each Scala
expert has their own way of implementing the same functionality.

I enjoy programming in Scala, I write code in it (when I _have_ to) but in all
honesty as cool as it is we find ourselves needing to stick with Python, Java
and JavaScript to ensure we have some sort of consistency between new hires
and experienced devs.

~~~
lihaoyi
I agree with this 100%. Having too many ways of doing things, and no way to
pick between them, results in everyone doing things every which way, all
different.

That's why this document exists at all. We can definitely make things better,
but to do so requires putting together guidelines, proposals, and consensus
(things developers are historically pretty poor at), rather than just cranking
out code and complaining (both of which developers excel at, especially fueled
by coffee)

~~~
incepted
But we went through that with C++ already: all companies used to publish their
own guidelines of what C++ features were allowed and not allowed to use. Of
course, all these subsets were different. And of course, if you work on open
source project, it's just impossible to be productive in such a language
without knowing all of it.

------
spdionis
> Given a choice of languages, choose the least powerful language capable of
> solving your problem

Then no one would ever use scala?

~~~
lmm
Scala allows you to restrict yourself to a much greater extent than many other
languages. You could say use assembly for everything - there's nothing that
assembly can't do, so in a sense assembly is more powerful than any other
language.

For me the reasons I use Scala rather than Java/Python/etc. are the same
reasons I would use Java/Python/etc. rather than assembly. To write code with
the same guarantees as the core of my system would be theoretically possible
in a "simpler" language, but it would be incredibly verbose: it would take
longer to write and be much harder to maintain (because you'd have to keep a
lot more in your head to understand each piece of functionality). If I
weakened the guarantees (i.e. didn't keep track of as many possible side
effects) I'd have a higher defect rate, especially when refactoring.

That's the small heart of a few projects, but I see a lot of merit in using
the same language for your whole codebase:
[http://www.teamten.com/lawrence/writings/java-for-
everything...](http://www.teamten.com/lawrence/writings/java-for-
everything.html) . And Scala is actually a pretty nice language for one-off
throwaway scripts where you don't care about tracking any effects or similar
as well.

~~~
TeMPOraL
> _To write code with the same guarantees as the core of my system would be
> theoretically possible in a "simpler" language, but it would be incredibly
> verbose: it would take longer to write and be much harder to maintain
> (because you'd have to keep a lot more in your head to understand each piece
> of functionality)._

I could use the same reasoning to justify writing my code in Common Lisp
instead. The thing about Turing-complete languages is that the only real
dimmension of power is their expressiveness - i.e. how much boilerplate do you
have to write to implement something (and therefore how much of that cruft
other people have to deal with when trying to understand your code).

I therefore find this principle as applied to programming languages to be
illogical.

It makes sense in non-Turing-complete languages though (configurations, DSLs)
- because there by "least power" one means power in the computational sense.
Accidentally making a language too powerful when it's not needed can lead to
security issues.

~~~
lmm
> I could use the same reasoning to justify writing my code in Common Lisp
> instead.

Maybe you should then. I find it too hard to restrict my code in lisps: if I
want a certain part of my code to be in a DSL-like constrained sublanguage,
there's no standardized way to express that, only ad-hoc macros.

> It makes sense in non-Turing-complete languages though (configurations,
> DSLs) - because there by "least power" one means power in the computational
> sense.

I think non-Turing-complete general-purpose languages are possible. I'm very
excited for Idris.

~~~
TeMPOraL
> _I find it too hard to restrict my code in lisps: if I want a certain part
> of my code to be in a DSL-like constrained sublanguage, there 's no
> standardized way to express that, only ad-hoc macros._

There is a standardized way - via "ad-hoc" macros. Except they're not really
more "ad-hoc" than using classes and traits in Scala is "ad-hoc".

Maybe 'aninhumer has a point - restricting the language is kind of the
opposite goal of making it expressive/powerful.

> _I think non-Turing-complete general-purpose languages are possible. I 'm
> very excited for Idris._

Idris is not Turing-complete? :o.

[0] -
[https://news.ycombinator.com/item?id=11470569](https://news.ycombinator.com/item?id=11470569)

~~~
lmm
> There is a standardized way - via "ad-hoc" macros. Except they're not really
> more "ad-hoc" than using classes and traits in Scala is "ad-hoc".

They're not standardized enough to have good common tooling around restricted
embedded DSLs. If you use the type system to enforce restrictions around
certain areas of Scala code, every Scala tool understands it. IME a lot of
lisp programmers end up writing their own tool integration, because there is
no standard for that kind of restriction (macros can be arbitrary code; in
practice programmers restrict themselves to sensible macros, but in a way that
isn't exposed to tooling).

> Idris is not Turing-complete? :o.

In practice there are escape hatches, but it's a total language: your function
must come with a proof that it terminates.

~~~
conceit
> [Idris] is a total language: your function must come with a proof that it
> terminates.

So, you can't express the programs that never terminate, but can you express
all the programs that would terminate?

Point in case, you probably shouldn't ever want to use a language for the
specific feature that could express a non-deterministic program.

~~~
lmm
> So, you can't express the programs that never terminate, but can you express
> all the programs that would terminate?

This gets philosophical - would the program "Y {f => x => if(x is a proof of
the inconsistency of PA via the Goedel encoding) 0 else f(x+1) } 0" terminate?
If you assume consistency of PA then no.

> Point in case, you probably shouldn't ever want to use a language for the
> specific feature that could express a non-deterministic program.

Well the mu operator or equivalent (informally, the inverse operator) is an
extremely useful feature. Many useful programs can't be written strictly
primitive recursively (as a trivial example, you can't compute the Ackermann
function). I have hope that we can find more restricted versions of mu that
let us express all the programs we want to, but that's a decidedly nontrivial
problem.

------
incepted
It's refreshing to see some down-to-earth pragmatism in Scala land for a
change. Thanks, Li-Haoyi.

------
jonathanpoulter
Some useful advice, even to someone not writing in Scala, especially sections
like published interfaces.

> When defining an interface to a package someone else will use...

> 1\. The simplest interface to a package is static method with standard types

> 2\. Next, static methods that take/return custom types, presumably with
> methods

> 3\. Next, is needing the user to instantiate some classes

> 4\. Lastly, is needing the user to inherit from some classes

------
optimuspaul
How do you define power? What makes a solution or language more powerful than
another?

------
atemerev
tl;dr Scala is powerful. With great power comes great responsibility.
Constrain yourself. :)

