Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

just-fp v1.3.0 #96

Merged
merged 23 commits into from
Oct 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
d206c39
Close #80 - Add mappend syntax for SemiGroup and Monoid
kevin-lee Sep 26, 2019
a6e7b1d
Merge pull request #81 from Kevin-Lee/task/80/add-mappend
kevin-lee Sep 27, 2019
933ff83
Fixed: names in EitherT and WriterT
kevin-lee Sep 28, 2019
08cfd91
Merge pull request #83 from Kevin-Lee/fix-names
kevin-lee Sep 28, 2019
e08838d
Minor change in build.sbt
kevin-lee Sep 28, 2019
ca5f05d
Merge pull request #84 from Kevin-Lee/fix-config
kevin-lee Sep 28, 2019
cb28cc1
Close #82 - Add OptionSemiGroup and OptionMonoid
kevin-lee Sep 28, 2019
f0dd02b
Merge pull request #85 from Kevin-Lee/task/82/add-option-semigroup-an…
kevin-lee Sep 28, 2019
4da1b8a
Fixed: Scala binary incompatibility issue
kevin-lee Sep 28, 2019
75b66b2
Merge pull request #86 from Kevin-Lee/fix-scala-binary-incompatibilit…
kevin-lee Sep 28, 2019
998c359
Close #87 - Add Writer constructor for (W, A)
kevin-lee Sep 29, 2019
f5b4c7d
Merge pull request #88 from Kevin-Lee/task/87/add-writer-constructor-…
kevin-lee Sep 30, 2019
4417ab5
Close #89 - Add writer syntax
kevin-lee Oct 2, 2019
4244972
Merge pull request #90 from Kevin-Lee/task/89/add-writer-syntax
kevin-lee Oct 2, 2019
724ad98
Changes in build script to check branch for proper packaging
kevin-lee Oct 10, 2019
b6a7803
Merge pull request #91 from Kevin-Lee/change-build-script
kevin-lee Oct 10, 2019
87d606c
Changed: README.md updated with some initial documentation for Either
kevin-lee Oct 12, 2019
1cdb0e4
Merge pull request #92 from Kevin-Lee/initial-readme-update
kevin-lee Oct 12, 2019
ce07beb
Upgraded:
kevin-lee Oct 12, 2019
cda40c3
Merge pull request #93 from Kevin-Lee/upgrade-kind-projector-and-scala
kevin-lee Oct 12, 2019
22bfcff
Upgraded: README.md - Option Constructors and Type-safe Equal
kevin-lee Oct 12, 2019
78545e4
Merge pull request #94 from Kevin-Lee/update-readme.md
kevin-lee Oct 12, 2019
314183c
Prepare to release 1.3.0
kevin-lee Oct 13, 2019
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
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