Skip to content

Instantly share code, notes, and snippets.

@softprops
Created March 16, 2011 06:05
Show Gist options
  • Save softprops/872086 to your computer and use it in GitHub Desktop.
Save softprops/872086 to your computer and use it in GitHub Desktop.
a.md

towards composibility

language is representation (data structures) and combination (control structures)

goals of a language: simplification => easy to express "what" it is you're trying to do

simplification => generalization

towards composability

goals of OOP

  • compartmentalize code into reusable components
  • accomplishes with state (mutable!) + behavior

goals of FP

  • generalize behavior into reusable functions and apply state
  • accomplishes with stateA -> behaviorA -> behaviorB -> behaviorC -> stateB
  • to be composible, everything should return a value

the real world often difficult to model in strict FP

scala provides both FP (preferred) mixed with OOP (+ some)

map flatmap (filter) & for

for is a generalized (unified) control structure for combinator function application

<- == in == :

map

  • applies a function to every element of a given type and produces an instance of new type for each element

  • n elements => n resulting values

times 2

1a) map "how"

var res: List[Int] = Nil
for(x <- List.range(0,4)) {
  res = x * 2 :: res
}
res.reverse // we appended the values incrementally

1b) map "what"

List.range(0,4) map(x => x * 2)

1b) map "generalized"

for(x <- List.range(0,4)) yield x * 2

flatmap (flattened map)

  • applies a function to every element of a given type and produces an new instance of the given type whose applied values will be collapsed (flattened)

  • result binding

  • n elements => m values

...

2a) flatmap "how"

var res: List[Int] = Nil
for(x <- List.range(0,4)) {
  res = x :: res
}
var res2: List[(Int, Int)] = Nil // (elements of res, element of another list)
for(x <- res.reverse) { // dont forget to reverse
  for(y <- List.range(5,10)) {
    res2 = (x, y) :: res2
  }
}
res2.reverse

2b) flatmap "what" List.range(0,4) flatMap { x => List.range(5, 10) map { y => (x, y) } }

2c) flatmap "generalized"

for(
  x <- List.range(0, 4)
  y <- List.range(5, 10)
 ) yield (x, y)

if only...

filter

  • function predicate for excluding elements of a type A => Boolean

3a) flatmap with filter "how"

var res: List[Int] = Nil
for(x <- List.range(0,4)) {
  res = x :: res
}
var res2: List[(Int, Int)] = Nil // (result of res * 2, value of y)
for(x <- res.reverse) { // dont forget to reverse
  for(y <- List.range(5,10)) {
    if(x % 2 == 0) res2 = (x, y) :: res2
  }
}
res2.reverse

3b) flatmap with filter "what"

List.range(0,4) filter(x => x % 2 == 0) flatMap { x =>
   List.range(5, 10) map { y =>
     (x, y)
   }
}

3c) flatmap with filter "generalized"

for(
  x <- List.range(0, 4)
  if(x % 2  == 0)
  y <- List.range(5, 10)
) yield (x, y)

"towards composibilty"

for is your instrument for composing behavior

generalizing function application

generalized contract for interacting with for

trait M[A] {
  def map[B](f: A => B): M[B] 
  def flatMap[B](f: A => M[B]): M[B]
  def filter(f: A => Boolean): M[A]
  // def foreach(f: A => Unit) // mutation
}

a very similar fp design pattern is called a monad. (see also haskell's do)

for is packaging function composition in a concise dsl

bonus

implement map in terms of fold

implicit def l2ml[A](l: List[A]) = new {
  def fmap[B](f: A => B) = ((Nil:List[B]) /: l)((a,e) =>
    f(e) :: a
  ) reverse
}

List(1, 2, 3) fmap(_ * 2) // List(2, 4, 6)
List(1, 2, 3) map(_ * 2)  // List(2, 4, 6)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment