Skip to content

Commit 33a3568

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

File tree

6 files changed

+127
-34
lines changed

6 files changed

+127
-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: 54 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ class NewTypeMacrosTest extends AnyFlatSpec with Matchers {
9393
val y = Cov(List(None))
9494
assertCompiles("y: Cov[None.type]")
9595

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

9899
someOrZero(x) shouldBe List(1)
99100
someOrZero(y) shouldBe List(0)
@@ -111,13 +112,12 @@ class NewTypeMacrosTest extends AnyFlatSpec with Matchers {
111112
behavior of "@newtype with type bounds"
112113

113114
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])
115+
val x = Sub(new java.util.HashMap[String, Int]): Sub[
116+
java.util.HashMap[String, Int]
117+
]
116118

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

120-
assertDoesNotCompile("x: Sub[java.util.concurrent.ConcurrentHashMap[String, Int]]")
121121
assertDoesNotCompile("y: Sub[java.util.HashMap[String, Int]]")
122122
}
123123

@@ -137,7 +137,8 @@ class NewTypeMacrosTest extends AnyFlatSpec with Matchers {
137137
it should "support deriving type class instances for simple newtypes via coerce" in {
138138
@newtype case class Text(private val s: String)
139139
object Text {
140-
implicit val arb: Arbitrary[Text] = scala.Predef.implicitly[Arbitrary[String]].coerce
140+
implicit val arb: Arbitrary[Text] =
141+
scala.Predef.implicitly[Arbitrary[String]].coerce
141142
}
142143
val x = scala.Predef.implicitly[Arbitrary[Text]].arbitrary.sample.get
143144
assertCompiles("x: Text")
@@ -148,7 +149,8 @@ class NewTypeMacrosTest extends AnyFlatSpec with Matchers {
148149
it should "support deriving type class instances for higher-kinded newtypes" in {
149150
@newtype class Nel[A](private val list: List[A])
150151
object Nel {
151-
def apply[A](head: A, tail: List[A]): Nel[A] = (head +: tail).coerce[Nel[A]]
152+
def apply[A](head: A, tail: List[A]): Nel[A] =
153+
(head +: tail).coerce[Nel[A]]
152154
implicit val functor: Functor[Nel] = derivingK
153155
}
154156

@@ -160,8 +162,10 @@ class NewTypeMacrosTest extends AnyFlatSpec with Matchers {
160162
it should "support deriving type class instances for higher-kinded newtypes via coerce" in {
161163
@newtype class Nel[A](private val list: List[A])
162164
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
165+
def apply[A](head: A, tail: List[A]): Nel[A] =
166+
(head +: tail).coerce[Nel[A]]
167+
implicit val functor: Functor[Nel] =
168+
scala.Predef.implicitly[Functor[List]].coerce
165169
}
166170

167171
val x = scala.Predef.implicitly[Functor[Nel]].map(Nel(1, List(2, 3)))(_ * 2)
@@ -196,12 +200,16 @@ class NewTypeMacrosTest extends AnyFlatSpec with Matchers {
196200
object EitherT {
197201
// Derive the Arbitrary instance explicitly
198202
implicit def arb[F[_], L, R](
199-
implicit a: Arbitrary[F[Either[L, R]]]
203+
implicit a: Arbitrary[F[Either[L, R]]]
200204
): Arbitrary[EitherT[F, L, R]] = deriving
201205
}
202206
val x = {
203207
import scala.Predef._
204-
scala.Predef.implicitly[Arbitrary[EitherT[List, String, Int]]].arbitrary.sample.get
208+
scala.Predef
209+
.implicitly[Arbitrary[EitherT[List, String, Int]]]
210+
.arbitrary
211+
.sample
212+
.get
205213
}
206214
assertCompiles("x: EitherT[List, String, Int]")
207215
val y = x.coerce[List[Either[String, Int]]]
@@ -213,12 +221,16 @@ class NewTypeMacrosTest extends AnyFlatSpec with Matchers {
213221
object EitherT {
214222
// Derive the Arbitrary instance explicitly
215223
implicit def arb[F[_], L, R](
216-
implicit a: Arbitrary[F[Either[L, R]]]
224+
implicit a: Arbitrary[F[Either[L, R]]]
217225
): Arbitrary[EitherT[F, L, R]] = a.coerce
218226
}
219227
val x = {
220228
import scala.Predef._
221-
scala.Predef.implicitly[Arbitrary[EitherT[List, String, Int]]].arbitrary.sample.get
229+
scala.Predef
230+
.implicitly[Arbitrary[EitherT[List, String, Int]]]
231+
.arbitrary
232+
.sample
233+
.get
222234
}
223235
assertCompiles("x: EitherT[List, String, Int]")
224236
val y = x.coerce[List[Either[String, Int]]]
@@ -230,12 +242,16 @@ class NewTypeMacrosTest extends AnyFlatSpec with Matchers {
230242
object EitherT {
231243
// Auto-derive all type classes of kind * -> *
232244
implicit def typeclass[T[_], F[_], L, R](
233-
implicit t: T[F[Either[L, R]]]
245+
implicit t: T[F[Either[L, R]]]
234246
): T[EitherT[F, L, R]] = deriving
235247
}
236248
val x = {
237249
import scala.Predef._
238-
scala.Predef.implicitly[Arbitrary[EitherT[List, String, Int]]].arbitrary.sample.get
250+
scala.Predef
251+
.implicitly[Arbitrary[EitherT[List, String, Int]]]
252+
.arbitrary
253+
.sample
254+
.get
239255
}
240256
assertCompiles("x: EitherT[List, String, Int]")
241257
val y = x.coerce[List[Either[String, Int]]]
@@ -247,12 +263,16 @@ class NewTypeMacrosTest extends AnyFlatSpec with Matchers {
247263
object EitherT {
248264
// Auto-derive all type classes of kind * -> *
249265
implicit def typeclass[T[_], F[_], L, R](
250-
implicit t: T[F[Either[L, R]]]
266+
implicit t: T[F[Either[L, R]]]
251267
): T[EitherT[F, L, R]] = t.coerce
252268
}
253269
val x = {
254270
import scala.Predef._
255-
scala.Predef.implicitly[Arbitrary[EitherT[List, String, Int]]].arbitrary.sample.get
271+
scala.Predef
272+
.implicitly[Arbitrary[EitherT[List, String, Int]]]
273+
.arbitrary
274+
.sample
275+
.get
256276
}
257277
assertCompiles("x: EitherT[List, String, Int]")
258278
val y = x.coerce[List[Either[String, Int]]]
@@ -335,8 +355,8 @@ class NewTypeMacrosTest extends AnyFlatSpec with Matchers {
335355
}
336356

337357
"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)
358+
@newtype(unapply = true) case class X0(private val x: String)
359+
@newtype(unapply = true) case class X1[A](private val x: A)
340360
@newsubtype(unapply = true) case class Y0(private val x: String)
341361
@newsubtype(unapply = true) case class Y1[A](private val x: A)
342362

@@ -406,21 +426,29 @@ object NewTypeMacrosTest {
406426
@newtype case class Maybe[A](unsafeGet: A) {
407427
def isEmpty: Boolean = unsafeGet == null
408428
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
429+
def map[B](f: A => B): Maybe[B] =
430+
if (isEmpty) Maybe.empty else Maybe(f(unsafeGet))
431+
def flatMap[B](f: A => Maybe[B]): Maybe[B] =
432+
if (isEmpty) Maybe.empty else f(unsafeGet)
433+
def filter(p: A => Boolean): Maybe[A] =
434+
if (isEmpty || !p(unsafeGet)) Maybe.empty else this
435+
def filterNot(p: A => Boolean): Maybe[A] =
436+
if (isEmpty || p(unsafeGet)) Maybe.empty else this
413437
def orElse(ma: => Maybe[A]): Maybe[A] = if (isDefined) this else ma
414438
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)
439+
def getOrThrow: A =
440+
if (isDefined) unsafeGet
441+
else throw new NoSuchElementException("Maybe.empty.get")
442+
def cata[B](ifEmpty: => B, ifDefined: A => B): B =
443+
if (isEmpty) ifEmpty else ifDefined(unsafeGet)
417444
def fold[B](ifEmpty: => B)(ifDefined: A => B): B = cata(ifEmpty, ifDefined)
418445
def contains(a: A): Boolean = isDefined && unsafeGet == a
419446
def exists(p: A => Boolean): Boolean = isDefined && p(unsafeGet)
420447
}
421448
object Maybe {
422449
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]]
450+
def fromOption[A](x: Option[A]): Maybe[A] =
451+
(if (x.isDefined) x.get else null).asInstanceOf[Maybe[A]]
424452
}
425453

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

0 commit comments

Comments
 (0)