{
  "@context": "https://schema.org",
  "@type": "BlogPosting",
  "@id": "https://www.vidyasource.com/blog/scala-go/",
  "url": "https://www.vidyasource.com/blog/scala-go/",
  "mainEntityOfPage": "https://www.vidyasource.com/blog/scala-go/",
  "headline": "Scala vs Go: Who Wore It Better?",
  "description": "Scala vs Go compared on error handling, collections, and absent values. Which one fits your project?",
  "datePublished": "2021-09-01T00:00:00.000Z",
  "author": {
    "@type": "Person",
    "name": "Neil Chaudhuri",
    "jobTitle": "President",
    "url": "https://www.linkedin.com/in/neil-chaudhuri/",
    "sameAs": "https://www.linkedin.com/in/neil-chaudhuri/"
  },
  "publisher": {
    "@id": "https://www.vidyasource.com/#organization"
  },
  "image": "https://www.vidyasource.com/img/blog/scala-go.png",
  "keywords": [
    "Golang",
    "Play Framework",
    "SBT",
    "Git",
    "Programming",
    "Scala",
    "Go",
    "Software Engineering",
    "DevOps",
    "Functional Programming",
    "Microservices",
    "REST",
    "Agile",
    "TypeScript",
    "Elm",
    "Kotlin"
  ],
  "articleBody": "Scala and Go are two of the fastest growing leading-edge\nprogramming languages in the world. In the United States, they are also among the\n[most lucrative](https://adtmag.com/articles/2017/08/18/go-scala-salaries.aspx). Scala and Go are among a slew of\nprogramming languages that innovate in numerous ways to produce faster, more resilient, more secure applications for a multicore,\ncloud native, mobile world.\n\nThe thing is Scala and Go have *very* different philosophies on what makes engineers most productive and what defines great\napplications. We are going to look at how Scala and Go solve five common programming tasks\nand how their contrasting approaches reflect their contrasting philosophies.\nThen you can decide for yourself which works best for your next application. This is a really long post, so here are the\ntasks we will consider so you can jump to the ones that interest you most:\n\n* [Absent Values](#absent-values)\n* [Error Handling](#error-handling)\n* [Collections](#collections)\n* [Concurrency and Parallelism](#concurrency-and-parallelism)\n* [Polymorphism](#polymorphism)\n\nOr you can skip straight to my [conclusion](#how-do-you-decide), which boils down to this:\n\n> Despite the strong possibility that this opinion will subject me to ritual humiliation on social media, I would use Scala (or a similarly featured language like Kotlin) for microservices or bigger but Go both to replace any bash or Python scripts that are part of my continuous delivery pipeline and to create lambda functions, which are supposed to be lightweight, fast, and focused.\n\nWith that, let me introduce you to Scala and Go.\n\n## Scala\n\nScala is an object-oriented (OO) *and* functional programming language built for\nthe JVM (though [Scala Native](https://scala-native.readthedocs.io/en/latest/) is in the works). It emerged from academia at the\n[EPFL](https://scala.epfl.ch/) in Switzerland from the mind of Academic Director Martin Odersky, who sought to prove that\nthe two paradigms--OO for representing your domain and functional for its\n[mathematical guarantees for working programs](https://www.vidyasource.com/blog/business-case-for-functional-programming/)--can blend\nseamlessly to yield the best of both worlds. Scala also has an exquisite static type system that can provide a\npowerful safety net.\n\nOperationally, as you might expect from a language borne from academia, Scala tooling can be problematic and compilation\ncan be slow--particularly if you are not yet using Scala 3, which only recently emerged and is very slowly percolating\nthrough the ecosystem (Remember the Python 2 to Python 3 transition?). But type inference, a vast standard library, and the time-tested reliability of the JVM make you\nvery productive once you get the hang of them. Performance varies with the JVM you're running, but regardless you do have to\ncontend with the size of compiled objects and the latency of garbage collection at runtime. When you want to experiment,\nyou can skip the ceremony of writing a class or test and instead use a command-line REPL, an online REPL called\n[Scastie](https://scastie.scala-lang.org/) you can share, or an outstanding third-party command-line REPL called\n[Ammonite](https://ammonite.io/#Ammonite-REPL). Dependency management is achieved with SBT typically but\nalso more general JVM build tools like Gradle and Maven.\n\nScala became really popular with the advent of \"Big Data\" because\n[functional programming lends itself so naturally to analytics](https://www.vidyasource.com/blog/java-is-dysfunctional-with-big-data),\nand the learning curve for\n[modern LISPs like Haskell and Clojure](https://en.wikipedia.org/wiki/Lisp_(programming_language)#2000_to_present)\nis too high for too many. Apache Spark is built in Scala, and when it got big, Scala got big. Since then Scala has\nalso become a popular language for other domains including\n[reactive web applications and microservices](https://www.reactivemanifesto.org/) with\n[Play Framework](https://www.playframework.com/) and [Akka](https://akka.io/) and even the front end with\n[Scala.js](https://www.scala-js.org/).\n\n## Go\n\nGo had an pragmatic mission from the start, so it was built with simplicity, minimalism, and performance in mind. Robert Griesemer,\nRob Pike, and Ken Thompson created Go at Google as an [alternative to C++](https://talks.golang.org/2015/gophercon-goevolution.slide#4)\nbuilt for modern machines. Go is compiled to native machine code; no virtual machine. Go is statically typed\nlike Scala but is imperative and procedural. It's not\na functional language *per se*, but [functions are first-class](https://golangbot.com/first-class-functions/). Where it\ntruly shines is in its concurrency primitives: [channels and goroutines](https://medium.com/rungo/anatomy-of-channels-in-go-concurrency-in-go-1ec336086adb).\nTogether, they enable you to leverage the full power of your multicore machine.\n\nOperationally, Go is really fast to compile and run. It too\nhas a vast standard library for common tasks like [REST calls](https://golang.org/pkg/net/http/) and\n[JSON de/serialization](https://golang.org/pkg/encoding/json/). When you want to experiment, you can\nuse the [Go Playground](https://play.golang.org/) or a scratch file in your IDE, but there is no command-line REPL. Unlike Scala,\nGo has by design a very lean feature set and simple constructs. This makes it\nrelatively easy to learn. However, you have to add a lot of features yourself that you take for granted in other languages\nor explore what Go offers as [potential workarounds](https://golang.org/doc/faq#Why_doesnt_Go_have_feature_X).\nTo get *really* good at advanced features of Go like concurrency and polymorphism is still a challenge.\nOfficial dependency management is nonexistent, and you may find Go's unorthodox project setup based on Git repos\n[takes getting used to](https://medium.com/rungo/working-in-go-workspace-3b0576e0534a).\n\nAfter Go took off at Google and was released to the public, it got really popular as the language of concurrency, which helped\nin turn to make it the language of\nDevOps--particularly in concert with\n[Kubernetes](https://rancher.com/using-kubernetes-api-go-kubecon-2017-session-recap/), which also emerged from Google.\nGo has expanded into other domains as well\nwith the CMS [Hugo](https://gohugo.io/) and the microservices framework [Go kit](https://gokit.io/).\n\n## Comparing Scala and Go\n\nLet's look at how Scala and Go handle the kinds of real-world problems you will encounter every day.\n\n*Disclaimer:* The sample code is not meant to showcase necessarily the \"best\" way to solve these problems--most performant, most elegant,\nor whatever. Instead it is meant to showcase reasonable, idiomatic solutions and, more importantly, how they reflect\nthe design philosophies of the respective languages. Of course\n[you are welcome to suggest improvements](https://www.vidyasource.com/contact/) regardless. Also, the Scala code uses some of the revolutionary\n[Scala 3](https://docs.scala-lang.org/scala3/new-in-scala3.html) syntax and constructs.\n\n### <a name=\"absent-values\"></a>Absent values\n\nYou have to deal with potentially absent values all the time like when database queries for single entities return no hits or\nwhen you are maintaining backwards-compatible microservices. Typically, absent values are represented with `null`. Sir\nTony Hoare called his invention of `null` to represent the absence of a value his\n\"[billion-dollar mistake](https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare).\"\nHandling absent values isn't glamorous, but if you do it poorly, you will suffer significant productivity loses.\n\n#### Scala\n\nAlthough `null` exists in Scala as a JVM language, you should (almost) never interact with it directly. Instead, you should work with `Option`, a\n[monad](https://stackoverflow.com/questions/44965/what-is-a-monad) designed specifically for this purpose.\nWe describe `Option` in more detail in [our tutorial](https://www.youtube.com/watch?v=rbZ6GzR8B7I),\nbut essentially it compels you to account for the potential absence of a value at *compile* time. This avoids the costly\n`NullPointerException` at runtime that has sent thousands of Java developers to therapy.\n\n~~~scala \ncase class Student(name: String, house: String)\n\ndef findStudent(key: Int): Option[Student] = {\n  val students = Map(\n    1 -> Student(\"Harry Potter\", \"Hogwarts\"),\n    3 -> Student(\"Draco Malfoy\", \"Slytherin\")\n  )\n  students.get(key)\n}\n\nfindStudent(3)\n  .map(s => s\"The student's name is ${s.name}\")\n  .getOrElse(\"Back to Hogwarts\")\n~~~\n\nIn this example, the `findStudent` function returns an `Option[Student]`. If the `Option` contains a value, the client\ncan transform it into a `String` (*i.e.* `Option[Student] => Option[String]`) with the `map` function on `Option`;\notherwise, `getOrElse` handles the absent value.\n\n`Option` allows you to safely work with potentially absent values\n[without fear](https://marvel.fandom.com/wiki/Daredevil:_The_Man_Without_Fear_Vol_1_1). The compile-time safety makes\nyou very productive. On the other hand, every transformation on the `Option`--via `map`, `filter`, *etc.*--produces a new\nvalue because of the functional programming bias towards [immutability](https://www.vidyasource.com/blog/business-case-for-functional-programming/).\nIf memory is at a premium, maybe this is a concern.\n\n#### Go\n\nGo handles potentially absent values through completely different idioms. Functions can return multiple return values.\nWhen a value is present, it's idiomatic to return the value and a `bool` value (named `ok` by convention) of `true`; otherwise, it returns\nthe [\"zero value\" of the return type](https://tour.golang.org/basics/12) and `false`. The client uses an imperative\n`if` statement to distinguish.\n\n~~~go \npackage main\n\nimport \"fmt\"\n\ntype Student struct {\n\tname string\n\thouse string\n}\n\nfunc findStudent(key int) (Student, bool) {\n\tstudents := map[int]Student{\n\t   1: Student{name: \"Harry Potter\", house: \"Hogwarts\"}, \n\t   3: Student{name: \"Draco Malfoy\", house: \"Slytherin\"}\n\t}\n\tstudent, ok := students[key]\n\treturn student, ok\n}\n\nfunc main() {\n\tif student, ok := findStudent(3); ok {\n\t\tfmt.Printf(\"The student's name is %v\\n\", student.name)\n\t} else {\n\t\tfmt.Println(\"Back to Hogwarts\")\n\t}\n}\n~~~\n\nIn this example, the `findStudent` function returns a `Student` *and* a `bool`. Go allows you to initialize and test\nconditions in one line, and that's what we see here. If `ok` is `true`, we know we got something and act\naccordingly; if `ok` is `false`, we know the value is absent and handle this contingency.\n\nFor those uncomfortable with monad composition and higher-order functions, Go provides a very simple alternative in keeping\nwith its mission. On the other hand, Go engineers need the knowledge and discipline to apply these language features and idioms.\nThere is no dedicated type like `Option` to help. Also, unlike with the composability afforded by monads like `Option`,\nif you need to compose multiple potentially absent values, you need to write multiple `if` conditions; that can feel verbose.\nThe simplicity may well be worth it though.\n\n### <a name=\"error-handling\"></a>Error Handling\n\nThis is another inglorious task that is critical to good software engineering. As programs become big and complex, proper\nerror handling is critical to diagnose bugs and move builds to production as quickly as possible.\n\n#### Scala\n\nAs you might imagine from a language that prizes on immutability and composability, Scala offers another monad,\n`Try`, for error handling. Analogous to `Option`, it compels\nyou to account for a possible error at compile time rather than the absence of a value. A `Try[Double]`, for example,\nrepresents either a `Double` if all is well or an error (or more precisely an instance of `Throwable`) otherwise.\n\n~~~scala\nimport scala.util.{Failure, Success, Try}\n\ndef squareRoot(value: Int): Try[Double] = {\n  if (value >= 0) {\n    Success(Math.sqrt(value))\n  } else {\n    Failure(new IllegalArgumentException(\"Value cannot be negative\"))\n  }\n}\nval sum = for\n  a <- squareRoot(4)\n  b <- squareRoot(16)\nyield a + b\nsum.map(s => s\"The result is $s\") match {\n  case Success(result) => result\n  case Failure(t) => t.getMessage\n}\n~~~\n\nIn this example, the `squareRoot` function returns `Try[Double]`, and the client does something a bit more complicated\nthan the prior example. It calls `squareRoot` twice and uses a [for comprehension](https://docs.scala-lang.org/tour/for-comprehensions.html),\nwhich is syntactic sugar for otherwise cumbersome `map` and `flatMap` transformations, to take advantage of the composability of\nmonads to sum the results. If both calls work out, then the result is a `Try` with the sum; if either fails, the error information\nis passed on. You can do similar with `Option` too. This is why monad composability in Scala is so cool. The same pattern\nworks on very different types as long as they follow the [monad laws](https://miklos-martin.github.io/learn/fp/2016/03/10/monad-laws-for-regular-developers.html).\n\nAs before though, keep in mind that you are generating new objects with each transformation. This means you're paying\nfor the protection immutability affords you with memory.\n\n#### Go\n\nJust as `Option` and `Try` in Scala are similar, handling absent values and handling errors in Go is similar. Here again Go\ntakes advantage of multiple return values, but rather than a value accompanied by a `bool`, idiomatic Go error handling features a value accompanied by\nan `Error` (named `err` by convention). If `err` has the value of `nil`, then you can work with the value because it's all good;\notherwise, you handle the error and (probably) ignore the zero-value.\n\n~~~go\npackage main\n\nimport \"fmt\"\nimport \"math\"\n\nfunc squareRoot(value int) (float64, error) {\n\tif value >= 0 {\n\t\treturn math.Sqrt(float64(value)), nil\n\t} else {\n\t\treturn 0, fmt.Errorf(\"invalid input: %v\", value)\n\t}\n}\n\nfunc sumRoots(v1, v2 int) (float64, error) {\n\ta, err := squareRoot(v1)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tb, err := squareRoot(v2)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn a + b, nil\n}\n\nfunc main() {\n\tif sum, err := sumRoots(4, 16); err == nil {\n\t\tfmt.Printf(\"The sum is %v\\n\", sum)\n\t\treturn\n\t} else {\n\t\tfmt.Println(\"Value cannot be negative\")\n\t}\n}\n~~~\n\nIn this example, the `sumRoots` function, which is the client of `squareRoot`, returns a value and an error.\nThose are saved from the first call to `squareRoot`. If there is an error, the function returns immediately; otherwise, it does the same\nwith the second call to `squareRoot`. If the function makes it to the end, then it returns the sum and a `nil` error.\nYou will see a similar approach in `main` with the call to `sumRoots`.\n\nThere are a few interesting things to note. First, once again the code reflects Go's bias toward simplicity. Furthermore,\nas immutability is not a priority, variable reuse is common in Go. In\nthis case, `err` is reused to store the error returned from `squareRoot`. It doesn't happen here, but it isn't unheard of\nto reuse the value variable as well once its initial value has served its purpose. Finally, we see a common pattern:\n\n* Call a function\n* Test `err` for `nil`\n* If an error is found, return\n* Call the next function\n* Test `err` for `nil`\n* If an error is found, return\n* Lather, rinse, repeat\n\nAbsent Scala's function composition, it's a fair bit of boilerplate--particularly when you are calling a lot of functions\nthat may return errors. You can take advantage of language features to make it more elegant like\n[this pattern utilizing interfaces and pointers from Rob Pike](https://golang.org/doc/faq#exceptions),\nbut this is one of the ways where Go's simplicity is a bit overrated. Building custom abstractions from Go's toolkit requires creativity,\nand you will have to do that often when you use Go to build mature applications. Still, it is almost certainly easier to\nmaster advanced patterns in Go than it is in Scala.\n\nFinally, even though it doesn't appear in this example, `defer` [is a simple but powerful construct](https://gobyexample.com/defer)\nin Go that executes a call at the end of a function no matter what happens--error or otherwise. You can use `defer` to\nclean things up after an error in the same way you'd use `finally` in other languages.\n\n### <a name=\"collections\"></a>Collections\n\nI don't have to tell you that manipulating data from a database, a [stream](https://medium.com/stream-processing/what-is-stream-processing-1eadfca11b97),\na REST request, or a host of other sources is a common task in application development. Languages that facilitate seamless\ntransformation and aggregation of collections of data make your life a lot easier.\n\n#### Scala\n\nScala has a vast library of collections--both immutable and mutable though immutable is preferred--each of which offers in turn\na vast collection of higher-order functions that let you manipulate the collection in numerous ways.\n\n~~~scala\nval words = List(\"Fear\", \"of\", \"a\", \"name\", \"only\", \"increases\", \"fear\", \"of\", \"the\", \"thing\", \"itself\")\n\nwords\n  .groupBy(_.toLowerCase)\n  .mapValues(_.size)\n~~~\n\nIn this example, given a `List` of words, the code uses `groupBy` to convert it into a `Map` of each word (lowercased to normalize them)\nto a list of occurrences of each word. Finally, those lists are transformed into their sizes, and the result is a `Map`\nof each word to its count.\n\nThere isn't much to see. This is a credit to Scala's powerful abstractions. Still, it is important to keep in mind that this\ncode performs multiple *O(n)* traversals and with each one consumes memory to produce an entirely new collection--all but the first\nand last of which are immediately thrown away.\n\n#### Go\n\nGo naturally takes a far more lightweight approach to collections. You will essentially only deal with [maps](https://blog.golang.org/go-maps-in-action) and\n[slices](https://blog.golang.org/go-slices-usage-and-internals), which are array views that enable memory-efficient\noperations on the backing arrays.\n\n~~~go\npackage main\n\nimport \"fmt\"\nimport \"strings\"\n\nfunc main() {\n\tdictionary := make(map[string]int)\n\twords := []string{\"Fear\", \"of\", \"a\", \"name\", \"only\", \"increases\", \"fear\", \"of\", \"the\", \"thing\", \"itself\"}\n\tfor _, word := range words {\n\t\tword = strings.ToLower(word)\n\t\tif count, ok := dictionary[word]; ok {\n\t\t\tdictionary[word] = count + 1\n\t\t} else {\n\t\t\tdictionary[word] = 1\n\t\t}\n\t}\n\n\tfmt.Printf(\"The counts are %v\\n\", dictionary)\n}\n~~~\n\nIn this example, the code uses `make` both to create an empty map (with values defaulting to the zero value, in this case literally 0)\nto hold the word counts and to create a slice containing\nthe words. It simply iterates over the slice and builds the word count using the `ok` idiom you saw before to check\nif there is an existing entry for the word in the map.\n\nThe code is more verbose than its Scala counterpart, but it is significantly more efficient in time and space. There is a\nsingle *O(n)* traversal with constant-time lookups in the map, and we maintain only two collections the entire time. The efficiency\nof Go collections and the simplicity of using them are among the best reasons to use Go in a project.\n\n### <a name=\"concurrency-and-parallelism\"></a>Concurrency and Parallelism\n\nModern software applications have a lot more to do in a lot less time, so it's important for your code to take advantage\nof every bit of power your machines have. Modern applications demand modern languages that enable you to leverage every\ncore through abstractions that strike the right balance of power and intuitiveness. Perhaps most importantly, they need to provide\nmechanisms for handling errors because concurrent/parallel programming is notoriously hard to debug. It's a challenge to reproduce\nthe conditions that generated the bug in the first place.\n\nBy the way, as Rob Pike has taught us, [concurrency is not parallelism](https://www.youtube.com/watch?v=cN_DpYBzKso). Long story\nshort, concurrency is about decomposing a problem into its components; each component might then run in parallel depending\non available resources. Concurrency *manages* a lot of things at once while *parallelism* does a lot of things at once.\n\nRegardless, doing concurrency and parallelism well is a hard problem. Whatever path you choose, you need to understand the\nnature of your tasks to achieve peak performance. Are they IO- or CPU-intensive? Are they intrinsically parallel?\n\n#### Scala\n\nAs a core language in [reactive programming](https://www.reactivemanifesto.org/), Scala takes concurrency and parallelism very seriously.\nIt offers `Future` as its [core primitive](https://docs.scala-lang.org/overviews/core/futures.html) to facilitate concurrency and parallelism--in concert with an\n`ExecutionContext`, which is basically a [thread pool](https://www.playframework.com/documentation/2.7.x/ThreadPools).\n`Future` abstracts the threads away from you, which is nice, but its association with `ExecutionContext`\ncan lead to some [complexity](https://stackoverflow.com/questions/27454798/is-future-in-scala-a-monad) in understanding\n[how they work](http://www.beyondthelines.net/computing/scala-future-and-execution-context/).\nThis is why notable [third-party libraries](https://alvinalexander.com/scala/differences-scalaz-task-scala-future-referential-lazy)\noffer their own concurrency primitives. Still, `Future` at least approximates a monad, and that means we can *mostly*\nadhere to the familiar patterns we saw with `Option` and `Try`.\n\nAt a low level, each asynchronous call delegates to a different thread. This helps scale your application, but it's also\na heavyweight operation as the operating\nsystem needs to schedule threads against physical processors and manage expensive context switching. The result is that\nfor some problems parallelism with `Future` in Scala may potentially consume a lot of resources for merely a modest\nincrease in performance--or even slow you down. When performance is a major concern, you need to configure your `ExecutionContext`\nsmartly according to the capability of your machine(s) and the nature of your tasks.\n\nBottom line? Reactive programming doesn't necessarily make your applications faster (except maybe in those cases where you\ncan do expensive work in parallel), but it usually allows them to be more resilient. Reactive applications\nscale under load with limited threads and memory especially when you have latency from inconsistent network IO like database and REST calls.\n\n~~~scala\nimport akka.actor.ActorSystem\nimport akka.stream.ActorMaterializer\nimport play.api.libs.ws._\nimport play.api.libs.ws.ahc._\n\nimport scala.concurrent.Future\nimport scala.concurrent.ExecutionContext.Implicits._\nimport play.api.libs.json._\n\nimplicit val system = ActorSystem()\nsystem.registerOnTermination {\n  System.exit(0)\n}\nimplicit val materializer = ActorMaterializer()\nval ws = StandaloneAhcWSClient()\n\ndef getUuid: Future[String] = {\n  ws.url(\"https://httpbin.org/uuid\")\n    .withHttpHeaders(\"Accept\" -> \"application/json\")\n    .get\n    .map { response =>\n      (response.body[JsValue] \\ \"uuid\").as[String]\n    }\n}\nval uuid1: Future[String] = getUuid\nval uuid2: Future[String] = getUuid\nval uuids = for\n  u1 <- uuid1\n  u2 <- uuid2\nyield (u1, u2)\nuuids\n  .foreach {\n    case (u1, u2) => println(s\"UUIDs: $u1, $u2\")\n  }\n  .andThen { case _ => ws.close() }\n  .andThen { case _ => system.terminate() }\n  .recover {\n    case t: Throwable => println(s\"There was a problem: $t\")\n  }\n~~~\n\nIn this example, the code uses [Play WS Standalone](https://github.com/playframework/play-ws) as a REST client to fetch\nJSON containing a [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier).\nPlay WS has an asynchronous, non-blocking API based on `Future`, so you need to\nprovide an `ExecutionContext` via [Akka](https://doc.akka.io/docs/akka/2.5/stream/). That's all the boilerplate at the\nbeginning of this example. Sometimes it will be done for you\nas when you use Play WS in the context of [Play Framework](https://www.playframework.com/). Nonetheless, you should be\naware it has to happen somewhere.\n\nIn the `getUuid` function, the `get` call to Play WS makes an asynchronous, non-blocking HTTP GET request and returns a `Future`\ncontaining the response. You need to be careful and make sure you only work directly with the `Future` itself lest you\nlose the eventual result--the response or an error. Otherwise anything you do next outside the realm of the asynchronous call\nwill execute after the request is dispatched but completely independently from when the response returns. That's a common\nmistake for engineers new to `Future`. This is why everything that happens after the call in `getUuid` is dispatched is a method call on the\n`Future` object that comes back.\n\nLike a typical monad, `Future` offers `map` to enable clients to transform results, and `getUuid` does exactly that by parsing\nthe JSON response into the UUID string and returning a `Future[String]`. If the REST call returned an error, it\nremains preserved in the `Future.` The client of `getUuid` requires two calls to complete before\nmoving forward, so it calls the function twice so the independent fetches can run in parallel\nand composes them as we saw with `Try` via `for` comprehension to produce a `Future[Tuple2[String, String]]`. Finally,\nthe code calls `map` again to transform the tuple of strings into a printed result. If there is an error any step of the way,\nwe handle it with `recover`.\n\n`Future` is a really nice concurrency primitive that offers the convenience of a monad, but it has its pitfalls. As mentioned,\nyou need to supply a finely tuned `ExecutionContext`, and everything you do once you make an asynchronous call better happen in the\ncontext of the `Future` that returns. Otherwise, you will find very confusing results like silent failures. The\n[amorphous referential transparency](https://www.reddit.com/r/scala/comments/3zofjl/why_is_future_totally_unusable/)\nof `Future` can confuse you too. If the code made its calls to `getUuid` inside the `for` comprehension, they would have\nbeen sequential and not parallel. The result is likely the same, which *feels*\n[referentially transparent](https://alvinalexander.com/scala/how-to-create-scala-methods-no-side-effects-pure-functions#referential-transparency), but the fact\nwhere you make the call impacts the desired parallelism definitely doesn't.\n\n`Future` also consumes a lot of system resources because it transacts in full threads that have to managed by the operating system.\nIt's important to profile your application to understand what's going on because there will almost be certainly occasions\nwhere things don't perform as you expect. You will need to diagnose if it is a code or resource problem.\n\n#### Go\n\nConcurrency and parallelism lie at the heart of Go's mission as well, but of course it takes a totally different approach\nwith primitives called [goroutines](https://tour.golang.org/concurrency/1) and [channels](https://gobyexample.com/channels).\nThe philosophy here is to break down your tasks into\nindependent functions, and spin them off into goroutines. The key thing to remember is\n[goroutines are not threads](https://golang.org/doc/faq#goroutines); they are lightweight pieces of memory tracking thread usage.\nAs a result, you can spin off literally hundreds of thousands of goroutines and use only a little memory on the stack while\nthe Go scheduler, not you, uses clever algorithms to manage them in concert with the operating system and its actual threads.\n\nGoroutines use a paradigm called [communicating sequential processes](https://en.wikipedia.org/wiki/Communicating_sequential_processes) (CSP)\ndeveloped by Sir Tony Hoare, who despite being the `null` guy is a pillar of computer science. CSP is a\nmessage-passing model. Goroutines pass their data over channels rather than\nsynchronize data, which slows things down. If you are familiar with messaging patterns like\n[Gregor Hohpe's Enterprise Integration Patterns](https://www.enterpriseintegrationpatterns.com/), then you understand the power\nof this approach to manipulate message data as needed to produce the results you want--and at scale thanks to Go.\n\n~~~go\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\ntype Result struct {\n\tuuid string\n\terr error\n}\n\nfunc getUuid(rc chan<- Result) {\n\tr, err := http.Get(\"https://httpbin.org/uuid\")\n\tif err != nil {\n\t\trc <- Result{\"\", err}\n\t\treturn\n\t}\n\tresponse := make(map[string]interface{})\n\terr = json.NewDecoder(r.Body).Decode(&response)\n\tif err != nil {\n\t\trc <- Result{\"\", err}\n\t\treturn\n\t}\n\trc <- Result{response[\"uuid\"].(string), nil}\n}\n\nfunc main() {\n\tn := 2\n\trc := make(chan Result, n)\n\tuuids := make([]string, 0, n)\n\tfor i := 0; i < n; i++ {\n\t\tgo getUuid(rc)\n\t}\n\tfor i := 0; i < n; i++ {\n\t\tr := <-rc\n\t\tif r.err != nil {\n\t\t\tfmt.Printf(\"There was a problem: %v\\n\", r.err)\n\t\t\treturn\n\t\t} else {\n\t\t\tuuids = append(uuids, r.uuid)\n\t\t}\n\t}\n\tfmt.Printf(\"UUIDs: %v\\n\", strings.Join(uuids, \", \"))\n}\n~~~\n\nIn this example, the code defines a type called `Result` containing a UUID and an error to hold the result of a concurrent\ncomputation. Only one of those should be populated with a meaningful value, but there is no simple way to enforce that.\nMeanwhile, the `main` function creates a [buffered channel](https://gobyexample.com/channel-buffering) for communicating\n`Result` values and spins off two goroutines for two concurrent `getUuid` calls, which take the channel as a write-only\nparameter. That *is* enforceable.\n\nThe `getUuid` function makes the REST call and unmarshals the JSON response. If an error occurs at either step, the code creates\na `Result` with the respective error (and the zero value, the empty string, for the UUID) and publishes it to the channel.\nIf all goes well, the code creates a `Result` with the UUID (and the zero value, `nil`, for the error) and publishes\nit to the channel instead. The `main` function knows in this case how many `Results` to expect from the channel and consumes\nthe right amount of data from the channel. It bails at the first error it finds, or it accumulates the data into a slice of UUIDs.\n\nGoroutines are lightweight and flexible--and straightforward if you have a good grasp on messaging. Still, there are subtleties\nthat can make them more complicated than we might expect from Go. If you don't understand the difference between buffered\nand unbuffered channels, you may find [curious results](https://stackoverflow.com/questions/18660533/why-using-unbuffered-channel-in-the-same-goroutine-gives-a-deadlock).\nError handling is also tricky because patterns aren't obvious. In this case we encapsulated success and error cases in a single\nstruct, but some advise using [dedicated error channels](https://stackoverflow.com/a/42890750/1347281). This example is also\nas simple as it gets: You have one channel, and you know how many computations will transpire. In more\ncomplicated cases you will need to utilize other Go functionality from the [sync](https://golang.org/pkg/sync/) package\nlike `Mutex`, `WaitGroup`, `Pool`. You might even need the [experimental](https://rodaine.com/2017/05/x-files-intro/)\n`ErrGroup`. Finally, you need to be very careful with [pointers](https://tour.golang.org/moretypes/1) and mutability generally\nas always in concurrent programming. They save memory but you need to govern access carefully.\n\nConcurrency and parallelism are always hard. You have to decide which language offers primitives that comport with your mental\nmodel of how things should work.\n\n### <a name=\"polymorphism\"></a>Polymorphism\n\nYou know polymorphism. Far beyond trite `Animal`-`Dog`-`Cat` examples, the business value of polymorphism is to\nleverage abstractions to limit changes to your code even as the functionality of your application grows. By defining\nnew behavior leveraged through old abstractions, you can build software efficiently, and you don't have\nto work weekends when your client demands new features immediately.\n\n#### Scala\n\nAs an OO language, Scala offers the familiar polymorphism that developers in Java, Ruby, and similar languages\nhave loved for years, but because it is a functional language with a rich type system, it also offers\n[typeclass polymorphism](https://medium.com/@sinisalouc/ad-hoc-polymorphism-and-type-classes-442ae22e5342), which enables\ncompletely unrelated types to exhibit polymorphic behavior. You can think of it as functional programming's take on the\n[Open-Closed Principle](https://stackify.com/solid-design-open-closed-principle/) from OO. Perhaps most striking of all\nin comparison to Go, Scala offers parametric polymorphism--what the kids call \"generics.\"\nWhen [Java 5 introduced generics](https://docs.oracle.com/javase/tutorial/extra/generics/index.html),\nit was revolutionary, and Scala benefits as well.\n\n~~~scala\n// Runtime polymorphism\n\ntrait Closeable:\n  def name: String\n  def close: String\n\ncase class Connection(override val name: String, database: String) extends Closeable:\n  override def close: String = s\"Closing connection $name\"\n\n\ncase class File(override val name: String, opener: String) extends Closeable:\n  override def close: String = s\"Closing file $name\"\n\n\ndef showClosing(c: Closeable): String = c.close\n\nval w = Connection(\"MyConnection\", \"PostgreSQL\")\nval f = File(\"file.txt\", \"TextEdit\")\nshowClosing(w)\nshowClosing(f)\n\n\n// Parameteric polymorphism (generics) with List[T]\n\nval numbers = List(1, 2, 3)\nval firstNumber: Int = numbers.head\n\nval strings = List(\"1\", \"2\", \"3\")\nval firstString: String = strings.head\n\ndef use[T <: File](t: T) = s\"Using ${t.name}\"\n\nuse(f)\n\n\n// Typeclass polymorphism\n\ncase class Complex(real: Double, imaginary: Double)\n\nobject Complex:\n  given Ordering[Complex] with\n    def compare(a: Complex, b: Complex): Int = a.real.compare(b.real)\n\n  \nList(Complex(3, 5), Complex(10, 2), Complex(1, 6)).sorted\n~~~\n\nIn this example, we really see three examples of polymorphism in Scala. The first is the kind of straightforward\n[runtime polymorphism](https://stackoverflow.com/questions/28961957/example-of-runtime-polymorphism-in-java) familiar to Java developers--except with\n[Scala traits rather than Java interfaces](https://stackoverflow.com/questions/16410298/what-are-the-differences-and-similarties-between-scala-traits-vs-java-8-interfa).\n`Connection` and `File` are marked as instances of `Closeable`, and the `showClosing` function accepts a `Closeable` parameter.\nTherefore either `Connection` or `File` is suitable to pass to `showClosing` and the result of the function is dynamically and\npolymorphically resolved.\n\nThe second example of polymorphism is also familiar to Java developers. It's generics. In Scala, you never have just `List`;\nyou have `List[T]`, where `T` is a type parameter indicating the type of item in the `List`. Scala's type inference deduces that\n`numbers` is an instance of `List[Int]`; `strings` is an instance of `List[String]`. The `head` method returns the first element\nof a `List`, which is a `T`. `T` is `Int` with `numbers` and `String` with `strings`.\nFinally, just for demonstration purposes, the code defines a function `use` that's not only type parameterized but type\nbounded. It only accepts a type that's `File` or any subclass of `File`. If you try passing a `Connection` to it, the code\nwon't compile.\n\nThe last example is what I consider the coolest and most powerful form of polymorphism in Scala--typeclass polymorphism.\n`List` has a `sorted` method as you'd hope, but it requires an\n[implicit ordering](https://www.scala-lang.org/api/current/scala/collection/immutable/List.html#sorted[B%3E:A](implicitord:scala.math.Ordering[B]):Repr).\nIn other words, you have to tell `List[T]` how to sort its elements by providing a function that\n[lifts](https://stackoverflow.com/questions/17965059/what-is-lifting-in-scala) `T` into an `Ordering[T]`. This makes\nperfect sense, and it is enforced by Scala's type system. The code defines\na type called `Complex` and a way to lift `Complex` to `Ordering[Complex]` that defines how instances of `Complex` should be sorted.\nWithout this, `List[Complex]` wouldn't know how to sort its contents, and that last line wouldn't compile. This means you can\nmake any `T` sortable by defining an `Ordering[T]` typeclass. More broadly, it means you can use typeclasses to add\npolymorphic behavior to completely unrelated types, which includes types you don't control like legacy types and/or\ntypes found in imported dependencies.\n\nPolymorphism in Scala is powerful and flexible because of its sophisticated type system. It allows you not only to extend functionality\nin clever ways but also to constrain the solution space. In other words, you can limit the number of ways a problem can be solved,\nwhich makes it harder to write bugs.\n\n#### Go\n\nGo is not an OO language. Structs have no capacity for inheritance by design--only composition via\n\"[embedding](https://golang.org/doc/effective_go.html#embedding)\". Go is not quite functional either. However, polymorphic\nbehavior is not only possible but a fundamental part of the power of Go via\n[structural typing](https://en.wikipedia.org/wiki/Structural_type_system). You can take advantage by doing two things. First, you write\n[interfaces](https://gobyexample.com/interfaces), which as usual define the method signatures for a set of API calls. You\ncan also compose interfaces via embedding. Second, you can\nendow any type--an existing Go type like `float64` or your own custom structs--with behavior by defining functions and assigning\nthem to the type. When you do this, the type is called a \"[receiver](https://tour.golang.org/methods/8)\", and if the receiver\nhas been assigned all the functions associated with a given interface, it is an implicit instance of that interface. You\ncan then pass the type to any function expecting an instance of that interface, and it's resolved at compile time, which\nmakes you more productive in stark contrast to the runtime resolution of\n[duck typing](https://en.wikipedia.org/wiki/Duck_typing) in dynamic languages like Python.\n\n~~~go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\ntype Named struct {\n\tname string\n}\n\ntype Connection struct {\n\tNamed\n\tdatabase string\n}\n\ntype Closeable interface {\n\tclose() string\n}\n\nfunc (c Connection) close() string {\n\treturn fmt.Sprintf(\"Closing connection %v\", c.name)\n}\n\ntype File struct {\n\tNamed\n\topener string\n}\n\nfunc (f File) close() string {\n\treturn fmt.Sprintf(\"Closing %v\", f.name)\n}\n\nfunc showClosing(c Closeable) string {\n\treturn c.close()\n}\n\nfunc main() {\n\tconnection := Connection{Named{name: \"MyConnection\"}, \"PostgreSQL\"}\n\tfile := File{Named{name: \"file.txt\"}, \"TextEdit\"}\n\tfmt.Println(showClosing(connection))\n\tfmt.Println(showClosing(file))\n}\n~~~\n\nIn this example, interface `Named` is embedded in `Connection` and `File`. Note that this does not denote any relationship\namong them, but there is a tight coupling similar to inheritance in that any changes to `Named` are reflected wherever it\nis embedded. Interface `Closeable` is defined with a single function `close` with no parameters and returning a `string`.\nAny type with an identical function is resolved at compile time as an instance of `Closeable`, and lucky for us,\n`Connection` and `File` qualify as they are both receivers of a `close` function with the right signature.\nAs a result, `showClosing` works just fine when called on both kinds of structs.\n\nThat's the extent of Go's polymorphism. Clearly it is not remotely as extensive as Scala's, but it promotes two important\nvalues--composition over inheritance and abstraction over implementation. Engineers coming from traditional OO backgrounds\nmay find Go's polymorphism takes a little getting used to, but with some creativity you will find it\n[quite powerful](https://talks.golang.org/2015/json.slide#1). There is no question, however, that the absence of generics\ncan be quite jarring for more complex applications. It's such a powerful and pervasive idiom that even front end languages\nlike [TypeScript](https://www.typescriptlang.org/docs/handbook/generics.html) and [Elm](https://elmprogramming.com/type-system.html) have it.\nAs it happens, there has been such demand for generics from the Go community that\n[the maintainers have begun considering it](https://go.googlesource.com/proposal/+/master/design/go2draft-generics-overview.md).\nAs the debate rages on whether the benefits outweigh the costs--potentially the speed and simplicity fundamental to\nGo's mission--just recognize you won't have the benefit of generics for a while.\n\n# How Do You Decide?\n\nScala and Go are two great languages with fundamentally different philosophies that offer distinct advantages and disadvantages. I've\ntried to lay those out as simply as I can so you can extrapolate which might be best suited to your situation.\n\nHaving worked on complex applications with both languages, I can tell you Scala takes longer to grasp, and you will have a harder\ntime finding Scala engineers--especially in the United States and especially if you require onsite work. But if you\nhave senior staff who can mentor novices and who can build abstractions that utilize functional programming's strengths\nand the exquisite type system to constrain wayward novices, you will find the team growing very productive very quickly.\nDay-to-day tasks like compilation and continuous delivery are slower, and you will often find yourself exploring Scala's rich\nopen-source community to enhance development.\n\nMeanwhile, anyone can learn Go. The constructs are simple and lightweight, and compiling and executing are just so fast. It's amazing.\nMastering the more advanced concepts of Go, however, demands effort. You will also find yourself reinventing the wheel\nfrom other languages often--like writing your own `filter` function, which is easy enough but is more plumbing than\ndirectly related to your business domain--and building creative workarounds for the limitations of Go by exploiting the powerful\nfeatures Go *does* offer. Dependency management and error handling could be better, and the absence of generics can be rather painful\nwhen dealing with unknown schemas like [unmarshaling dynamic JSON](https://stackoverflow.com/questions/28877512/taking-a-json-string-unmarshaling-it-into-a-mapstringinterface-editing-an).\n\nSo it all depends on if the priorities of your application and project align with the priorities of the language and ecosystem\nyou choose. Despite the strong possibility that this opinion will subject me to ritual humiliation on social media, I would use\nScala (or a similarly featured language like Kotlin for microservices or bigger\nbut Go both to replace any [bash or Python scripts](https://www.quora.com/Where-do-we-use-Python-or-shell-scripts-in-the-DevOps-project-life-cycle)\nthat are part of my continuous delivery pipeline and\nto create [lambda functions](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html), which are supposed to be lightweight,\nfast, and focused. This means you may not necessarily have to choose because nontrivial cloud native architectures will often\nblend both microservices and lambdas, and you should *always* have a continuous delivery pipeline.\n\nI hope this helps. If you read the whole thing, you deserve a nap."
}