Skip to content

Commit

Permalink
Prevent implicit resolution for Default type class (#55)
Browse files Browse the repository at this point in the history
* Prevent implicit resolution for Default type class
- those implicits will not be applied by default
- default implicits can be imported where it is needed
- parameter is required until default value is not specified in case class constructor or Default implicit is in scope

* default value `None` for `Option[T]` fields
- wrapping case class field into Option[T] makes argument optional
- reduced boilerplate in case class constructors because there is no need to specify default value for optional fields

* Update README.md
- optional/required arguments

* Update README.md
- fix link to Default
  • Loading branch information
lavrov authored and alexarchambault committed Apr 9, 2017
1 parent f6f2b2f commit d292098
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 57 deletions.
34 changes: 13 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import caseapp._
```scala
case class Options(
user: Option[String],
enableFoo: Boolean,
enableFoo: Boolean = false,
file: List[String]
)

Expand All @@ -32,17 +32,22 @@ CaseApp.parse[Options](



### Default values
### Required and optional arguments

Most primitive types have default values, like `0` for `Int`, or `false`
for `Double`. Options default to `None`, and sequences to `Nil`.
Full list in the [source](https://github.com/alexarchambault/case-app/blob/master/src/main/scala/caseapp/core/Default.scala).
All arguments are required by default. To define an optional argument simply
wrap its type into `Option[T]`.

Optional arguments can also be defined by providing a default value.
There are two ways to do that:
- providing default value ad hoc in the case class definition
- defining default value for a type with [Default](https://github.com/alexarchambault/case-app/blob/master/src/main/scala/caseapp/core/Default.scala)
type class

```scala
case class Options(
user: Option[String],
enableFoo: Boolean,
file: List[String]
enableFoo: Boolean = false,
file: List[String] = Nil
)

CaseApp.parse[Options](Seq()) == Right((Options(None, false, Nil), Seq.empty))
Expand All @@ -51,19 +56,6 @@ CaseApp.parse[Options](Seq()) == Right((Options(None, false, Nil), Seq.empty))



Alternatively, default values can be manually specified, like
```scala
case class Options(
user: String = "default",
enableFoo: Boolean = true
)

CaseApp.parse[Options](Seq()) == Right((Options("default", true), Seq.empty))
```




### Lists

Some arguments can be specified several times on the command-line. These
Expand All @@ -72,7 +64,7 @@ should be typed as lists, e.g. `file` in
```scala
case class Options(
user: Option[String],
enableFoo: Boolean,
enableFoo: Boolean = false,
file: List[String]
)

Expand Down
26 changes: 14 additions & 12 deletions core/shared/src/main/scala/caseapp/core/Default.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,7 @@ object Default extends PlatformDefaults {
): Default[CC] =
Default.instance(gen.from(defaultOr.value(default.value())))

implicit val unit: Default[Unit] = Default.instance(())
implicit val int: Default[Int] = Default.instance(0)
implicit val long: Default[Long] = Default.instance(0L)
implicit val float: Default[Float] = Default.instance(0f)
implicit val double: Default[Double] = Default.instance(0d)
implicit val bigDecimal: Default[BigDecimal] = Default.instance(BigDecimal(0))
implicit val boolean: Default[Boolean] = Default.instance(false)
implicit val counter: Default[Int @@ Counter] = Default.instance(Tag of 0)
implicit val string: Default[String] = Default.instance("", str => s""""$str"""")

implicit def option[T]: Default[Option[T]] = Default.instance[Option[T]](None)
implicit def list[T]: Default[List[T]] = Default.instance[List[T]](Nil)

}

trait DefaultOr[L <: HList, D <: HList] {
Expand Down Expand Up @@ -76,3 +64,17 @@ object DefaultOr extends LowPriorityDefaultOr {
d :: tail(td)
}
}

object Defaults {
implicit val unit: Default[Unit] = Default.instance(())
implicit val int: Default[Int] = Default.instance(0)
implicit val long: Default[Long] = Default.instance(0L)
implicit val float: Default[Float] = Default.instance(0f)
implicit val double: Default[Double] = Default.instance(0d)
implicit val bigDecimal: Default[BigDecimal] = Default.instance(BigDecimal(0))
implicit val boolean: Default[Boolean] = Default.instance(false)
implicit val counter: Default[Int @@ Counter] = Default.instance(Tag of 0)
implicit val string: Default[String] = Default.instance("", str => s""""$str"""")

implicit def list[T]: Default[List[T]] = Default.instance[List[T]](Nil)
}
1 change: 1 addition & 0 deletions core/shared/src/test/scala/caseapp/Tests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import org.scalatest._

class Tests extends FlatSpec with Matchers {
import Definitions._
import core.Defaults._

"A parser" should "parse no args" in {
Parser[NoArgs].apply(Seq.empty) shouldEqual Right((NoArgs(), Seq.empty))
Expand Down
37 changes: 13 additions & 24 deletions doc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import caseapp._
```tut:silent
case class Options(
user: Option[String],
enableFoo: Boolean,
enableFoo: Boolean = false,
file: List[String]
)
Expand All @@ -37,17 +37,22 @@ assert(
)
```

### Default values
### Required and optional arguments

Most primitive types have default values, like `0` for `Int`, or `false`
for `Double`. Options default to `None`, and sequences to `Nil`.
Full list in the [source](https://github.com/alexarchambault/case-app/blob/master/src/main/scala/caseapp/core/Default.scala).
All arguments are required by default. To define an optional argument simply
wrap its type into `Option[T]`.

Optional arguments can also be defined by providing a default value.
There are two ways to do that:
- providing default value ad hoc in the case class definition
- defining default value for a type with [Default](https://github.com/alexarchambault/case-app/blob/master/core/shared/src/main/scala/caseapp/core/Default.scala)
type class

```tut:silent
case class Options(
user: Option[String],
enableFoo: Boolean,
file: List[String]
enableFoo: Boolean = false,
file: List[String] = Nil
)
CaseApp.parse[Options](Seq()) == Right((Options(None, false, Nil), Seq.empty))
Expand All @@ -59,22 +64,6 @@ assert(
)
```

Alternatively, default values can be manually specified, like
```tut:silent
case class Options(
user: String = "default",
enableFoo: Boolean = true
)
CaseApp.parse[Options](Seq()) == Right((Options("default", true), Seq.empty))
```

```tut:invisible
assert(
CaseApp.parse[Options](Seq()) == Right((Options("default", true), Seq.empty))
)
```

### Lists

Some arguments can be specified several times on the command-line. These
Expand All @@ -83,7 +72,7 @@ should be typed as lists, e.g. `file` in
```tut:silent
case class Options(
user: Option[String],
enableFoo: Boolean,
enableFoo: Boolean = false,
file: List[String]
)
Expand Down

0 comments on commit d292098

Please sign in to comment.