├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .mill-version ├── LICENSE ├── README.md ├── build.sc ├── examples └── src │ ├── AutoParamTupling.scala │ ├── ContextQueries.scala │ ├── Conversion.scala │ ├── EnumTypes.scala │ ├── ImpliedInstances.scala │ ├── IntersectionTypes.scala │ ├── Main.scala │ ├── MultiversalEquality.scala │ ├── PatternMatching.scala │ ├── StructuralTypes.scala │ ├── TraitParams.scala │ ├── TypeLambdas.scala │ └── UnionTypes.scala └── mill /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: 4 | pull_request: 5 | push: 6 | 7 | env: 8 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 9 | 10 | jobs: 11 | run: 12 | name: Build and Run 13 | strategy: 14 | matrix: 15 | java-version: [8, 11, 17] 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout current branch (full) 19 | uses: actions/checkout@v3 20 | with: 21 | fetch-depth: 0 22 | 23 | - name: Setup Java 24 | uses: actions/setup-java@v3 25 | with: 26 | distribution: temurin 27 | java-version: ${{ matrix.java-version }} 28 | 29 | - uses: coursier/cache-action@v6 30 | 31 | - name: Run project 32 | run: ./mill examples.run 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.class 3 | *.log 4 | *~ 5 | 6 | # sbt specific 7 | dist/* 8 | target/ 9 | lib_managed/ 10 | src_managed/ 11 | project/boot/ 12 | project/plugins/project/ 13 | project/local-plugins.sbt 14 | .history 15 | 16 | # Scala-IDE specific 17 | .scala_dependencies 18 | .cache 19 | .classpath 20 | .project 21 | .settings 22 | classes/ 23 | 24 | # idea 25 | .idea 26 | .idea_modules 27 | /.worksheet/ 28 | 29 | # mill 30 | out/ 31 | 32 | # bsp 33 | .bsp/ 34 | -------------------------------------------------------------------------------- /.mill-version: -------------------------------------------------------------------------------- 1 | 0.11.1 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 The dotty-example-project contributors. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 3. The name of the author may not be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Example mill project that compiles using Scala 3 2 | 3 | ## Usage 4 | 5 | This is a normal mill project. You can compile code with `mill examples.compile` and run it 6 | with `mill examples.run`, `mill -i examples.console` will start a Scala 3 REPL. 7 | 8 | ### IDE support 9 | 10 | It's recommended to either use [Metals](https://scalameta.org/metals/) with the 11 | editor of your choice or [the Scala Plugin for 12 | IntelliJ](https://blog.jetbrains.com/scala/). 13 | 14 | ## Using Scala 3 in an existing project 15 | 16 | ### build.sc 17 | 18 | ```scala 19 | def scalaVersion = "3.7.1" 20 | ``` 21 | 22 | ### Getting your project to compile with Scala 3 23 | 24 | For help with porting an existing Scala 2 project to Scala 3, see the 25 | [Scala 3 migration guide](https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html). 26 | 27 | ## Need help? 28 | 29 | https://www.scala-lang.org/community/ has links. 30 | -------------------------------------------------------------------------------- /build.sc: -------------------------------------------------------------------------------- 1 | import mill._, scalalib._ 2 | 3 | object examples extends ScalaModule { 4 | def scalaVersion = "3.7.1" 5 | } 6 | -------------------------------------------------------------------------------- /examples/src/AutoParamTupling.scala: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Automatic Tupling of Function Params: https://dotty.epfl.ch/docs/reference/other-new-features/auto-parameter-tupling.html 4 | */ 5 | object AutoParamTupling { 6 | 7 | def test: Unit = { 8 | 9 | /** 10 | * In order to get thread safety, you need to put @volatile before lazy vals. 11 | * https://dotty.epfl.ch/docs/reference/changed-features/lazy-vals.html 12 | */ 13 | @volatile lazy val xs: List[String] = List("d", "o", "t", "t", "y") 14 | 15 | /** 16 | * Current behaviour in Scala 2.12.2 : 17 | * error: missing parameter type 18 | * Note: The expected type requires a one-argument function accepting a 2-Tuple. 19 | * Consider a pattern matching anonymous function, `{ case (s, i) => ... }` 20 | */ 21 | xs.zipWithIndex.map((s, i) => println(s"$i: $s")) 22 | 23 | } 24 | } -------------------------------------------------------------------------------- /examples/src/ContextQueries.scala: -------------------------------------------------------------------------------- 1 | 2 | import scala.concurrent.{ExecutionContext, Future} 3 | import scala.util.Try 4 | 5 | /** 6 | * Context Queries: 7 | * - http://dotty.epfl.ch/docs/reference/contextual/query-types.html, 8 | * - https://www.scala-lang.org/blog/2016/12/07/implicit-function-types.html 9 | */ 10 | object ContextQueries /* Formerly known as Implicit Function Types */ { 11 | 12 | object context { 13 | // type alias Contextual 14 | type Contextual[T] = ExecutionContext ?=> T 15 | 16 | // sum is expanded to sum(x, y)(ctx) 17 | def asyncSum(x: Int, y: Int): Contextual[Future[Int]] = Future(x + y) 18 | 19 | def asyncMult(x: Int, y: Int)(using ctx: ExecutionContext) = Future(x * y) 20 | } 21 | 22 | object parse { 23 | 24 | type Parseable[T] = ImpliedInstances.StringParser[T] ?=> Try[T] 25 | 26 | def sumStrings(x: String, y: String): Parseable[Int] = { 27 | val parser = implicitly[ImpliedInstances.StringParser[Int]] 28 | val tryA = parser.parse(x) 29 | val tryB = parser.parse(y) 30 | 31 | for { 32 | a <- tryA 33 | b <- tryB 34 | } yield a + b 35 | } 36 | } 37 | 38 | def test: Unit = { 39 | 40 | import ExecutionContext.Implicits.global 41 | context.asyncSum(3, 4).foreach(println) 42 | context.asyncMult(3, 4).foreach(println) 43 | 44 | println(parse.sumStrings("3", "4")) 45 | println(parse.sumStrings("3", "a")) 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /examples/src/Conversion.scala: -------------------------------------------------------------------------------- 1 | import scala.language.implicitConversions 2 | 3 | /** 4 | * Conversions: http://dotty.epfl.ch/docs/reference/contextual/conversions.html 5 | */ 6 | object Conversion { 7 | 8 | case class IntWrapper(a: Int) extends AnyVal 9 | case class DoubleWrapper(b: Double) extends AnyVal 10 | 11 | def convert[T, U](x: T)(using converter: Conversion[T, U]): U = converter(x) 12 | 13 | given IntWrapperToDoubleWrapper: Conversion[IntWrapper, DoubleWrapper] = new Conversion[IntWrapper, DoubleWrapper] { 14 | override def apply(i: IntWrapper): DoubleWrapper = new DoubleWrapper(i.a.toDouble) 15 | } 16 | 17 | def useConversion(using f: Conversion[IntWrapper, DoubleWrapper]) = { 18 | val y: IntWrapper = new IntWrapper(4) 19 | val x: DoubleWrapper = y 20 | x 21 | } 22 | 23 | /* Not working anymore. 24 | def useConversion(implicit f: A => B) = { 25 | val y: A = ... 26 | val x: B = a // error under Dotty 27 | } 28 | */ 29 | 30 | def test: Unit = { 31 | println(useConversion) 32 | println(convert(new IntWrapper(42))) 33 | } 34 | 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /examples/src/EnumTypes.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Enum Types: http://dotty.epfl.ch/docs/reference/enums/adts.html 3 | */ 4 | object EnumTypes { 5 | 6 | enum ListEnum[+A] { 7 | case Cons(h: A, t: ListEnum[A]) 8 | case Empty 9 | } 10 | 11 | enum Planet(mass: Double, radius: Double) { 12 | private final val G = 6.67300E-11 13 | def surfaceGravity = G * mass / (radius * radius) 14 | def surfaceWeight(otherMass: Double) = otherMass * surfaceGravity 15 | 16 | case Mercury extends Planet(3.303e+23, 2.4397e6) 17 | case Venus extends Planet(4.869e+24, 6.0518e6) 18 | case Earth extends Planet(5.976e+24, 6.37814e6) 19 | case Mars extends Planet(6.421e+23, 3.3972e6) 20 | case Jupiter extends Planet(1.9e+27, 7.1492e7) 21 | case Saturn extends Planet(5.688e+26, 6.0268e7) 22 | case Uranus extends Planet(8.686e+25, 2.5559e7) 23 | case Neptune extends Planet(1.024e+26, 2.4746e7) 24 | } 25 | 26 | def test: Unit = { 27 | 28 | val emptyList = ListEnum.Empty 29 | val list = ListEnum.Cons(1, ListEnum.Cons(2, ListEnum.Cons(3, ListEnum.Empty))) 30 | println(emptyList) 31 | println(s"${list}\n") 32 | 33 | def calculateEarthWeightOnPlanets(earthWeight: Double) = { 34 | val mass = earthWeight/Planet.Earth.surfaceGravity 35 | for (p <- Planet.values) 36 | println(s"Your weight on $p is ${p.surfaceWeight(mass)}") 37 | } 38 | 39 | calculateEarthWeightOnPlanets(80) 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /examples/src/ImpliedInstances.scala: -------------------------------------------------------------------------------- 1 | import scala.util.{Success, Try} 2 | 3 | /** 4 | * Implied Instances: 5 | * - https://dotty.epfl.ch/docs/reference/contextual/instance-defs.html 6 | */ 7 | object ImpliedInstances { 8 | 9 | sealed trait StringParser[A] { 10 | def parse(s: String): Try[A] 11 | } 12 | 13 | object StringParser { 14 | 15 | def apply[A](using parser: StringParser[A]): StringParser[A] = parser 16 | 17 | private def baseParser[A](f: String ⇒ Try[A]): StringParser[A] = new StringParser[A] { 18 | override def parse(s: String): Try[A] = f(s) 19 | } 20 | 21 | given stringParser: StringParser[String] = baseParser(Success(_)) 22 | given intParser: StringParser[Int] = baseParser(s ⇒ Try(s.toInt)) 23 | 24 | given optionParser[A](using parser: => StringParser[A]): StringParser[Option[A]] = new StringParser[Option[A]] { 25 | override def parse(s: String): Try[Option[A]] = s match { 26 | case "" ⇒ Success(None) // implicit parser not used. 27 | case str ⇒ parser.parse(str).map(x ⇒ Some(x)) // implicit parser is evaluated at here 28 | } 29 | } 30 | } 31 | 32 | def test: Unit = { 33 | println(implicitly[StringParser[Option[Int]]].parse("21")) 34 | println(implicitly[StringParser[Option[Int]]].parse("")) 35 | println(implicitly[StringParser[Option[Int]]].parse("21a")) 36 | 37 | println(implicitly[StringParser[Option[Int]]](StringParser.optionParser[Int]).parse("42")) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /examples/src/IntersectionTypes.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Intersection Types: https://dotty.epfl.ch/docs/reference/new-types/intersection-types.html 3 | */ 4 | object IntersectionTypes { 5 | 6 | sealed trait X { 7 | def x: Double 8 | def tpe: X 9 | } 10 | 11 | sealed trait Y { 12 | def y: Double 13 | def tpe: Y 14 | } 15 | 16 | type P = Y & X 17 | type PP = X & Y 18 | 19 | final case class Point(x: Double, y: Double) extends X with Y { 20 | override def tpe: X & Y = ??? 21 | } 22 | 23 | def test: Unit = { 24 | 25 | def euclideanDistance(p1: X & Y, p2: X & Y) = { 26 | Math.sqrt(Math.pow(p2.y - p1.y, 2) + Math.pow(p2.x - p1.x, 2)) 27 | } 28 | 29 | val p1: P = Point(3, 4) 30 | val p2: PP = Point(6, 8) 31 | println(euclideanDistance(p1, p2)) 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /examples/src/Main.scala: -------------------------------------------------------------------------------- 1 | 2 | object Main { 3 | 4 | def main(args: Array[String]): Unit = { 5 | 6 | runExample("Trait Params")(TraitParams.test) 7 | 8 | runExample("Enum Types")(EnumTypes.test) 9 | 10 | runExample("Context Queries")(ContextQueries.test) 11 | 12 | runExample("Implied Instances")(ImpliedInstances.test) 13 | 14 | runExample("Conversion")(Conversion.test) 15 | 16 | runExample("Union Types")(UnionTypes.test) 17 | 18 | runExample("Intersection Types")(IntersectionTypes.test) 19 | 20 | runExample("Type Lambda")(TypeLambdas.test) 21 | 22 | runExample("Multiversal Equality")(MultiversalEquality.test) 23 | 24 | runExample("Auto Param Tupling")(AutoParamTupling.test) 25 | 26 | runExample("Structural Types")(StructuralTypes.test) 27 | 28 | runExample("Pattern Matching")(PatternMatching.test) 29 | 30 | } 31 | 32 | private def runExample(name: String)(f: => Unit) = { 33 | println(Console.MAGENTA + s"$name example:" + Console.RESET) 34 | f 35 | println() 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /examples/src/MultiversalEquality.scala: -------------------------------------------------------------------------------- 1 | import scala.language.strictEquality 2 | 3 | /** 4 | * Multiversal Equality: https://dotty.epfl.ch/docs/reference/contextual/multiversal-equality.html 5 | * scala.Eq definition: https://github.com/lampepfl/dotty/blob/master/library/src/scala/CanEqual.scala 6 | */ 7 | object MultiversalEquality { 8 | 9 | def test: Unit = { 10 | 11 | // Values of types Int and String cannot be compared with == or !=, 12 | // unless we add the derived delegate instance like: 13 | given CanEqual[Int, String] = CanEqual.derived 14 | println(3 == "3") 15 | 16 | // By default, all numbers are comparable, because of; 17 | // implicit def eqlNumber: CanEqual[Number, Number] = derived 18 | println(3 == 5.1) 19 | 20 | // By default, all Sequences are comparable, because of; 21 | // implicit def eqlSeq[T, U](implicit eq: CanEqual[T, U]): CanEqual[GenSeq[T], GenSeq[U]] = derived 22 | println(List(1, 2) == Vector(1, 2)) 23 | 24 | class A(a: Int) 25 | class B(b: Int) 26 | 27 | val a = new A(4) 28 | val b = new B(4) 29 | 30 | // scala.language.strictEquality is enabled, therefore we need some extra delegate instances 31 | // to compare instances of A and B. 32 | given CanEqual[A, B] = CanEqual.derived 33 | given CanEqual[B, A] = CanEqual.derived 34 | 35 | println(a != b) 36 | println(b == a) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/src/PatternMatching.scala: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Pattern Matching: https://dotty.epfl.ch/docs/reference/changed-features/pattern-matching.html 4 | */ 5 | object PatternMatching { 6 | 7 | object booleanPattern { 8 | 9 | object Even { 10 | def unapply(s: String): Boolean = s.length % 2 == 0 11 | } 12 | 13 | } 14 | 15 | object productPattern { 16 | 17 | class Person(name: String, age: Int) extends Product { 18 | // if we not define that, it will give compile error. 19 | // we change the order 20 | def _1 = age 21 | def _2 = name 22 | 23 | // Not used by pattern matching: Product is only used as a marker trait. 24 | def canEqual(that: Any): Boolean = ??? 25 | def productArity: Int = ??? 26 | def productElement(n: Int): Any = ??? 27 | } 28 | 29 | object Person { 30 | def unapply(a: (String, Int)): Person = new Person(a._1, a._2) 31 | } 32 | 33 | } 34 | 35 | object seqPattern { 36 | 37 | // adapted from http://danielwestheide.com/blog/2012/11/28/the-neophytes-guide-to-scala-part-2-extracting-sequences.html 38 | object Names { 39 | def unapplySeq(name: String): Option[Seq[String]] = { 40 | val names = name.trim.split(" ") 41 | if (names.size < 2) None 42 | else Some(names.last :: names.head :: names.drop(1).dropRight(1).toList) 43 | } 44 | } 45 | 46 | } 47 | 48 | object namePattern { 49 | 50 | class Name(val name: String) { 51 | def get: String = name 52 | def isEmpty = name.isEmpty 53 | } 54 | 55 | object Name { 56 | def unapply(s: String): Name = new Name(s) 57 | } 58 | 59 | } 60 | 61 | def test: Unit = { 62 | 63 | import booleanPattern._ 64 | 65 | "even" match { 66 | case s @ Even() => println(s"$s has an even number of characters") 67 | case s => println(s"$s has an odd number of characters") 68 | } 69 | 70 | // http://dotty.epfl.ch/docs/reference/changed/vararg-patterns.html 71 | def containsConsecutive(list: List[Int]): Boolean = list match { 72 | case List(a, b, xs: _ *) => if (a == b) true else containsConsecutive(b :: xs.toList) 73 | case List(a, _ : _*) => false 74 | case Nil => false 75 | } 76 | 77 | println(containsConsecutive(List(1, 2, 3, 4, 5))) 78 | println(containsConsecutive(List(1, 2, 3, 3, 5))) 79 | 80 | import productPattern._ 81 | ("john", 42) match { 82 | case Person(n, a) => println(s"name: $n, age: $a") 83 | } 84 | 85 | import seqPattern._ 86 | 87 | def greet(fullName: String) = fullName match { 88 | case Names(lastName, firstName, _: _*) => "Good morning, " + firstName + " " + lastName + "!" 89 | case _ => "Welcome! Please make sure to fill in your name!" 90 | } 91 | 92 | println(greet("Alan Turing")) 93 | println(greet("john")) 94 | println(greet("Wolfgang Amadeus Mozart")) 95 | 96 | import namePattern._ 97 | "alice" match { 98 | case Name(n) => println(s"name is $n") 99 | case _ => println("empty name") 100 | } 101 | 102 | } 103 | } -------------------------------------------------------------------------------- /examples/src/StructuralTypes.scala: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Structural Types: https://dotty.epfl.ch/docs/reference/changed-features/structural-types.html 4 | */ 5 | object StructuralTypes { 6 | 7 | case class Record(elems: (String, Any)*) extends Selectable { 8 | def selectDynamic(name: String): Any = elems.find(_._1 == name).get._2 9 | } 10 | 11 | type Person = Record { 12 | val name: String 13 | val age: Int 14 | } 15 | 16 | val person = Record("name" -> "Emma", "age" -> 42, "salary" -> 320L).asInstanceOf[Person] 17 | 18 | val invalidPerson = Record("name" -> "John", "salary" -> 42).asInstanceOf[Person] 19 | 20 | def test: Unit = { 21 | println(person.name) 22 | println(person.age) 23 | 24 | println(invalidPerson.name) 25 | // age field is java.util.NoSuchElementException: None.get 26 | //println(invalidPerson.age) 27 | } 28 | } -------------------------------------------------------------------------------- /examples/src/TraitParams.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Trait Parameters: https://dotty.epfl.ch/docs/reference/other-new-features/trait-parameters.html 3 | */ 4 | object TraitParams { 5 | 6 | trait Base(val msg: String) 7 | class A extends Base("Hello") 8 | class B extends Base("Dotty!") 9 | 10 | // Union types only exist in Dotty, so there's no chance that this will accidentally be compiled with Scala 2 11 | private def printMessages(msgs: (A | B)*) = println(msgs.map(_.msg).mkString(" ")) 12 | 13 | def test: Unit = { 14 | 15 | printMessages(new A, new B) 16 | 17 | // Sanity check the classpath: this won't run if the dotty jar is not present. 18 | val x: Int => Int = z => z 19 | x(1) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/src/TypeLambdas.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Type Lambdas: https://dotty.epfl.ch/docs/reference/new-types/type-lambdas.html 3 | */ 4 | object TypeLambdas { 5 | 6 | type T[+X, Y] = Map[Y, X] 7 | 8 | type Tuple = [X] =>> (X, X) 9 | 10 | def test: Unit = { 11 | 12 | val m: T[String, Int] = Map(1 -> "1") 13 | println(m) 14 | 15 | val tuple: Tuple[String] = ("a", "b") 16 | println(tuple) 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /examples/src/UnionTypes.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Union Types: https://dotty.epfl.ch/docs/reference/new-types/union-types.html 3 | */ 4 | object UnionTypes { 5 | 6 | sealed trait Division 7 | final case class DivisionByZero(msg: String) extends Division 8 | final case class Success(double: Double) extends Division 9 | 10 | // You can create type aliases for your union types (sum types). 11 | type DivisionResult = DivisionByZero | Success 12 | 13 | sealed trait List[+A] 14 | final case class Empty() extends List[Nothing] 15 | final case class Cons[+A](h: A, t: List[A]) extends List[A] 16 | 17 | private def safeDivide(a: Double, b: Double): DivisionResult = { 18 | if (b == 0) DivisionByZero("DivisionByZeroException") else Success(a / b) 19 | } 20 | 21 | private def either(division: Division) = division match { 22 | case DivisionByZero(m) => Left(m) 23 | case Success(d) => Right(d) 24 | } 25 | 26 | def test: Unit = { 27 | 28 | val divisionResultSuccess: DivisionResult = safeDivide(4, 2) 29 | 30 | // commutative 31 | val divisionResultFailure: Success | DivisionByZero = safeDivide(4, 0) 32 | 33 | // calling `either` function with union typed value. 34 | println(either(divisionResultSuccess)) 35 | 36 | // calling `either` function with union typed value. 37 | println(either(divisionResultFailure)) 38 | 39 | val list: Cons[Int] | Empty = Cons(1, Cons(2, Cons(3, Empty()))) 40 | val emptyList: Empty | Cons[Any] = Empty() 41 | println(list) 42 | println(emptyList) 43 | 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /mill: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # This is a wrapper script, that automatically download mill from GitHub release pages 4 | # You can give the required mill version with --mill-version parameter 5 | # If no version is given, it falls back to the value of DEFAULT_MILL_VERSION 6 | # 7 | # Project page: https://github.com/lefou/millw 8 | # Script Version: 0.4.7 9 | # 10 | # If you want to improve this script, please also contribute your changes back! 11 | # 12 | # Licensed under the Apache License, Version 2.0 13 | 14 | set -e 15 | 16 | if [ -z "${DEFAULT_MILL_VERSION}" ] ; then 17 | DEFAULT_MILL_VERSION="0.11.0" 18 | fi 19 | 20 | 21 | if [ -z "${GITHUB_RELEASE_CDN}" ] ; then 22 | GITHUB_RELEASE_CDN="" 23 | fi 24 | 25 | 26 | MILL_REPO_URL="https://github.com/com-lihaoyi/mill" 27 | 28 | if [ -z "${CURL_CMD}" ] ; then 29 | CURL_CMD=curl 30 | fi 31 | 32 | # Explicit commandline argument takes precedence over all other methods 33 | if [ "$1" = "--mill-version" ] ; then 34 | shift 35 | if [ "x$1" != "x" ] ; then 36 | MILL_VERSION="$1" 37 | shift 38 | else 39 | echo "You specified --mill-version without a version." 1>&2 40 | echo "Please provide a version that matches one provided on" 1>&2 41 | echo "${MILL_REPO_URL}/releases" 1>&2 42 | false 43 | fi 44 | fi 45 | 46 | # Please note, that if a MILL_VERSION is already set in the environment, 47 | # We reuse it's value and skip searching for a value. 48 | 49 | # If not already set, read .mill-version file 50 | if [ -z "${MILL_VERSION}" ] ; then 51 | if [ -f ".mill-version" ] ; then 52 | MILL_VERSION="$(head -n 1 .mill-version 2> /dev/null)" 53 | elif [ -f ".config/mill-version" ] ; then 54 | MILL_VERSION="$(head -n 1 .config/mill-version 2> /dev/null)" 55 | fi 56 | fi 57 | 58 | if [ -z "${MILL_DOWNLOAD_PATH}" ] ; then 59 | if [ -n "${XDG_CACHE_HOME}" ] ; then 60 | MILL_DOWNLOAD_PATH="${XDG_CACHE_HOME}/mill/download" 61 | else 62 | MILL_DOWNLOAD_PATH="${HOME}/.cache/mill/download" 63 | fi 64 | fi 65 | 66 | # If not already set, try to fetch newest from Github 67 | if [ -z "${MILL_VERSION}" ] ; then 68 | # TODO: try to load latest version from release page 69 | echo "No mill version specified." 1>&2 70 | echo "You should provide a version via '.mill-version' file or --mill-version option." 1>&2 71 | 72 | mkdir -p "${MILL_DOWNLOAD_PATH}" 73 | LANG=C touch -d '1 hour ago' "${MILL_DOWNLOAD_PATH}/.expire_latest" 2>/dev/null || ( 74 | # we might be on OSX or BSD which don't have -d option for touch 75 | # but probably a -A [-][[hh]mm]SS 76 | touch "${MILL_DOWNLOAD_PATH}/.expire_latest"; touch -A -010000 "${MILL_DOWNLOAD_PATH}/.expire_latest" 77 | ) || ( 78 | # in case we still failed, we retry the first touch command with the intention 79 | # to show the (previously suppressed) error message 80 | LANG=C touch -d '1 hour ago' "${MILL_DOWNLOAD_PATH}/.expire_latest" 81 | ) 82 | 83 | # POSIX shell variant of bash's -nt operator, see https://unix.stackexchange.com/a/449744/6993 84 | # if [ "${MILL_DOWNLOAD_PATH}/.latest" -nt "${MILL_DOWNLOAD_PATH}/.expire_latest" ] ; then 85 | if [ -n "$(find -L "${MILL_DOWNLOAD_PATH}/.latest" -prune -newer "${MILL_DOWNLOAD_PATH}/.expire_latest")" ]; then 86 | # we know a current latest version 87 | MILL_VERSION=$(head -n 1 "${MILL_DOWNLOAD_PATH}"/.latest 2> /dev/null) 88 | fi 89 | 90 | if [ -z "${MILL_VERSION}" ] ; then 91 | # we don't know a current latest version 92 | echo "Retrieving latest mill version ..." 1>&2 93 | LANG=C ${CURL_CMD} -s -i -f -I ${MILL_REPO_URL}/releases/latest 2> /dev/null | grep --ignore-case Location: | sed s'/^.*tag\///' | tr -d '\r\n' > "${MILL_DOWNLOAD_PATH}/.latest" 94 | MILL_VERSION=$(head -n 1 "${MILL_DOWNLOAD_PATH}"/.latest 2> /dev/null) 95 | fi 96 | 97 | if [ -z "${MILL_VERSION}" ] ; then 98 | # Last resort 99 | MILL_VERSION="${DEFAULT_MILL_VERSION}" 100 | echo "Falling back to hardcoded mill version ${MILL_VERSION}" 1>&2 101 | else 102 | echo "Using mill version ${MILL_VERSION}" 1>&2 103 | fi 104 | fi 105 | 106 | MILL="${MILL_DOWNLOAD_PATH}/${MILL_VERSION}" 107 | 108 | try_to_use_system_mill() { 109 | MILL_IN_PATH="$(command -v mill || true)" 110 | 111 | if [ -z "${MILL_IN_PATH}" ]; then 112 | return 113 | fi 114 | 115 | UNIVERSAL_SCRIPT_MAGIC="@ 2>/dev/null # 2>nul & echo off & goto BOF" 116 | 117 | if ! head -c 128 "${MILL_IN_PATH}" | grep -qF "${UNIVERSAL_SCRIPT_MAGIC}"; then 118 | if [ -n "${MILLW_VERBOSE}" ]; then 119 | echo "Could not determine mill version of ${MILL_IN_PATH}, as it does not start with the universal script magic2" 1>&2 120 | fi 121 | return 122 | fi 123 | 124 | # Roughly the size of the universal script. 125 | MILL_VERSION_SEARCH_RANGE="2403" 126 | MILL_IN_PATH_VERSION=$(head -c "${MILL_VERSION_SEARCH_RANGE}" "${MILL_IN_PATH}" |\ 127 | sed -n 's/^.*-DMILL_VERSION=\([^\s]*\) .*$/\1/p' |\ 128 | head -n 1) 129 | 130 | if [ -z "${MILL_IN_PATH_VERSION}" ]; then 131 | echo "Could not determine mill version, even though ${MILL_IN_PATH} has the universal script magic" 1>&2 132 | return 133 | fi 134 | 135 | if [ "${MILL_IN_PATH_VERSION}" = "${MILL_VERSION}" ]; then 136 | MILL="${MILL_IN_PATH}" 137 | fi 138 | } 139 | try_to_use_system_mill 140 | 141 | # If not already downloaded, download it 142 | if [ ! -s "${MILL}" ] ; then 143 | 144 | # support old non-XDG download dir 145 | MILL_OLD_DOWNLOAD_PATH="${HOME}/.mill/download" 146 | OLD_MILL="${MILL_OLD_DOWNLOAD_PATH}/${MILL_VERSION}" 147 | if [ -x "${OLD_MILL}" ] ; then 148 | MILL="${OLD_MILL}" 149 | else 150 | case $MILL_VERSION in 151 | 0.0.* | 0.1.* | 0.2.* | 0.3.* | 0.4.* ) 152 | DOWNLOAD_SUFFIX="" 153 | DOWNLOAD_FROM_MAVEN=0 154 | ;; 155 | 0.5.* | 0.6.* | 0.7.* | 0.8.* | 0.9.* | 0.10.* | 0.11.0-M* ) 156 | DOWNLOAD_SUFFIX="-assembly" 157 | DOWNLOAD_FROM_MAVEN=0 158 | ;; 159 | *) 160 | DOWNLOAD_SUFFIX="-assembly" 161 | DOWNLOAD_FROM_MAVEN=1 162 | ;; 163 | esac 164 | 165 | DOWNLOAD_FILE=$(mktemp mill.XXXXXX) 166 | 167 | if [ "$DOWNLOAD_FROM_MAVEN" = "1" ] ; then 168 | DOWNLOAD_URL="https://repo1.maven.org/maven2/com/lihaoyi/mill-dist/${MILL_VERSION}/mill-dist-${MILL_VERSION}.jar" 169 | else 170 | MILL_VERSION_TAG=$(echo $MILL_VERSION | sed -E 's/([^-]+)(-M[0-9]+)?(-.*)?/\1\2/') 171 | DOWNLOAD_URL="${GITHUB_RELEASE_CDN}${MILL_REPO_URL}/releases/download/${MILL_VERSION_TAG}/${MILL_VERSION}${DOWNLOAD_SUFFIX}" 172 | unset MILL_VERSION_TAG 173 | fi 174 | 175 | # TODO: handle command not found 176 | echo "Downloading mill ${MILL_VERSION} from ${DOWNLOAD_URL} ..." 1>&2 177 | ${CURL_CMD} -f -L -o "${DOWNLOAD_FILE}" "${DOWNLOAD_URL}" 178 | chmod +x "${DOWNLOAD_FILE}" 179 | mkdir -p "${MILL_DOWNLOAD_PATH}" 180 | mv "${DOWNLOAD_FILE}" "${MILL}" 181 | 182 | unset DOWNLOAD_FILE 183 | unset DOWNLOAD_SUFFIX 184 | fi 185 | fi 186 | 187 | if [ -z "$MILL_MAIN_CLI" ] ; then 188 | MILL_MAIN_CLI="${0}" 189 | fi 190 | 191 | MILL_FIRST_ARG="" 192 | if [ "$1" = "--bsp" ] || [ "$1" = "-i" ] || [ "$1" = "--interactive" ] || [ "$1" = "--no-server" ] || [ "$1" = "--repl" ] || [ "$1" = "--help" ] ; then 193 | # Need to preserve the first position of those listed options 194 | MILL_FIRST_ARG=$1 195 | shift 196 | fi 197 | 198 | unset MILL_DOWNLOAD_PATH 199 | unset MILL_OLD_DOWNLOAD_PATH 200 | unset OLD_MILL 201 | unset MILL_VERSION 202 | unset MILL_REPO_URL 203 | 204 | # We don't quote MILL_FIRST_ARG on purpose, so we can expand the empty value without quotes 205 | # shellcheck disable=SC2086 206 | exec "${MILL}" $MILL_FIRST_ARG -D "mill.main.cli=${MILL_MAIN_CLI}" "$@" 207 | --------------------------------------------------------------------------------