Scala notes

Monads, for comprehension, flatMaps

 

Definitions

The for comprehension is a syntax shortcut to combine flatMap and map in a way that's easy to read and reason about.

Let's simplify things a bit and assume that every class that provides both aforementioned methods can be called a monad and we'll use the symbol M[A] to mean a monad with an inner type A.

Examples

Some commonly seen monads

  • List[String] where
    • M[_]: List[_]
    • A: String
  • Option[Int] where
    • M[_]: Option[_]
    • A: Int
  • Future[String => Boolean] where
    • M[_]: Future[_]
    • A: String => Boolean

map and flatMap

Defined in a generic monad M[A]

/* applies a transformation of the monad "content" mantaining the 
  * monad "external shape"  
  * i.e. a List remains a List and an Option remains an Option 
  * but the inner type changes
  */
  def map(f: A => B): M[B] 

 /* applies a transformation of the monad "content" by composing
  * this monad with an operation resulting in another monad instance 
  * of the same type
  */
  def flatMap(f: A => M[B]): M[B]

Examples:

  val list = List("neo", "smith", "trinity")

  //converts each character of the string to its corresponding code
  val f: String => List[Int] = s => s.map(_.toInt).toList 

  list map f
  >> List(List(110, 101, 111), List(115, 109, 105, 116, 104), List(116, 114, 105, 110, 105, 116, 121))

  list flatMap f
  >> List(110, 101, 111, 115, 109, 105, 116, 104, 116, 114, 105, 110, 105, 116, 121)

for expression

1. Each line in the expression using the <- symbol is translated to a flatMap call, except for the last line which is translated to a concluding map call, where the "bound symbol" on the left-hand side is passed as the parameter to the argument function (what we previously called f: A => M[B]):

// The following ...
for {
  bound <- list
  out <- f(bound)
} yield out

// ... is translated by the Scala compiler as ...
list.flatMap { bound =>
  f(bound).map { out =>
    out
  }
}

// ... which can be simplified as ...
list.flatMap { bound =>
  f(bound)
}

// ... which is just another way of writing:
list flatMap f

2. A for-expression with only one <- is converted to a map call with the expression passed as argument:

// The following ...
for {
  bound <- list
} yield f(bound)

// ... is translated by the Scala compiler as ...
list.map { bound =>
  f(bound)
}

// ... which is just another way of writing:
list map f

Now to the point

As you can see, the map operation preserves the "shape" of the original monad, so the same happens for the yield expression: a List remains a List with the content transformed by the operation in the yield

On the other hand each binding line in the for is just a composition of successive monads, which must be "flattened" in order to maintain a single "external shape".

Suppose for a moment that each internal binding was translated to a map call, but the right-hand was the same A => M[B] function, you would end up with a M[M[B]] for each line in the comprehension.

The intent of the whole for syntax is to easily "flatten" the concatenation of successive monadic operations (i.e. operations that "lift" a value in a "monadic shape": A => M[B]), with the addition of a final map operation that possibly performs a concluding transformation.

I hope this explains the logic behind the choice of translation, which is applied in a mechanical way, that is: n flatMap nested calls concluded by a single map call.

A contrived illustrative example

Meant to show the expressiveness of the for syntax
case class Customer(value: Int)
case class Consultant(portfolio: List[Customer])
case class Branch(consultants: List[Consultant])
case class Company(branches: List[Branch])

def getCompanyValue(company: Company): Int = {

  val valuesList = for {
    branch     <- company.branches
    consultant <- branch.consultants
    customer   <- consultant.portfolio
  } yield (customer.value)

  valueList reduce (_ + _)
}

As already said, the shape of the monad is mantained through the comprehension, so we start with a List in company.branches, and must end with a List.

The inner type instead changes and is determined by the yield expression: which is customer.value: Int

valueList should be a List[Int]



Access sender actor in injected children

 

When you have injected children in an injected parent actor, you might get deadLetters when trying to access the sender from inside the created children.

This is because the sender of the message gets picked up via an implicit parameter.

The signature is:

def !(message: Any)(implicit sender: ActorRef = Actor.noSender): Unit

The sender gets picked up from this implicit parameter. When sending a message from within the parent actor, this parameter is set to the default value: Actor.noSender which translates to deadLetters when accessing sender from within the children.

In order to have access to the sender inside the child, we can use tell :

final def tell(msg: Any, sender: ActorRef): Unit = this.!(msg)(sender)

This way, sender can be correctly set and passed to the child actor.

Extract case class fields

 

A "simple" way of extracting the fields from any case class:

def extractFieldNames[T<:Any](implicit m: Manifest[T]) = {
implicitly[Manifest[T]].runtimeClass.getDeclaredFields.map(_.getName).filter(!_.equals("$outer"))
}


The Eight Fallacies of Distributed Computing

 

The fallacies of distributed computing are a set of assertions made by L Peter Deutsch and others at Sun Microsystems describing false assumptions that programmers new to distributed applications invariably make.

These result either in the failure of the system, a substantial reduction in system scope, or in large, unplanned expenses required to redesign the system to meet its original goals.

  1. the network is reliable
  2. latency is zero
  3. bandwidth is infinite
  4. the network is secure
  5. topology doesn't change
  6. there is one administrator
  7. transport cost is zero
  8. the network is homogeneous

Source: https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing

Fix docker connectivity problem

 

Problem

Docker images aparently have no connectivity. Issue resides with the dns resolvers.
Looks like when using ips, everything works; when using names, it doesn't work.

Two ways of doing this...

Option #1: disable dnsmasq

Edit the file: /etc/NetworkManager/NetworkManager.conf

Comment the line containing dns=dnsmasq.

Restart networking: sudo systemctl restart network-manager

Restart docker: sudo systemctl restart docker

Option #2: set the ip of the docker bridge with systemd

On elementary loki (ubuntu 16.04), docker configuration file is here: /lib/systemd/system/docker.service

Edit this config file and add to ExecStart like this: ExecStart=/usr/bin/docker daemon -H fd:// --bip=192.168.169.1/24

Run: sudo systemctl daemon-reload

Stop docker service: systemctl stop docker

Install brctl to control the bridge: sudo apt-get install bridge-utils

Bring down the bridge: sudo ip link set docker0 down

Delete the bridge: sudo brctl delbr docker0

Restart docker: systemctl start docker


Option #2 did not work for me. Option #1 did.

Source and more details: http://stackoverflow.com/a/35519951/3004463

Git step by step

 

Git basics

Before all this, we must have ssh key added to gitlab / github

Check username and email
git config user.name
git config user.email

Set username and email
git config user.name Someone
git config user.email someone@something.com
Use option --global to set it globally, for all future projects

Go to folder where we want to init the repo
git init #initializez a git repo

Add a new remote to be used.
For remote_name `origin` is usually used; url is taken from gitlab/github page
git remote add <remote_name> <url>

Fetch everything (all branches) from remote
git fetch

Change to another branch
git checkout <branch_name>

Update a branch - usually done after moving to it
git pull <remote_name> <branch_name>
If already on a branch, short version can be used
git pull

Add new/modified files to be commited
git add <relative_path/to/file/or/folder>

Commit added files
git commit -m "Commit message"

Push changes
git push -u <remote_name> <branch_name>


Commit to new branch, with issue fix and ready for merge request

Change necessary files on the master branch (for example, since everything should be relative to master)

Create new branch
git checkout -b <new_branch_name>
This will create the new branch, add everything to it and move to the new branch

Add changes to be commited
git add relative/path/to/files

Commit changes on the new branch, specifying what issue this commit fixes
git commit -m "Commit message. Fixes#<issue_number>"

Push everything (the new branch, changes, everything)
git push -u <remote_name> <new_branch_name>

Now go to github / gitlab and create a merge request.

Docker basics

 

List current local images

docker images

List current processes (containers, running or stopped)

docker ps -a

Stop and remove all running or stopped containers

docker ps -aq | xargs docker stop && docker ps -aq | xargs docker rm

Build image from Dockerfile from current folder, specifying a custom image tag

docker build . -t <image_tag>

Create a network

docker network create -d <network_type> --subnet x.x.x.x/mask --ip-range x.x.x.x/mask <network_name>

Example: docker network create -b bridge --subnet 192.168.10.0/28 --ip-range 192.168.10.1/29 net1

Running a container from an image

docker run [OPTIONS] <image_name> [COMMAND] [ARG...]

Where OPTIONS can be:

  • -i - interactive, keep stdin open even if not attached
  • -t - allocate a pseudo tty
  • -d - detach, run dontainer in background and print container id
  • -h <host_name> - container host name string
  • -p host_port:container_port - publish container's ip to host; host will forward requests to the container on the specified port; can be used multiple times
  • --network <network_name> - specify a custom network to run this container in
  • --name <custom_container_name> - assign a name to this container
  • --ip <set_ip_value> - assign a fixed ip value to the docker container

Example:

docker run -d --network net1 -p 9000:9000 --name my_image -h my_image_host my_local_image

Retrieve a running container's ip address

docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}} {{end}}' <container_name>

docker inspect --format '{{ .NetworkSettings.IPAddress }}' <container_name>

Extract method and parameters - play framework

 

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RouteExtractor {

//something like "/foo/$id<[^/]+>/edit/$james<[^/]+>"
private String routePattern;
private String path;

//something like /$id<[^/]+>
private static final String INDEX_PATTERN = "\\$(.+?)<\\[\\^/]\\+>";

public RouteExtractor(String routePattern, String path) {

this.routePattern = routePattern;
this.path = path;
}

private Map<Integer, String> extractPositions() {
Pattern pattern = Pattern.compile(INDEX_PATTERN);
Matcher matcher = pattern.matcher(this.routePattern);
Map<Integer, String> results = new HashMap<>();
int index = 0;
while (matcher.find()) {
results.put(index++, matcher.group(1));
}
return results;
}

private String replaceRoutePatternWithGroup() {
Pattern pattern = Pattern.compile(INDEX_PATTERN);
Matcher matcher = pattern.matcher(this.routePattern);
return matcher.replaceAll("([^/]+)");
}

public Map<String, String> extract() {
Pattern pattern = Pattern.compile(this.replaceRoutePatternWithGroup());
Matcher matcher = pattern.matcher(this.path);
final Map<String, String> results = new HashMap<>();
if (matcher.find()) {
this.extractPositions().forEach((key, value) -> results.put(value, matcher.group(key + 1)));
}
return results;
}

}


Source here.

How to generate random alphanumeric strings

 

Here is a small code for generating random alphanumeric strings of specified length:


import java.math.BigInteger;
import java.security.SecureRandom;

public class KeyGenerator {

private static final SecureRandom secureRandom = new SecureRandom();

public static String generate(int stringLength) {
return new BigInteger(stringLength * 5, secureRandom).toString(32);
}
}


This works by choosing a multiple of 5 bits from a cryptographically secure random bit generator and encoding them in base-32.

Each digit in a base-32 number can encode 5 bits. So the multiplier would give the number of resulting characters.

This encoding is compact and efficient, with 5 random bits per character. Compare this to a random UUID, which only has 3.4 bits per character in standard layout, and only 122 random bits in total.

Note that SecureRandom objects are expensive to initialize so you'll want to keep one around and reuse it.

Based on this answer.