Skip to content

Commit

Permalink
Merge pull request #96 from Kevin-Lee/rc
Browse files Browse the repository at this point in the history
just-fp v1.3.0
  • Loading branch information
kevin-lee authored Oct 13, 2019
2 parents af489ab + 314183c commit 575c845
Show file tree
Hide file tree
Showing 19 changed files with 420 additions and 32 deletions.
4 changes: 2 additions & 2 deletions .build-scripts/build-project-simple.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ else
echo "Build projects"
echo "--------------------------------------------"
echo ""
if [[ "$BRANCH_NAME" == "rc" ]]
if [[ "$BRANCH_NAME" == "master" || "$BRANCH_NAME" == "release" ]]
then
sbt -d -J-Xmx2048m "; ++ ${scala_version}!; clean; test"
sbt -d -J-Xmx2048m "; ++ ${scala_version}!; packageBin; packageSrc; packageDoc"
sbt -d -J-Xmx2048m "; ++ ${scala_version}!; packagedArtifacts"
else
sbt -d -J-Xmx2048m "; ++ ${scala_version}!; clean; test; package"
fi
Expand Down
4 changes: 2 additions & 2 deletions .build-scripts/build-project.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ else
echo "Build projects"
echo "--------------------------------------------"
echo ""
if [[ "$BRANCH_NAME" == "rc" ]]
if [[ "$BRANCH_NAME" == "master" || "$BRANCH_NAME" == "release" ]]
then
sbt -d -J-Xmx2048m "; ++ ${scala_version}!; clean; coverage; test; coverageReport; coverageAggregate"
sbt -d -J-Xmx2048m "; ++ ${scala_version}!; packageBin; packageSrc; packageDoc"
sbt -d -J-Xmx2048m "; ++ ${scala_version}!; packagedArtifacts"
else
sbt -d -J-Xmx2048m "; ++ ${scala_version}!; clean; coverage; test; coverageReport; coverageAggregate; package"
fi
Expand Down
190 changes: 188 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,203 @@
[![Build Status](https://semaphoreci.com/api/v1/kevin-lee/just-fp/branches/master/badge.svg)](https://semaphoreci.com/kevin-lee/just-fp)
[![Download](https://api.bintray.com/packages/kevinlee/maven/just-fp/images/download.svg)](https://bintray.com/kevinlee/maven/just-fp/_latestVersion)

A small Functional Programming library. This is not meant to be an alternative to Scalaz or Cats. The reason for having this library is that in your project you don't want to have Scalaz or Cats as its dependency and you only need much smaller set of functional programming features than what Scalaz or Cats offers. So the users of your library can choose Scalaz or Cats to be used with your library.

# Getting Started
In `build.sbt`,

```sbt
resolvers += "3rd Party Repo" at "https://dl.bintray.com/kevinlee/maven"
resolvers += "Just Repo" at "https://dl.bintray.com/kevinlee/maven"

libraryDependencies += "kevinlee" %% "just-fp" % "1.2.0"
libraryDependencies += "kevinlee" %% "just-fp" % "1.3.0"
```
then import

```scala
import just.fp._
import just.fp.syntax._
```
or
```scala
import just.fp._, syntax._
```
In all the example code using `just-fp` below, I assume that you've already imported `just-fp` at the top.

# Either

## Right-Biased `Either`
`Either` in Scala prior to 2.12 is not right-biased meaning that you have to call `Either.right` all the time if you want to use it with `for-comprehension`.

e.g.) Before 2.12
```scala
for {
b <- methodReturningEither(a).right
c <- anotherReturningEither(b).right
} yield c
```
If you use `just-fp`, it becomes
```scala
for {
b <- methodReturningEither(a)
c <- anotherReturningEither(b)
} yield c
```
Of course, you don't need to do it if you use Scala 2.12 or higher.

## Either Constructors
In normal ways, if you want to create `Left` or `Right`, you just use the `apply` methods of their companion objects (i.e. `Left()` `Right()`) A problem with this is that what these return is not `Either` but its data, `Left` or `Right`.

You also need to specify not only type parameter for `Left` but also the one for `Right` when creating `Right`.

e.g.) Without type parameters,
```scala
Right(1)
// Right[Nothing, Int] = Right(1)
```
You don't want to have `Nothing` there. So do it with type parameters,
```scala
Right[String, Int](1)
// Right[String, Int] = Right(1)
```
So it becomes unnecessarily verbose. Right should be inferred as the compiler knows it already yet to specify the left one, you have to put both left and right parameters.

`Left`, of course, has the same problem.

```scala
Left("error")
// Left[String, Nothing] = Left("error")
```
```scala
Left[String, Int]("error")
// Left[String, Int] = Left("error")
```

Now with `just-fp`, it's simpler. You can use use `left` and `right` constructors as extension methods to the actual data values with only missing type info specified.

e.g.)
```scala
1.right[String] // Now you only need to specify
// the missing type info only
// that is Left type parameter.
// Either[String, Int] = Right(1)
```
For `Left`,
```scala
"error".left[Int]
// Either[String, Int] = Left("error")
```

## `leftMap` and `leftFlatMap`
So if you Scala 2.12 or higher or `just-fp` with the older Scala, `Either` is right-biassed. Then what about the `Left` case? Can I ever use `Left` for something useful like transforming the `Left` value to something else?

For that, `just-fp` has added `leftMap` and `leftFlatMap` to `Either`.
e.g.)
```scala
for {
b <- f1(a).leftMap(AppError.calculatioError)
c <- f2(b).leftMap(AppError.fromComputeError)
} yield c
// f1 returns Either[String, Int]
// f2 returns Either[ComputeError, Int]
// The result is Either[AppError, Int]
```

# Option
## Option Constructors
Similar to `Either`, creating `Option` can expose its data instead of type.

e.g.) The following code returns `Some[Int]` not `Option[Int]`.
```scala
Some(1)
// Some[Int] = Some(1)
```

```scala
None
// None.type = None
// Also None is None not Option[A]
```

With `just-fp`,
```scala
1.some
// Option[Int] = Some(1)

none[String]
// Option[String] = None
```

# Type-safe Equal
`==` in Scala is not type safe so the following code can never be `true` as their types are different but it has no compile-time error.

```scala
1 == "1" // always false, no compile-time error
```

`just-fp` has `Equal` typeclass with typeclass instances for value types (`Byte`, `Short`, `Int`, `Char`, `Long`, `Float` and `Double`) as well as `String`, `BigInt` and `BigDecimal`.

It also has `Equal` typeclass instances for `WriterT`, `Writer` and `EitherT`.

With `just-fp`,
```scala
// use triple equals from just-fp
1 === "1" // compile-time error
1 === 1 // true
"a" === "a" // true
1 !== 1 // false
1 !== 2 // true
```

If it's a `case class` the `equals` of which can be used for equality check, `NatualEqual` can be used.

e.g.)
```scala
case class Foo(n: Int)
```
```scala
Foo(1) === Foo(1)
^
error: value === is not a member of Foo
```
This can be solved by `NatualEqual`.

```scala
case class Foo(n: Int)

object Foo {
implicit val eqaul: Equal[Foo] = Equal.equalA[Foo]
}

Foo(1) === Foo(1)
// Boolean = true

Foo(1) === Foo(2)
// Boolean = false

Foo(1) !== Foo(1)
// Boolean = false

Foo(1) !== Foo(2)
// Boolean = true
```

# Semi-Group
// To be updated ...

# Monoid
// To be updated ...

# Functor
// To be updated ...

# Applicative
// To be updated ...

# Monad
// To be updated ...

# WriterT / Writer
// To be updated ...

# EitherT
// To be updated ...
31 changes: 18 additions & 13 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,32 @@ lazy val justFp = (project in file("."))
else
Seq(sharedSourceDir / "scala-2.10_2.11")
}
, resolvers += Resolver.sonatypeRepo("releases")
, addCompilerPlugin("org.typelevel" % "kind-projector" % "0.10.3" cross CrossVersion.binary)
, wartremoverErrors in (Compile, compile) ++= commonWarts
, wartremoverErrors in (Test, compile) ++= commonWarts
, resolvers += Deps.hedgehogRepo
, libraryDependencies ++= Deps.hedgehogLibs
, dependencyOverrides ++= crossVersionProps(Seq.empty[ModuleID], SemanticVersion.parseUnsafe(scalaVersion.value)) {
case (Major(2), Minor(10)) =>
Seq("org.wartremover" %% "wartremover" % "2.3.7")
case x =>
Seq.empty
}
, resolvers ++= Seq(
Resolver.sonatypeRepo("releases")
, Deps.hedgehogRepo
)
, addCompilerPlugin("org.typelevel" % "kind-projector" % "0.11.0" cross CrossVersion.full)
, libraryDependencies := Deps.hedgehogLibs ++
crossVersionProps(Seq.empty[ModuleID], SemanticVersion.parseUnsafe(scalaVersion.value)) {
case (Major(2), Minor(10)) =>
libraryDependencies.value.filterNot(m => m.organization == "org.wartremover" && m.name == "wartremover")
case x =>
libraryDependencies.value
}
, wartremoverErrors in (Compile, compile) ++= commonWarts((scalaBinaryVersion in update).value)
, wartremoverErrors in (Test, compile) ++= commonWarts((scalaBinaryVersion in update).value)
, testFrameworks ++= Seq(TestFramework("hedgehog.sbt.Framework"))

, gitTagFrom := "release"

/* Bintray { */
, bintrayPackageLabels := Seq("Scala", "Functional Programming", "FP")
, bintrayVcsUrl := Some("""git@github.com:Kevin-Lee/just-fp.git""")
, licenses += ("MIT", url("http://opensource.org/licenses/MIT"))
/* } Bintray */

, initialCommands in console := """import just.fp._"""
, initialCommands in console :=
"""import just.fp._; import just.fp.syntax._"""

/* Coveralls { */
, coverageHighlighting := (CrossVersion.partialVersion(scalaVersion.value) match {
Expand Down
62 changes: 62 additions & 0 deletions changelogs/1.3.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
## [1.3.0](https://github.com/Kevin-Lee/just-fp/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aclosed+milestone%3A%22milestone9%22) - 2019-10-13

### Done
* Add writer syntax (#89)

e.g.)
```scala
1.writer("Get value")
// Writer[String,Int] = WriterT(("Get value",1))
// WriterT[Id, String, Int]

"something".writer(List("something happened"))
// Writer[List[String],String] = WriterT((List("something happened"),"something"))
// WriterT[Id, List[String], String]
```

* Add `Writer` constructor for `(W, A)` (#87)

e.g.)
```scala
for {
a <- Writer.writer(("abc", 1))
b <- Writer.writer(("def", a))
} yield b
// WriterT[Id,String,Int] = WriterT(("abcdef",1))
```

* Add `mappend` syntax for `SemiGroup` and `Monoid` (#80)

mappend syntax for SemiGroup and Monoid. append is too common name so there can be conflict in names with other types. So use mappend (short name for Monoid append) instead of append.
```scala
1.mappend(2)
// Int = 3

"abc".mappend("def")
// String = abcdef

List(1, 2, 3).mappend(List(4, 5, 6))
// List[Int] = List(1, 2, 3, 4, 5, 6)
```

* Add `OptionSemiGroup` and `OptionMonoid` (#82)

Add `OptionSemiGroup` to make `Option[A]` `SemiGroup[A]` and `OptionMonoid` to make `Option[A]` `Monoid[A]` if `SemiGroup[A]` exists.
```scala
1.some |+| 999.some // Option[Int] = Some(1000)

1.some.mappend(999.some) // Option[Int] = Some(1000)

123.some |+| none[Int] // Option[Int] = Some(123)

none[Int] |+| 999.some // Option[Int] = Some(999)

List(1, 2, 3).some |+| List(4, 5, 6).some
// Option[List[Int]] = Some(List(1, 2, 3, 4, 5, 6))

List(1, 2, 3).some |+| none[List[Int]]
// Option[List[Int]] = Some(List(1, 2, 3))

none[List[Int]] |+| List(1, 2, 3).some
// Option[List[Int]] = Some(List(1, 2, 3))
```
10 changes: 8 additions & 2 deletions project/ProjectInfo.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import sbt.Keys.scalaBinaryVersion
import sbt.Keys.update
import wartremover.WartRemover.autoImport.{Wart, Warts}

/**
Expand All @@ -9,9 +11,13 @@ object ProjectInfo {
val ProjectScalaVersion: String = "2.13.1"
val CrossScalaVersions: Seq[String] = Seq("2.10.7", "2.11.12", "2.12.10", ProjectScalaVersion)

val ProjectVersion: String = "1.2.0"
val ProjectVersion: String = "1.3.0"

val commonWarts: Seq[wartremover.Wart] =
def commonWarts(scalaBinaryVersion: String): Seq[wartremover.Wart] = scalaBinaryVersion match {
case "2.10" =>
Seq.empty
case _ =>
Warts.allBut(Wart.DefaultArguments, Wart.Overloading, Wart.Any, Wart.Nothing, Wart.NonUnitStatements)
}

}
2 changes: 1 addition & 1 deletion project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ logLevel := sbt.Level.Warn

addSbtPlugin("org.foundweekends" % "sbt-bintray" % "0.5.5")

addSbtPlugin("org.wartremover" % "sbt-wartremover" % "2.4.2")
addSbtPlugin("org.wartremover" % "sbt-wartremover" % "2.4.3")

addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.6.0")

Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/just/fp/EitherT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ sealed abstract class EitherTFunctorInstance {
}

sealed abstract class EitherTApplicativeInstance extends EitherTFunctorInstance {
implicit def eitherTFunctor[F[_], A](implicit F0: Applicative[F]): Applicative[EitherT[F, A, *]] =
implicit def eitherTApplicative[F[_], A](implicit F0: Applicative[F]): Applicative[EitherT[F, A, *]] =
new EitherTApplicative[F, A] {
override implicit val F: Applicative[F] = F0
}
Expand Down
Loading

0 comments on commit 575c845

Please sign in to comment.