Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions 1-fp-library/src/main/scala/fplibrary/IO.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.github.sentenza.hacktoberfest.fplibrary

final case class IO[+A](unsafeRun: () => A) extends AnyVal

object IO {
// private type Thunk[A] = () => A
// type Description[A] = Thunk[A]

def create[A](a: => A): IO[A] =
IO(() => a)

implicit val M: Monad[IO] = new Monad[IO] {
final override def flatMap[A, B](ca: IO[A])(acb: A => IO[B]): IO[B] =
IO.create {
val a = ca.unsafeRun()
val db = acb(a)

val b = db.unsafeRun()

b
}
}
}
16 changes: 16 additions & 0 deletions 1-fp-library/src/main/scala/fplibrary/Monad.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.github.sentenza.hacktoberfest.fplibrary

trait Monad[C[_]] {
// We needed this helper method to compose functions
// def helper[A, B](ca: C[A], acb: A => C[B]): C[B] // From Helper to FlatMap

def flatMap[A, B](ca: C[A])(acb: A => C[B]): C[B] // Curried version of helper method
// haskell bind
@inline def bind[A, B](ca: C[A])(acb: A => C[B]): C[B] = flatMap(ca)(acb)
@inline def >>=[A, B](ca: C[A])(acb: A => C[B]): C[B] = flatMap(ca)(acb) // >>= Haskell Logo
}

// Type classes and implicitly
object Monad {
def apply[C[_]: Monad]: Monad[C] = implicitly[Monad[C]]
}
33 changes: 33 additions & 0 deletions 1-fp-library/src/main/scala/fplibrary/PointFree.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.github.sentenza.hacktoberfest.fplibrary

object PointFree {
// val ac = compose(ab, bc)
def compose[A, B, C](ab: A => B, bc: B => C): A => C = a => {
// taking A in and producing b
val b = ab(a)
// Going from B to C
val c = bc(b)
// Returning C
c
}

def composeKleisliOld[A, B, C](
adb: A => IO[B],
bdc: B => IO[C]
): A => IO[C] = a => {
val db: IO[B] = adb(a)
val b = db.unsafeRun()

val dc = bdc(b)

dc
}

// Kleisli Composition won't actually work without Monads (try to remove Monad from D[_]: Monad)
def composeKleisli[A, B, C, D[_]: Monad](adb: A => D[B], bdc: B => D[C]): A => D[C] = a => {
val db: D[B] = adb(a)
val dc = Monad[D].flatMap(db)(bdc)
dc
}

}
25 changes: 25 additions & 0 deletions 1-fp-library/src/main/scala/fplibrary/package.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.github.sentenza.hacktoberfest

package object fplibrary {
// private type Thunk[A] = () => A
// type Description[A] = Thunk[A]
private type RegularArrow[A, B] = A => B
private type KleisliArrow[A, B, C[_]] = A => C[B]

// Why are we extending AnyVal? You said performance, why?
// val ac = ab `;` bc
implicit final class InfixNotationForPointFree[A, B](private val ab: A => B) extends AnyVal {
// Alias: In Haskell compose is a simple dot
@inline def `.`[C](bc: B => C): A => C = PointFree.compose(ab, bc)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't this be simply

@inline def `.`[C](bc: B => C): A => C = bc.compose(ab)

?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd also be very careful with this choice, since the . is already integral part of the language syntax, the risk of creating confusion seems to me too high for the benefit gained.

Also considering that compose and andThen are already in the stdlib.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that it would be a better choice to use andThen and avoid the aliasing for dot. Again, this was just left behind as a trace of what I was trying to do.

// Let's also define an Arrow
@inline def -->[C](bc: B => C): A => C = PointFree.compose(ab, bc)
}

implicit final class InfixNotationForPointFreeKleisli[A, B, D[_]](private val adb: A => D[B])
extends AnyVal {
// Haskell Fish
@inline def >=>[C](bdc: B => D[C])(implicit M: Monad[D]): A => D[C] =
PointFree.composeKleisli(adb, bdc)
}

}
Empty file.
57 changes: 57 additions & 0 deletions 2-application-library/src/main/scala/application/Program.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package io.github.sentenza.hacktoberfest.application

import io.github.sentenza.hacktoberfest.fplibrary._

object Program {
// def createIO(args: Array[String]): IO[Unit] =
// () => displayMessages()

lazy val unreadableCreateIO: Array[String] => IO[Unit] = args =>
IO.create(
display(
hyphens(
display(
createMessage(
display(
hyphens(
args
)
)
)
)
)
)
)

// lazy val createDescription: Array[String] => IO[Unit] =
// ignoreArgs --> hyphens --> displayKleisli >=>
// question --> displayKleisli >=>
// promptKleisli >=>
// convertStringToInt --> ensureAmountIsPositive --> round --> createMessage --> displayKleisli >=>
// hyphens --> displayKleisli

// --> is equal to the std library "andThen"

// Format: OFF
lazy val createIO: Array[String] => IO[Unit] =
ignoreArgs --> hyphens --> displayKleisli >=>
createMessage --> displayKleisli >=>
hyphens --> displayKleisli
//Format: ON

// Transforming defs into functions - Old version had def
// The following values were actually defs and now we transformed them into functions
// Functions from A to B: A => B
// private def hyphens(input: Any): String = "\u2500" * 50
private lazy val hyphens: Any => String = _ => "\u2500" * 50
private lazy val createMessage: Any => String = _ => "Welcome to HacktoberFest Scala Algorhitms!"
private lazy val display: Any => Unit = input => println(input)
private lazy val prompt: Any => String = _ => scala.io.StdIn.readLine()
private lazy val ignoreArgs: Array[String] => Unit = _ => ()

// Kleisli
private lazy val displayKleisli: Any => IO[Unit] = input =>
IO.create(println(input))
private lazy val promptKleisli: Any => IO[String] = _ =>
IO.create(scala.io.StdIn.readLine())
}
42 changes: 42 additions & 0 deletions 3-end-of-the-world/src/main/scala/eotw/Interpreter.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package io.github.sentenza.hacktoberfest.eotw

import io.github.sentenza.hacktoberfest.fplibrary._
import io.github.sentenza.hacktoberfest.application.Program

object Interpreter {
def main(args: Array[String]): Unit = {
val description: IO[Unit] = Program.createIO(args)
def interpret[A](desc: IO[A]): A = desc.unsafeRun()
print(Console.YELLOW)
printDisclaimer()
print(Console.RED)
// <==== If you saw some red text you would know that something bad is going on ====>
print(Console.GREEN)
interpret(description)
print(Console.RESET)
}

private def printDisclaimer(): Unit = {
println(heading + gplDisclaimer)
}

private val heading =
"""
_ _ _ _ _ ______ _
| | | | | | | | | | | ___| | |
| |_| | __ _ ___| | _| |_ ___ | |__ ___ _ __| |_ ___ ___| |_
| _ |/ _` |/ __| |/ / __/ _ \| '_ \ / _ \ '__| _/ _ \/ __| __|
| | | | (_| | (__| <| || (_) | |_) | __/ | | || __/\__ \ |_
\_| |_/\__,_|\___|_|\_\\__\___/|_.__/ \___|_| \_| \___||___/\__|

"""

private val gplDisclaimer =
"""
HacktoberFest Scala Algorhitms Copyright (C) 2018-2019 @sentenza
This program comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
under certain conditions. All the details can be found at:
https://github.com/sentenza/hacktoberfest-scala-algorithms/blob/master/LICENSE.
"""
}
52 changes: 48 additions & 4 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,12 +1,56 @@
scalaVersion := "2.13.1"
name := "hacktoberfest-algorithms"
organization := "hacktoberfest"
ThisBuild / scalaVersion := "2.13.1"
ThisBuild / name := "hacktoberfest-algorithms"
ThisBuild / organization := "hacktoberfest"
// We will use Semver for this project
version := "0.11.0"
ThisBuild / version := "0.12.0"

// libraries
val scalaTestVersion = "3.0.8"
val ammoniteVersion = "1.7.4"

libraryDependencies += "org.scalatest" %% "scalatest" % scalaTestVersion % "test"
libraryDependencies += "com.lihaoyi" % "ammonite" % ammoniteVersion % "test" cross CrossVersion.full

// Fancy SBT functions
watchTriggeredMessage := Watch.clearScreenOnTrigger
shellPrompt := (_ => fancyPrompt(name.value))

def cyan(projectName: String): String =
scala.Console.CYAN + projectName + scala.Console.RESET

def fancyPrompt(projectName: String): String =
s"""|
|[info] Welcome to the ${cyan(projectName)} project!
|sbt> """.stripMargin

// Projects
lazy val `fp-library` =
project
.in(file("./1-fp-library"))
.settings(
shellPrompt := (_ => fancyPrompt(name.value))
)

lazy val `application-library` =
project
.in(file("./2-application-library"))
.settings(shellPrompt := (_ => fancyPrompt(name.value)))
.dependsOn(`fp-library`)

// This project should contain just the main method
lazy val `end-of-the-world` =
project
.in(file("./3-end-of-the-world"))
.settings(shellPrompt := (_ => fancyPrompt(name.value)))
.dependsOn(`application-library`)

// Command Aliases
addCommandAlias("ll", "projects")

addCommandAlias("cd", "project")

addCommandAlias("lib", "project fp-library")

addCommandAlias("app", "project application-library")

addCommandAlias("eow", "project end-of-the-world")
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=1.3.2
sbt.version=1.3.3
Loading