Announce: learn-fp - functional programming course/tutorial

Hi.
I would like to announce learn-by-doing course/tutorial I have created.
You can find it at https://github.com/dehun/learn-fp
Course is test-driven - you have stubs which you have to implement and pass all unit tests.
It is something like write yourself mini scalaz/cats.

It covers type classes, functors, applicatives, monads, foldable/traversable, monad transformers, free monads.
I am working on adding co-functors and co-monads right now.

It was heavily inspired by NICTA course(haskell) - I thought that Scala community will benefit from something like that but on Scala.

I would appreciate any feedback. PRs and suggestions are very welcome.

Thank you.

1 Like

Added co-functors and co-monads

Looks like decent work. Congrats.

Btw: I am persuing a sligtly different path by writing a tutorial on arrow-like code (promoting point-free programming) that closely follows FP of John Backus. Monadic programming is pointful. I claim that Scala (I use Dotty) comes to the rescue to spice the pointfree programming style with a convenient programming DSL.

see Program Description Based Programming

Cheers

1 Like

Hello,

You asked for feedback.

I looked at the type class related code.

A few remarks

code like

object TypeClassUser {
  def foo[A](x:A)(implicit typeClass:TypeClass[A]):String  = {
    typeClass.foo(x)
  }
}

can be replaced by (imho more idiomatic code)

object TypeClassUser {
  def foo[A: TypeClass](x: A): String = {
    implicitly[TypeClass[A]].foo(x)
  }
}

and code like

object Eq {
  def eq[A](lhs:A, rhs:A)(implicit eqt:Eq[A]) = eqt.eq(lhs, rhs)
}

class EqOps[A](lhs:A)(implicit eqt:Eq[A]) {
  def ====(rhs:A):Boolean = eqt.eq(lhs, rhs)
}

object EqOps {
  implicit def toEqOps[A](lhs:A)(implicit eqt:Eq[A]) = new EqOps(lhs)
}

can be replaced by (imho more idiomatic code)

object Eq {
  def eq[A: Eq](lhs:A, rhs:A) = implicitly[Eq[A]].eq(lhs, rhs)
}

object Ops {
  implicit class EqOps[A: Eq](lhs:A) {
    def ====(rhs:A):Boolean = implicitly[Eq[A]].eq(lhs, rhs)
  }
}

Here is another remark (after reading the Monoid stuff):

It is worth looking at ScalaCheck (cfr QuickCheck in Haskell)

so you can write stuff like

import org.scalacheck.Arbitrary.arbitrary
import org.scalacheck.Arbitrary
import org.scalacheck.Prop.forAll

class MonoidLaws[M: Monoid] {
  import Ops._

  def associativityLaw(implicit iam: Arbitrary[M]) =
    forAll {
      (m1: M, m2: M, m3: M) =>
        val lhs = (m1 |+| m2) |+| m3
        val rhs = m1 |+| (m2 |+| m3)
        lhs == rhs
    }

}

import org.scalatest.FlatSpec

class SimpleMonoidCheck extends FlatSpec with Matchers {

  import SimpleMonoid._

  implicit def arbitrarySum: Arbitrary[Sum] =
    Arbitrary {
      for {
        ai <- arbitrary[Int]
      } yield Sum(ai)
    }

  object sumMonoidLaws extends MonoidLaws[Sum]

  import sumMonoidLaws.associativityLaw

  "sum monoid associativity laws" should "pass 100 tests" in {

    associativityLaw.check
    
  }

}

you have to add

libraryDependencies += "org.scalacheck" %% "scalacheck" % "1.13.4"

to build.sbt

Personality, I don’t see any advantages in passing the implicit as a bound and then summoning it again with implicitly, rather than directly as a named implicit parameter.

My own rule of thumb is to use the bound notation if and only if you only ever use it implicitly.

2 Likes