Skip to content

Commit dc792bf

Browse files
committed
Add support for scala-native and scalajs 1.0
1 parent 46775cf commit dc792bf

File tree

6 files changed

+129
-34
lines changed

6 files changed

+129
-34
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
.idea/
22
target/
3+
*.hnir

build.sbt

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,22 @@ import sbtcrossproject.CrossPlugin.autoImport.crossProject
44
organization in ThisBuild := "io.estatico"
55

66
lazy val root = project.in(file("."))
7-
.aggregate(newtypeJS, newtypeJVM, catsTestsJVM)
7+
.aggregate(newtypeJS, newtypeJVM, newtypeNative, catsTestsJVM)
88
.settings(noPublishSettings)
99

10-
lazy val newtype = crossProject(JSPlatform, JVMPlatform).in(file("."))
10+
lazy val newtype = crossProject(JSPlatform, JVMPlatform, NativePlatform).in(file("."))
1111
.settings(defaultSettings)
1212
.settings(releasePublishSettings)
1313
.settings(name := "newtype")
14+
.nativeSettings(
15+
crossScalaVersions := List("2.11.12"),
16+
scalaVersion := "2.11.12",
17+
nativeLinkStubs := true,
18+
Compile / scalacOptions += "-Yno-predef" // needed to ensure users can use -Yno-predef
19+
)
1420

1521
lazy val newtypeJVM = newtype.jvm
22+
lazy val newtypeNative = newtype.native
1623
lazy val newtypeJS = newtype.js
1724

1825
lazy val catsTests = crossProject(JVMPlatform).in(file("cats-tests"))
@@ -116,7 +123,6 @@ lazy val defaultSettings = Seq(
116123

117124
lazy val defaultScalacOptions = scalacOptions ++= Seq(
118125
"-Xfatal-warnings",
119-
"-Yno-predef", // needed to ensure users can use -Yno-predef
120126
"-unchecked",
121127
"-feature",
122128
"-deprecation",
@@ -129,12 +135,17 @@ lazy val defaultScalacOptions = scalacOptions ++= Seq(
129135
case _ =>
130136
// on scala 2.12+ some spurious unused warnings get triggered
131137
Seq("-Xlint:-unused,_")
132-
})
138+
}) ++ (if (crossProjectPlatform.value == NativePlatform)
139+
Seq() // Removing the 'predef' on scala native-tests, breaks the test integration with sbt
140+
else
141+
Seq("-Yno-predef")) // needed to ensure users can use -Yno-predef
133142

134143
lazy val defaultLibraryDependencies = libraryDependencies ++= Seq(
135144
scalaOrganization.value % "scala-reflect" % scalaVersion.value % Provided,
136145
scalaOrganization.value % "scala-compiler" % scalaVersion.value % Provided,
137146
"org.scalacheck" %%% "scalacheck" % "1.14.3" % Test,
138-
"org.scalatest" %%% "scalatest" % "3.1.1" % Test,
139-
"org.scalatestplus" %%% "scalacheck-1-14" % "3.1.1.1" % Test,
147+
"org.scalatest" %%% "scalatest" % "3.2.0-M4" % Test,
148+
"org.scalatestplus" %%% "scalacheck-1-14" % "3.2.0.0-M4" % Test
140149
)
150+
151+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package io.estatico.newtype.macros
2+
3+
import org.scalatest.flatspec.AnyFlatSpec
4+
import org.scalatest.matchers.should.Matchers
5+
import io.estatico.newtype.ops._
6+
import org.scalacheck.Arbitrary
7+
8+
class NonNativeNewTypeMacrosTest extends AnyFlatSpec with Matchers {
9+
10+
import NewTypeMacrosTest._
11+
12+
behavior of "@newtype with type bounds"
13+
14+
it should "enforce type bounds" in {
15+
val x = Sub(new java.util.HashMap[String, Int]): Sub[java.util.HashMap[String, Int]]
16+
val y = Sub(new java.util.concurrent.ConcurrentHashMap[String, Int])
17+
18+
assertCompiles("x: Sub[java.util.HashMap[String, Int]]")
19+
assertCompiles("y: Sub[java.util.concurrent.ConcurrentHashMap[String, Int]]")
20+
21+
assertDoesNotCompile("x: Sub[java.util.concurrent.ConcurrentHashMap[String, Int]]")
22+
assertDoesNotCompile("y: Sub[java.util.HashMap[String, Int]]")
23+
}
24+
25+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package io.estatico.newtype.macros
2+
3+
import org.scalatest.flatspec.AnyFlatSpec
4+
import org.scalatest.matchers.should.Matchers
5+
import io.estatico.newtype.ops._
6+
import org.scalacheck.Arbitrary
7+
8+
class NonNativeNewTypeMacrosTest extends AnyFlatSpec with Matchers {
9+
10+
import NewTypeMacrosTest._
11+
12+
behavior of "@newtype with type bounds"
13+
14+
it should "enforce type bounds" in {
15+
val x = Sub(new java.util.HashMap[String, Int]): Sub[java.util.HashMap[String, Int]]
16+
val y = Sub(new java.util.concurrent.ConcurrentHashMap[String, Int])
17+
18+
assertCompiles("x: Sub[java.util.HashMap[String, Int]]")
19+
assertCompiles("y: Sub[java.util.concurrent.ConcurrentHashMap[String, Int]]")
20+
21+
assertDoesNotCompile("x: Sub[java.util.concurrent.ConcurrentHashMap[String, Int]]")
22+
assertDoesNotCompile("y: Sub[java.util.HashMap[String, Int]]")
23+
}
24+
25+
}

project/plugins.sbt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,8 @@ addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.1")
22
addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.13")
33
addSbtPlugin("com.dwijnand" % "sbt-travisci" % "1.2.0")
44
addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.8.1")
5-
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.0.1")
6-
addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.0.0")
5+
6+
addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.0.0")
7+
addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.0.0")
8+
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.0.1")
9+
addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.0-M2")

shared/src/test/scala/io/estatico/newtype/macros/NewTypeMacrosTest.scala

Lines changed: 56 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package io.estatico.newtype.macros
22

3+
import org.scalatest.flatspec.AnyFlatSpec
4+
import org.scalatest.matchers.should.Matchers
35
import io.estatico.newtype.ops._
46
import org.scalacheck.Arbitrary
57
import org.scalatest.flatspec.AnyFlatSpec
@@ -93,7 +95,8 @@ class NewTypeMacrosTest extends AnyFlatSpec with Matchers {
9395
val y = Cov(List(None))
9496
assertCompiles("y: Cov[None.type]")
9597

96-
def someOrZero[A](c: Cov[Option[Int]]): Cov[Int] = Cov(c.value.map(_.getOrElse(0)))
98+
def someOrZero[A](c: Cov[Option[Int]]): Cov[Int] =
99+
Cov(c.value.map(_.getOrElse(0)))
97100

98101
someOrZero(x) shouldBe List(1)
99102
someOrZero(y) shouldBe List(0)
@@ -111,13 +114,12 @@ class NewTypeMacrosTest extends AnyFlatSpec with Matchers {
111114
behavior of "@newtype with type bounds"
112115

113116
it should "enforce type bounds" in {
114-
val x = Sub(new java.util.HashMap[String, Int]): Sub[java.util.HashMap[String, Int]]
115-
val y = Sub(new java.util.concurrent.ConcurrentHashMap[String, Int])
117+
val x = Sub(new java.util.HashMap[String, Int]): Sub[
118+
java.util.HashMap[String, Int]
119+
]
116120

117121
assertCompiles("x: Sub[java.util.HashMap[String, Int]]")
118-
assertCompiles("y: Sub[java.util.concurrent.ConcurrentHashMap[String, Int]]")
119122

120-
assertDoesNotCompile("x: Sub[java.util.concurrent.ConcurrentHashMap[String, Int]]")
121123
assertDoesNotCompile("y: Sub[java.util.HashMap[String, Int]]")
122124
}
123125

@@ -137,7 +139,8 @@ class NewTypeMacrosTest extends AnyFlatSpec with Matchers {
137139
it should "support deriving type class instances for simple newtypes via coerce" in {
138140
@newtype case class Text(private val s: String)
139141
object Text {
140-
implicit val arb: Arbitrary[Text] = scala.Predef.implicitly[Arbitrary[String]].coerce
142+
implicit val arb: Arbitrary[Text] =
143+
scala.Predef.implicitly[Arbitrary[String]].coerce
141144
}
142145
val x = scala.Predef.implicitly[Arbitrary[Text]].arbitrary.sample.get
143146
assertCompiles("x: Text")
@@ -148,7 +151,8 @@ class NewTypeMacrosTest extends AnyFlatSpec with Matchers {
148151
it should "support deriving type class instances for higher-kinded newtypes" in {
149152
@newtype class Nel[A](private val list: List[A])
150153
object Nel {
151-
def apply[A](head: A, tail: List[A]): Nel[A] = (head +: tail).coerce[Nel[A]]
154+
def apply[A](head: A, tail: List[A]): Nel[A] =
155+
(head +: tail).coerce[Nel[A]]
152156
implicit val functor: Functor[Nel] = derivingK
153157
}
154158

@@ -160,8 +164,10 @@ class NewTypeMacrosTest extends AnyFlatSpec with Matchers {
160164
it should "support deriving type class instances for higher-kinded newtypes via coerce" in {
161165
@newtype class Nel[A](private val list: List[A])
162166
object Nel {
163-
def apply[A](head: A, tail: List[A]): Nel[A] = (head +: tail).coerce[Nel[A]]
164-
implicit val functor: Functor[Nel] = scala.Predef.implicitly[Functor[List]].coerce
167+
def apply[A](head: A, tail: List[A]): Nel[A] =
168+
(head +: tail).coerce[Nel[A]]
169+
implicit val functor: Functor[Nel] =
170+
scala.Predef.implicitly[Functor[List]].coerce
165171
}
166172

167173
val x = scala.Predef.implicitly[Functor[Nel]].map(Nel(1, List(2, 3)))(_ * 2)
@@ -196,12 +202,16 @@ class NewTypeMacrosTest extends AnyFlatSpec with Matchers {
196202
object EitherT {
197203
// Derive the Arbitrary instance explicitly
198204
implicit def arb[F[_], L, R](
199-
implicit a: Arbitrary[F[Either[L, R]]]
205+
implicit a: Arbitrary[F[Either[L, R]]]
200206
): Arbitrary[EitherT[F, L, R]] = deriving
201207
}
202208
val x = {
203209
import scala.Predef._
204-
scala.Predef.implicitly[Arbitrary[EitherT[List, String, Int]]].arbitrary.sample.get
210+
scala.Predef
211+
.implicitly[Arbitrary[EitherT[List, String, Int]]]
212+
.arbitrary
213+
.sample
214+
.get
205215
}
206216
assertCompiles("x: EitherT[List, String, Int]")
207217
val y = x.coerce[List[Either[String, Int]]]
@@ -213,12 +223,16 @@ class NewTypeMacrosTest extends AnyFlatSpec with Matchers {
213223
object EitherT {
214224
// Derive the Arbitrary instance explicitly
215225
implicit def arb[F[_], L, R](
216-
implicit a: Arbitrary[F[Either[L, R]]]
226+
implicit a: Arbitrary[F[Either[L, R]]]
217227
): Arbitrary[EitherT[F, L, R]] = a.coerce
218228
}
219229
val x = {
220230
import scala.Predef._
221-
scala.Predef.implicitly[Arbitrary[EitherT[List, String, Int]]].arbitrary.sample.get
231+
scala.Predef
232+
.implicitly[Arbitrary[EitherT[List, String, Int]]]
233+
.arbitrary
234+
.sample
235+
.get
222236
}
223237
assertCompiles("x: EitherT[List, String, Int]")
224238
val y = x.coerce[List[Either[String, Int]]]
@@ -230,12 +244,16 @@ class NewTypeMacrosTest extends AnyFlatSpec with Matchers {
230244
object EitherT {
231245
// Auto-derive all type classes of kind * -> *
232246
implicit def typeclass[T[_], F[_], L, R](
233-
implicit t: T[F[Either[L, R]]]
247+
implicit t: T[F[Either[L, R]]]
234248
): T[EitherT[F, L, R]] = deriving
235249
}
236250
val x = {
237251
import scala.Predef._
238-
scala.Predef.implicitly[Arbitrary[EitherT[List, String, Int]]].arbitrary.sample.get
252+
scala.Predef
253+
.implicitly[Arbitrary[EitherT[List, String, Int]]]
254+
.arbitrary
255+
.sample
256+
.get
239257
}
240258
assertCompiles("x: EitherT[List, String, Int]")
241259
val y = x.coerce[List[Either[String, Int]]]
@@ -247,12 +265,16 @@ class NewTypeMacrosTest extends AnyFlatSpec with Matchers {
247265
object EitherT {
248266
// Auto-derive all type classes of kind * -> *
249267
implicit def typeclass[T[_], F[_], L, R](
250-
implicit t: T[F[Either[L, R]]]
268+
implicit t: T[F[Either[L, R]]]
251269
): T[EitherT[F, L, R]] = t.coerce
252270
}
253271
val x = {
254272
import scala.Predef._
255-
scala.Predef.implicitly[Arbitrary[EitherT[List, String, Int]]].arbitrary.sample.get
273+
scala.Predef
274+
.implicitly[Arbitrary[EitherT[List, String, Int]]]
275+
.arbitrary
276+
.sample
277+
.get
256278
}
257279
assertCompiles("x: EitherT[List, String, Int]")
258280
val y = x.coerce[List[Either[String, Int]]]
@@ -335,8 +357,8 @@ class NewTypeMacrosTest extends AnyFlatSpec with Matchers {
335357
}
336358

337359
"unapply = true" should "generate an unapply method" in {
338-
@newtype (unapply = true) case class X0(private val x: String)
339-
@newtype (unapply = true) case class X1[A](private val x: A)
360+
@newtype(unapply = true) case class X0(private val x: String)
361+
@newtype(unapply = true) case class X1[A](private val x: A)
340362
@newsubtype(unapply = true) case class Y0(private val x: String)
341363
@newsubtype(unapply = true) case class Y1[A](private val x: A)
342364

@@ -406,21 +428,29 @@ object NewTypeMacrosTest {
406428
@newtype case class Maybe[A](unsafeGet: A) {
407429
def isEmpty: Boolean = unsafeGet == null
408430
def isDefined: Boolean = unsafeGet != null
409-
def map[B](f: A => B): Maybe[B] = if (isEmpty) Maybe.empty else Maybe(f(unsafeGet))
410-
def flatMap[B](f: A => Maybe[B]): Maybe[B] = if (isEmpty) Maybe.empty else f(unsafeGet)
411-
def filter(p: A => Boolean): Maybe[A] = if (isEmpty || !p(unsafeGet)) Maybe.empty else this
412-
def filterNot(p: A => Boolean): Maybe[A] = if (isEmpty || p(unsafeGet)) Maybe.empty else this
431+
def map[B](f: A => B): Maybe[B] =
432+
if (isEmpty) Maybe.empty else Maybe(f(unsafeGet))
433+
def flatMap[B](f: A => Maybe[B]): Maybe[B] =
434+
if (isEmpty) Maybe.empty else f(unsafeGet)
435+
def filter(p: A => Boolean): Maybe[A] =
436+
if (isEmpty || !p(unsafeGet)) Maybe.empty else this
437+
def filterNot(p: A => Boolean): Maybe[A] =
438+
if (isEmpty || p(unsafeGet)) Maybe.empty else this
413439
def orElse(ma: => Maybe[A]): Maybe[A] = if (isDefined) this else ma
414440
def getOrElse(a: => A): A = if (isDefined) unsafeGet else a
415-
def getOrThrow: A = if (isDefined) unsafeGet else throw new NoSuchElementException("Maybe.empty.get")
416-
def cata[B](ifEmpty: => B, ifDefined: A => B): B = if (isEmpty) ifEmpty else ifDefined(unsafeGet)
441+
def getOrThrow: A =
442+
if (isDefined) unsafeGet
443+
else throw new NoSuchElementException("Maybe.empty.get")
444+
def cata[B](ifEmpty: => B, ifDefined: A => B): B =
445+
if (isEmpty) ifEmpty else ifDefined(unsafeGet)
417446
def fold[B](ifEmpty: => B)(ifDefined: A => B): B = cata(ifEmpty, ifDefined)
418447
def contains(a: A): Boolean = isDefined && unsafeGet == a
419448
def exists(p: A => Boolean): Boolean = isDefined && p(unsafeGet)
420449
}
421450
object Maybe {
422451
def empty[A]: Maybe[A] = null.asInstanceOf[Maybe[A]]
423-
def fromOption[A](x: Option[A]): Maybe[A] = (if (x.isDefined) x.get else null).asInstanceOf[Maybe[A]]
452+
def fromOption[A](x: Option[A]): Maybe[A] =
453+
(if (x.isDefined) x.get else null).asInstanceOf[Maybe[A]]
424454
}
425455

426456
@newtype case class Id[A](value: A) {

0 commit comments

Comments
 (0)