-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Jonathon Belotti
committed
Jul 19, 2021
1 parent
26f0733
commit 55dd853
Showing
5 changed files
with
263 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,3 +19,4 @@ additional_languages: | |
python | ||
# scala | ||
# typescript | ||
scala |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
books/fp_in_scala/src/main/scala/fpinscala/gettingstarted/BUILD.bazel
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
load("@io_bazel_rules_scala//scala:scala.bzl", "scala_library", "scala_binary", "scala_repl") | ||
|
||
scala_library( | ||
name = "gettingstarted", | ||
srcs = ["GettingStarted.scala"] | ||
) | ||
|
||
scala_binary( | ||
name = "MyModule", | ||
main_class = "fpinscala.gettingstarted.MyModule", | ||
srcs = ["GettingStarted.scala"], | ||
) | ||
|
||
scala_binary( | ||
name = "FormatAbsAndFactorial", | ||
main_class = "fpinscala.gettingstarted.FormatAbsAndFactorial", | ||
srcs = ["GettingStarted.scala"], | ||
) | ||
|
||
# Exercise 2.1 | ||
scala_binary( | ||
name = "TestFib", | ||
main_class = "fpinscala.gettingstarted.TestFib", | ||
srcs = ["GettingStarted.scala"], | ||
) | ||
|
||
|
||
scala_repl( | ||
name = "repl", | ||
deps = [ | ||
":gettingstarted", | ||
] | ||
) |
192 changes: 192 additions & 0 deletions
192
books/fp_in_scala/src/main/scala/fpinscala/gettingstarted/GettingStarted.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
package fpinscala.gettingstarted | ||
|
||
// A comment! Yay | ||
/* Another comment */ | ||
/** A documentation comment */ | ||
object MyModule { | ||
def abs(n: Int): Int = | ||
if (n < 0) -n | ||
else n | ||
|
||
private def formatAbs(x: Int) = { | ||
val msg = "The absolute value of %d is %d" | ||
msg.format(x, abs(x)) | ||
} | ||
|
||
def main(args: Array[String]): Unit = | ||
println(formatAbs(-42)) | ||
|
||
// A definition of factorial, using a local, tail recursive function | ||
def factorial(n: Int): Int = { | ||
@annotation.tailrec | ||
def go(n: Int, acc: Int): Int = | ||
if (n <= 0) acc | ||
else go(n-1, n*acc) | ||
|
||
go(n, 1) | ||
} | ||
|
||
// Another implementation of `factorial`, this time with a `while` loop | ||
def factorial2(n: Int): Int = { | ||
var acc = 1 | ||
var i = n | ||
while (i > 0) { acc *= i; i -= 1 } | ||
acc | ||
} | ||
|
||
// Exercise 2.1: Write a function to get the nth Fibonacci number | ||
// The nth number is always the sum of the previous two. | ||
def fib(n: Int): Int = { | ||
@annotation.tailrec | ||
def go(n: Int, previous: Int, current: Int): Int = { | ||
if (n <= 0) previous | ||
else go(n - 1, current, previous + current) | ||
} | ||
go(n, 0, 1) | ||
} | ||
|
||
// This definition and `formatAbs` are very similar.. | ||
private def formatFactorial(n: Int) = { | ||
val msg = "The factorial of %d is %d." | ||
msg.format(n, factorial(n)) | ||
} | ||
|
||
// We can generalize `formatAbs` and `formatFactorial` to | ||
// accept a _function_ as a parameter | ||
def formatResult(name: String, n: Int, f: Int => Int) = { | ||
val msg = "The %s of %d is %d." | ||
msg.format(name, n, f(n)) | ||
} | ||
} | ||
|
||
object FormatAbsAndFactorial { | ||
|
||
import MyModule._ | ||
|
||
// Now we can use our general `formatResult` function | ||
// with both `abs` and `factorial` | ||
def main(args: Array[String]): Unit = { | ||
println(formatResult("absolute value", -42, abs)) | ||
println(formatResult("factorial", 7, factorial)) | ||
} | ||
} | ||
|
||
object TestFib { | ||
|
||
import MyModule._ | ||
|
||
// test implementation of `fib` | ||
def main(args: Array[String]): Unit = { | ||
println("Expected: 0, 1, 1, 2, 3, 5, 8") | ||
println("Actual: %d, %d, %d, %d, %d, %d, %d".format(fib(0), fib(1), fib(2), fib(3), fib(4), fib(5), fib(6))) | ||
} | ||
} | ||
|
||
// Functions get passed around so often in FP that it's | ||
// convenient to have syntax for constructing a function | ||
// *without* having to give it a name | ||
object AnonymousFunctions { | ||
import MyModule._ | ||
|
||
// Some examples of anonymous functions: | ||
def main(args: Array[String]): Unit = { | ||
println(formatResult("absolute value", -42, abs)) | ||
println(formatResult("factorial", 7, factorial)) | ||
println(formatResult("increment", 7, (x: Int) => x + 1)) | ||
println(formatResult("increment2", 7, (x) => x + 1)) | ||
println(formatResult("increment3", 7, x => x + 1)) | ||
println(formatResult("increment4", 7, _ + 1)) | ||
println(formatResult("increment5", 7, x => { val r = x + 1; r })) | ||
} | ||
} | ||
|
||
object MonomorphicBinarySearch { | ||
|
||
// First, a binary search implementation, specialized to `Double`, | ||
// another primitive type in Scala, representing 64-bit floating | ||
// point numbers | ||
// Ideally, we could generalize this to work for any `Array` type, | ||
// so long as we have some way of comparing elements of the `Array` | ||
def binarySearch(ds: Array[Double], key: Double): Int = { | ||
@annotation.tailrec | ||
def go(low: Int, mid: Int, high: Int): Int = { | ||
if (low > high) -mid - 1 | ||
else { | ||
val mid2 = (low + high) / 2 | ||
val d = ds(mid2) // We index into an array using the same | ||
// syntax as function application | ||
if (d == key) mid2 | ||
else if (d > key) go(low, mid2, mid2-1) | ||
else go(mid2 + 1, mid2, high) | ||
} | ||
} | ||
go(0, 0, ds.length - 1) | ||
} | ||
|
||
} | ||
|
||
object PolymorphicFunctions { | ||
|
||
// Here's a polymorphic version of `binarySearch`, parameterized on | ||
// a function for testing whether an `A` is greater than another `A`. | ||
def binarySearch[A](as: Array[A], key: A, gt: (A,A) => Boolean): Int = { | ||
@annotation.tailrec | ||
def go(low: Int, mid: Int, high: Int): Int = { | ||
if (low > high) -mid - 1 | ||
else { | ||
val mid2 = (low + high) / 2 | ||
val a = as(mid2) | ||
val greater = gt(a, key) | ||
if (!greater && !gt(key,a)) mid2 | ||
else if (greater) go(low, mid2, mid2-1) | ||
else go(mid2 + 1, mid2, high) | ||
} | ||
} | ||
go(0, 0, as.length - 1) | ||
} | ||
|
||
// Exercise 2.2: Implement `isSorted`, which checks whether an Array[A] | ||
// is sorted according to a given comparison function: | ||
def isSorted[A](as: Array[A], gt: (A,A) => Boolean): Boolean = { | ||
def go(i: Int, hi: Int): Boolean = { | ||
if (i > hi) true | ||
else if (!gt(as(i), as(i-1))) false | ||
else go(i+1, hi) | ||
} | ||
|
||
go(1, as.length - 1) | ||
} | ||
|
||
// Polymorphic functions are often so constrained by their type | ||
// that they only have one implementation! Here's an example: | ||
|
||
def partial1[A,B,C](a: A, f: (A,B) => C): B => C = | ||
(b: B) => f(a, b) | ||
|
||
// Exercise 3: Implement `curry`. | ||
|
||
// Note that `=>` associates to the right, so we could | ||
// write the return type as `A => B => C` | ||
def curry[A,B,C](f: (A, B) => C): A => (B => C) = | ||
??? | ||
|
||
// NB: The `Function2` trait has a `curried` method already | ||
|
||
// Exercise 4: Implement `uncurry` | ||
def uncurry[A,B,C](f: A => B => C): (A, B) => C = | ||
??? | ||
|
||
/* | ||
NB: There is a method on the `Function` object in the standard library, | ||
`Function.uncurried` that you can use for uncurrying. | ||
Note that we can go back and forth between the two forms. We can curry | ||
and uncurry and the two forms are in some sense "the same". In FP jargon, | ||
we say that they are _isomorphic_ ("iso" = same; "morphe" = shape, form), | ||
a term we inherit from category theory. | ||
*/ | ||
|
||
// Exercise 5: Implement `compose` | ||
|
||
def compose[A,B,C](f: B => C, g: A => B): A => C = | ||
??? | ||
} |
14 changes: 14 additions & 0 deletions
14
books/fp_in_scala/src/main/scala/fpinscala/gettingstarted/repl.sh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
#!/usr/bin/env bash | ||
|
||
set -euo pipefail | ||
|
||
REPO_ROOT="$(git rev-parse --show-toplevel)" | ||
|
||
main() { | ||
pushd "$REPO_ROOT" | ||
bazel build //books/fp_in_scala/src/main/scala/fpinscala/gettingstarted:repl | ||
./bazel-bin/books/fp_in_scala/src/main/scala/fpinscala/gettingstarted/repl | ||
popd "$REPO_ROOT" | ||
} | ||
|
||
main "$@" |