Practical functional design in Scala?


Practical functional design in Scala?

Hi people, I want to share some thoughts I collected so far while trying to understand intended way to use Scala.

Cases I've seen so far are:

  • use Scala as "better java". Prefer classes to traits, use modern OOP AKA preferring immutable classes and class aggregation. Good example of such style is http://ift.tt/zvT3Jc .
  • Haskell-style, with heavy use of monads and combinators. Largest practical project I've seen is http://ift.tt/1VILicn
  • Dotty compiler. Seems to be most interesting since it is relatively new Scala code with high requirements on performance written by people who should know Scala the best. Sources are here: http://ift.tt/2t5qpDB

Some facts about Dotty sources:

  • they do not use functions that return functions
  • they do not use IO, State or other monads that wrap functions
  • no typeclasses
  • no parser combinators
  • no implicit fields in objects that have to be imported
  • no implicit conversions
  • VERY limited set of types for implicit parameters (see below)
  • no cake pattern 🙂

Most code is organized in functions or methods with following signature:

def fun(arg1, arg2...)(implict ctx: Context): Result 

Where Context is huge object combined from many traits which serves two functions:

  • mutable global state.
  • application configuration. It provides implementations for multiple traits therefore working as dependency injection

So, what do I want to say…

Twitter-style OOP is solid and well-known with well-known problems of object aggregation – that to get working code we need to assemble large object tree. Which complicates code isolation and testing

Haskell-style uses excess CPU/GC which makes it less practical for writing fast software. Also it is quite complicated to write and understand.

Dotty takes another approach by separating data, algorithms and configuration. Naive approach to write code as functions is not that flexible: given

def f(v: Int) = g(v) 

you can't add new argument to g() w/o adding new arguments to entire call stack, you can't replace g() with another implementation except for passing it as function argument both for sake of configuration or testing.

Implicits won't really help because they are resolved by type – to provide implicit instances, caller must have them as implicit parameters, import them or pass them directly to second argument list.

Single Context type helps but is that really the best way? It breaks encapsulation by feeding functions more data than they need, it must be assembled correctly for unit tests, it shadows real dependencies of the function. Other issues are described there in scaladoc: http://ift.tt/2s6tdve

Well. Can dotty sources be used as an example of intended design for Scala applications? Is there better way to manage dependencies in programs that are composed of functions and not of objects? What design do you use?

Please, comment.

Submitted June 24, 2017 at 02:20PM by Scf37
via reddit http://ift.tt/2tF38FA