From cd838dbaf3a5150a6c9371d5d08a7a6466a82fb2 Mon Sep 17 00:00:00 2001 From: Magnus Madsen Date: Mon, 29 Jul 2024 11:22:57 +0200 Subject: [PATCH 01/22] . --- src/interoperability.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interoperability.md b/src/interoperability.md index 3e25d29..8e8fa5a 100644 --- a/src/interoperability.md +++ b/src/interoperability.md @@ -1,6 +1,6 @@ # Interoperability with Java -Flix is [Java Virtual Machine](https://en.wikipedia.org/wiki/Java_virtual_machine) (JVM)-based programming language, +Flix is a [Java Virtual Machine](https://en.wikipedia.org/wiki/Java_virtual_machine) (JVM)-based programming language, hence: - Flix programs compile to efficient JVM bytecode. From ab239b143a12d941cec06bdc820563cb9cd94a76 Mon Sep 17 00:00:00 2001 From: Magnus Madsen Date: Mon, 29 Jul 2024 12:03:49 +0200 Subject: [PATCH 02/22] work on creating objects --- src/creating-objects.md | 71 ++++++++++++++++++++++++++--------------- src/interoperability.md | 13 ++------ 2 files changed, 48 insertions(+), 36 deletions(-) diff --git a/src/creating-objects.md b/src/creating-objects.md index 52cbe28..fb6fa20 100644 --- a/src/creating-objects.md +++ b/src/creating-objects.md @@ -1,41 +1,60 @@ ## Creating Objects -We can import the constructor of a Java class as a -Flix function and use it to construct new objects. +In Flix, we can create a objects using syntax similar to Java. For example: ```flix -import java_new java.io.File(String): ##java.io.File \ IO as newFile; -newFile("HelloWorld.txt") +import java.io.File + +def main(): Unit \ IO = + let f = new File("foo.txt"); + println("Hello World!") ``` -Here we import the constructor of the `java.io.File` -class and give it the local name `newFile`. -The `newFile` function takes a string argument and -returns a fresh Java `File` object. -Constructing a fresh object is impure, hence `main` -is marked as having the `IO` effect. +Here we import the `java.io.File` class and instantiate a `File` object by +calling one of its constructor using the `new` keyword. + +The `File` class has multiple constructors, so we can also write: + +```flix +import java.io.File + +def main(): Unit \ IO = + let f1 = new File("foo.txt"); + let f2 = new File("bar", "foo.txt"); + println("Hello World!") +``` -When we import a constructor, we must specify the -types of its formal parameters. This is required because -Java supports constructor overloading (i.e. a class may -have multiple constructors only distinguished by their -formal parameters.) +Flix resolves the constructor based on the number of arguments and their types. -For example, the `java.io.File` class has another -constructor that takes two arguments: one for the parent -pathname and one for the child pathname. -We can use this constructor as follows: +As a final example, we can write: ```flix -import java_new java.io.File(String, String): ##java.io.File \ IO as newFile; -newFile("foo", "HelloWorld.txt") +import java.io.File +import java.net.URI + +def main(): Unit \ IO = + let f1 = new File("foo.txt"); + let f2 = new File("bar", "foo.txt"); + let f3 = new File(new URI("file://foo.txt")); + println("Hello World!") +``` + +We can use a _renaming import_ to resolve a clash between a Java name and a Flix +module: + +```flix +import java.lang.{String => JString} + +def main(): Unit \ IO = + let s = new JString("Hello World"); + println("Hello World!") ``` -Here the import describes that the constructor expects two -`String` arguments. +Here `JString` refers to the Java class `java.lang.String` whereas `String` +refers to the Flix module. Note that internally Flix and Java strings are the +same. -> **Note:** `import` statements must occur at the expression-level, -> i.e. they must occur inside a function. Unlike `use` declarations, -> they cannot occur at top of a module. +> **Note:** In Flix, Java classes must be `import`ed before they can be used. +> Specifically, we _cannot_ write `new java.io.File(...)`. diff --git a/src/interoperability.md b/src/interoperability.md index 8e8fa5a..dd132cf 100644 --- a/src/interoperability.md +++ b/src/interoperability.md @@ -17,7 +17,8 @@ Flix supports most Java features necessary for interoperability: Thus Flix programs can reuse Java Class Library and have access to the Java ecosystem. -Flix and Java share the same base types, in particular: +Flix and Java share the same base types, but they have different names, as shown +in the table: | Flix Type | Java Type | |-----------|-----------| @@ -35,12 +36,4 @@ In Flix primitive types are always unboxed. Hence, to call a Java method that expects a `java.lang.Integer`, if you have a Flix `Int32`, it must be boxed by calling `java.lang.Integer.valueOf`. -> **Design Note:** Unlike other programming languages that target the JVM, -> Flix does not aim to embed the Java type system within Flix. -> Instead, Flix sacrifices some convenience to stay true to its design goals. -> In particular, the Flix type system does not support sub-typing. -> Consequently, unlike in Java, a sub-type cannot be used where its super-type is expected. -> For example, `java.lang.String` is incompatible with `java.lang.Object`. -> Fortunately, this limitation can be overcome by using [upcasts](./upcast.md). - -[^1]: Flix currently targets Java 11. Once Project Loom is released, we will target that version. +[^1]: Flix requires at least Java 21. From 2f03d205c7e1f236ca40c71c283c5fdf515d8538 Mon Sep 17 00:00:00 2001 From: Magnus Madsen Date: Mon, 29 Jul 2024 12:11:06 +0200 Subject: [PATCH 03/22] work on creating objects --- src/creating-objects.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/creating-objects.md b/src/creating-objects.md index fb6fa20..22cd432 100644 --- a/src/creating-objects.md +++ b/src/creating-objects.md @@ -1,6 +1,6 @@ ## Creating Objects -In Flix, we can create a objects using syntax similar to Java. +In Flix, we can create objects using syntax similar to Java. For example: From c5c90274ad4d3428fe88f98885d9400e6e2f2de1 Mon Sep 17 00:00:00 2001 From: Magnus Madsen Date: Mon, 29 Jul 2024 12:46:05 +0200 Subject: [PATCH 04/22] work on calling methods --- src/calling-methods.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/calling-methods.md b/src/calling-methods.md index f89ec08..8045632 100644 --- a/src/calling-methods.md +++ b/src/calling-methods.md @@ -1,5 +1,23 @@ ## Invoking Object Methods +In Flix, we can call methods on objects using syntax similar to Java. + +For example: + +```flix +import java.io.File + +def main(): Unit \ IO = + let f = new File("foo.txt"); + println(f.getName()) +``` + +Here we import the `java.io.File` class, instantiate a `File` object, and then +call the `getName` method on that object. + + +
+ We can use the import mechanism to invoke methods on objects. For example: @@ -62,3 +80,4 @@ import static java.lang.String.valueOf(Bool): String \ {}; valueOf(true) ``` +
\ No newline at end of file From 8838b828d8766f6402f6ce49f3fb8802eed7a1ee Mon Sep 17 00:00:00 2001 From: Magnus Madsen Date: Tue, 30 Jul 2024 13:18:08 +0200 Subject: [PATCH 05/22] work on calling methods --- src/calling-methods.md | 77 ++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 40 deletions(-) diff --git a/src/calling-methods.md b/src/calling-methods.md index 8045632..71cf7b8 100644 --- a/src/calling-methods.md +++ b/src/calling-methods.md @@ -15,63 +15,60 @@ def main(): Unit \ IO = Here we import the `java.io.File` class, instantiate a `File` object, and then call the `getName` method on that object. +Like with constructors, Flix resolves the method based on the number of +arguments and their types. -
- -We can use the import mechanism to invoke methods on objects. - -For example: +Here is another example: ```flix -import java_new java.io.File(String): ##java.io.File \ IO as newFile; -import java.io.File.exists(): Bool \ IO as fileExists; -let f = newFile("HelloWorld.txt"); -fileExists(f) -``` - -Here we import the `java.io.File.exists` method under the name `fileExists`. - -If the Java method name is a legal Flix name and we want to reuse it, -we can also import the method without an `as` clause. For example: +import java.io.File -```flix -import java_new java.io.File(String): ##java.io.File \ IO as newFile; -import java.io.File.exists(): Bool \ IO; -let f = newFile("HelloWorld.txt"); -exists(f) +def main(): Unit \ IO = + let f = new File("foo.txt"); + if (f.exists()) + println("The file ${f.getName()} exists!") + else + println("The file ${f.getName()} does not exist!") ``` -Here we import the method under the name `exists`. - -When a Java method is imported, we must annotate it with its effect. -Most commonly, a Java method has a side-effect (such as deleting a file), -and hence must be annotated with the `IO` effect. - -In rare cases where a method is pure, we can import it as such by -writing the empty effect set: `{}`. For example: +And here is a larger example: ```flix -import java.lang.String.startsWith(String): Bool \ {}; -startsWith("Hello World", "Hello") +import java.io.File +import java.io.FileWriter + +def main(): Unit \ IO = + let f = new File("foo.txt"); + let w = new FileWriter(f); + w.append("Hello World\n"); + w.close() ``` -And as another example: +In the above example, we may want to catch the `IOException` that can be raised: ```flix -import java.lang.String.charAt(Int32): Char \ {}; -charAt("Hello World", 2) +import java.io.File +import java.io.FileWriter +import java.io.IOException + +def main(): Unit \ IO = + let f = new File("foo.txt"); + try { + let w = new FileWriter(f); + w.append("Hello World\n"); + w.close() + } catch { + case _: IOException => println("Unable to write to file: ${f.getName()}") + } ``` -Type signatures should use Flix type names and not -Java type names for primitive types. -For example, if a Java method takes a `Double` its -signature should use the Flix type `Float64`. -Similarly, if a Java method takes a `Boolean` its -signature should use the Flix type `Bool`. -This goes for return types, too. ## Invoking Static Methods + +
+ + We can invoke a _static_ method by writing the `static` keyword after import: From 5923445f66509e72a65e6651044ec814895c9335 Mon Sep 17 00:00:00 2001 From: Magnus Madsen Date: Tue, 30 Jul 2024 13:30:04 +0200 Subject: [PATCH 06/22] work on calling methods --- src/calling-methods.md | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/calling-methods.md b/src/calling-methods.md index 71cf7b8..5ae2cc3 100644 --- a/src/calling-methods.md +++ b/src/calling-methods.md @@ -58,23 +58,39 @@ def main(): Unit \ IO = w.append("Hello World\n"); w.close() } catch { - case _: IOException => println("Unable to write to file: ${f.getName()}") + case ex: IOException => + println("Unable to write to file: ${f.getName()}"); + println("The error message was: ${ex.getMessage()}") } ``` - ## Invoking Static Methods +In Flix, we can call static methods (i.e. class methods) using syntax similar to Java: + +For example: -
+```flix +import java.lang.Math +def main(): Unit \ IO = + let n = Math.sin(3.14); + println(n) -We can invoke a _static_ method by writing the -`static` keyword after import: +``` + +Like with constructors and methods, Flix resolves the static method based on the +number of arguments and their types. + +Here is another example: ```flix -import static java.lang.String.valueOf(Bool): String \ {}; -valueOf(true) +import java.lang.Math + +def main(): Unit \ IO = + println(Math.abs(-123i32)); + println(Math.abs(-123i64)); + println(Math.abs(-123.456f32)); + println(Math.abs(-123.456f64)) ``` -
\ No newline at end of file From 6b94eecce3fbe421a8a9dd1d9aa06e57b21f937f Mon Sep 17 00:00:00 2001 From: Magnus Madsen Date: Tue, 30 Jul 2024 13:31:56 +0200 Subject: [PATCH 07/22] work on calling methods --- src/calling-methods.md | 2 ++ src/creating-objects.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/calling-methods.md b/src/calling-methods.md index 5ae2cc3..dbd5e8b 100644 --- a/src/calling-methods.md +++ b/src/calling-methods.md @@ -1,5 +1,7 @@ ## Invoking Object Methods +> **Note:** Requires Flix 0.49.0 + In Flix, we can call methods on objects using syntax similar to Java. For example: diff --git a/src/creating-objects.md b/src/creating-objects.md index 22cd432..58159a2 100644 --- a/src/creating-objects.md +++ b/src/creating-objects.md @@ -1,5 +1,7 @@ ## Creating Objects +> **Note:** Requires Flix 0.49.0 + In Flix, we can create objects using syntax similar to Java. For example: From d95e2e1cac470e3c2f47e39c10155b19ae8f350a Mon Sep 17 00:00:00 2001 From: Magnus Madsen Date: Tue, 30 Jul 2024 13:40:40 +0200 Subject: [PATCH 08/22] work on calling methods --- src/calling-methods.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/calling-methods.md b/src/calling-methods.md index dbd5e8b..2a9fa37 100644 --- a/src/calling-methods.md +++ b/src/calling-methods.md @@ -96,3 +96,32 @@ def main(): Unit \ IO = println(Math.abs(-123.456f64)) ``` +## Invoking Java Methods Known to be Pure + +Any Flix expression that creates a Java object, calls an object method, or calls +a static method gets the `IO` effect. This is to be expected: Java code may have +side-effects on the environment. + +In rare cases, if we know for certain that a Java method has no side-effects, we +can use an `unsafe` block to inform Flix to treat that expression as pure. + +For example: + +```flix +import java.lang.Math + +def pythagoras(x: Float64, y: Float64): Float64 = + unsafe Math.sqrt((Math.pow(x, 2.0) + Math.pow(y, 2.0))) + +def main(): Unit \ IO = + println(pythagoras(3.0, 4.0)) +``` + +Here we know for certain that `Math.pow` and `Math.sqrt` are _pure_ functions, +hence we can put them inside an `unsafe` block. Thus we are able to type the +Flix `pythagoras` function as pure, i.e. without the `IO` effect. + +> **Warning:** Do _not_, under any circumstances, use `unsafe` on expressions +> that have side-effects. Doing so breaks the type and effect system which can +> lead to incorrect compiler optimizations which can change the meaning of your +> program in subtle or catastrophic ways! From 98cb0e3b6c619f890e999761520ed1bfe5762aa7 Mon Sep 17 00:00:00 2001 From: Magnus Madsen Date: Tue, 30 Jul 2024 13:43:54 +0200 Subject: [PATCH 09/22] work on calling methods --- src/calling-methods.md | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/calling-methods.md b/src/calling-methods.md index 2a9fa37..41afcfb 100644 --- a/src/calling-methods.md +++ b/src/calling-methods.md @@ -98,19 +98,20 @@ def main(): Unit \ IO = ## Invoking Java Methods Known to be Pure -Any Flix expression that creates a Java object, calls an object method, or calls -a static method gets the `IO` effect. This is to be expected: Java code may have -side-effects on the environment. +Any Flix expression that creates a Java object, calls a Java method, or calls a +Java static method has the `IO` effect. This is to be expected: Java +constructors and methods may have arbitrary side-effects. -In rare cases, if we know for certain that a Java method has no side-effects, we -can use an `unsafe` block to inform Flix to treat that expression as pure. +In rare cases, if we know for certain that a Java constructor or method +invocation has no side-effects, we can use an `unsafe` block to tell Flix to +treat that expression as pure. For example: ```flix import java.lang.Math -def pythagoras(x: Float64, y: Float64): Float64 = +def pythagoras(x: Float64, y: Float64): Float64 = // Pure, no IO effect unsafe Math.sqrt((Math.pow(x, 2.0) + Math.pow(y, 2.0))) def main(): Unit \ IO = @@ -118,10 +119,10 @@ def main(): Unit \ IO = ``` Here we know for certain that `Math.pow` and `Math.sqrt` are _pure_ functions, -hence we can put them inside an `unsafe` block. Thus we are able to type the -Flix `pythagoras` function as pure, i.e. without the `IO` effect. +hence we can put them inside an `unsafe` block. Thus we are able to type check +the Flix `pythagoras` function as pure, i.e. without the `IO` effect. -> **Warning:** Do _not_, under any circumstances, use `unsafe` on expressions -> that have side-effects. Doing so breaks the type and effect system which can -> lead to incorrect compiler optimizations which can change the meaning of your +> **Warning:** Do not, under any circumstances, use `unsafe` on expressions that +> have side-effects. Doing so breaks the type and effect system which can lead +> to incorrect compiler optimizations which can change the meaning of your > program in subtle or catastrophic ways! From 571e8e3dba024fcf32a34c5ee8ac5a1a4e457707 Mon Sep 17 00:00:00 2001 From: Magnus Madsen Date: Tue, 30 Jul 2024 17:33:19 +0200 Subject: [PATCH 10/22] fix typos --- src/calling-methods.md | 20 ++++++++++++++++---- src/creating-objects.md | 2 +- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/calling-methods.md b/src/calling-methods.md index 41afcfb..7b5d6df 100644 --- a/src/calling-methods.md +++ b/src/calling-methods.md @@ -2,7 +2,7 @@ > **Note:** Requires Flix 0.49.0 -In Flix, we can call methods on objects using syntax similar to Java. +In Flix, we can call methods on Java objects using syntax similar to Java. For example: @@ -96,15 +96,27 @@ def main(): Unit \ IO = println(Math.abs(-123.456f64)) ``` +## Callings Methods with VarArgs + +TBD + +## Dealing with Boxing and Unboxing + +TBD + +## When Constructor or Method Resolution Fails + +TBD + ## Invoking Java Methods Known to be Pure Any Flix expression that creates a Java object, calls a Java method, or calls a Java static method has the `IO` effect. This is to be expected: Java constructors and methods may have arbitrary side-effects. -In rare cases, if we know for certain that a Java constructor or method -invocation has no side-effects, we can use an `unsafe` block to tell Flix to -treat that expression as pure. +If we know for certain that a Java constructor or method invocation has no +side-effects, we can use an `unsafe` block to tell Flix to treat that expression +as pure. For example: diff --git a/src/creating-objects.md b/src/creating-objects.md index 58159a2..17f8da1 100644 --- a/src/creating-objects.md +++ b/src/creating-objects.md @@ -15,7 +15,7 @@ def main(): Unit \ IO = ``` Here we import the `java.io.File` class and instantiate a `File` object by -calling one of its constructor using the `new` keyword. +calling one of its constructors using the `new` keyword. The `File` class has multiple constructors, so we can also write: From d2f22720135974e8a6a93f8ba53c9b6a409ae9c7 Mon Sep 17 00:00:00 2001 From: Magnus Madsen Date: Wed, 31 Jul 2024 09:18:28 +0200 Subject: [PATCH 11/22] write on exceptions --- src/exceptions.md | 70 ++++++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 38 deletions(-) diff --git a/src/exceptions.md b/src/exceptions.md index 7bb948c..b3e2b2e 100644 --- a/src/exceptions.md +++ b/src/exceptions.md @@ -1,52 +1,46 @@ ## Exceptions -In Flix, all error handling should be done using the `Result[e, t]` type. -However, for interoperability with Java, Flix also has a classic `try-catch` -mechanism. +> **Note:** Requires Flix 0.49.0 -For example, we can write: +In Flix, we can catch Java exceptions using a `try-catch` construct similar to +the one in Java. The syntax is slightly different, but semantically equivalent +to Java. + +For example: ```flix -/// -/// Returns `true` if the given file `f` exists. -/// -pub def exists(f: String): Result[String, Bool] \ IO = +import java.io.BufferedReader +import java.io.File +import java.io.FileReader +import java.io.FileNotFoundException +import java.io.IOException + +def main(): Unit \ IO = + let f = new File("foo.txt"); try { - import java_new java.io.File(String): ##java.io.File \ IO as newFile; - import java.io.File.exists(): Bool \ IO; - Ok(exists(newFile(f))) + let r = new BufferedReader(new FileReader(f)); + let l = r.readLine(); + println("The first line of the file is: ${l}"); + r.close() } catch { - case ex: ##java.io.IOException => - import java.lang.Throwable.getMessage(): String \ IO; - Err(getMessage(ex)) + case _: FileNotFoundException => + println("The file does not exist!") + case ex: IOException => + println("The file could not be read!"); + println("The error message was: ${ex.getMessage()}") } ``` -Here we import the `File` constructor as `newFile` and the `File.exists` method -as `exists`. We then call the methods and catch the `IOException`. - -> **Note:** Flix programs should not rely on the exception mechanism. Instead, -> we should guard all call to Java code that might throw exceptions close to -> their call site and turn these exceptions into `Result`s. - -### Structured Concurrency and Exceptions +Here the calls `new FileReader()`, `r.readLine()`, and `r.close()` can throw +`IOException`s. We use a `try-catch` block to catch these exceptions. We add a +special case for the `FileNotFoundException` exception. -Flix supports [structured concurrency](./concurrency.md). This means that (1) -threads cannot outlive the lifetime of their region and (2) that exceptions -thrown in sub-threads are propagated to the thread of the region. +> **Note:** Flix programs should not use exceptions. It is considered bad style. +> Instead, programs should use the `Result[e, t]` type. The `try-catch` +> construct should only be used on the boundary between Flix and Java code. -For example, given the program: +> **Note:** Flix does not (yet) support a `finally` block. -```flix -def main(): Unit \ IO = - region rc { - spawn f() @ rc; - spawn g() @ rc - }; - println("Done") -``` +> **Note:** Flix does not (yet) support throwing Java exceptions. -where `f` and `g` are some functions. If `f` or `g` were to throw an unhandled -exception then that exception would be _caught_ and _rethrown_ inside the `main` -thread. This means that we cannot successfully leave the scope of `rc` _unless_ -`f` and `g` terminated _and_ did not throw any unhandled exceptions. +> **Note:** In Flix a function can contain at most one `try-catch` block. From 2483d487c22a80e1a5525431049e4eefbf61a9d4 Mon Sep 17 00:00:00 2001 From: Magnus Madsen Date: Wed, 31 Jul 2024 09:26:56 +0200 Subject: [PATCH 12/22] write on nested classes --- src/exceptions.md | 2 +- src/nested-and-inner-classes.md | 27 ++++++++++++++++++++++----- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/exceptions.md b/src/exceptions.md index b3e2b2e..d79c399 100644 --- a/src/exceptions.md +++ b/src/exceptions.md @@ -35,7 +35,7 @@ Here the calls `new FileReader()`, `r.readLine()`, and `r.close()` can throw `IOException`s. We use a `try-catch` block to catch these exceptions. We add a special case for the `FileNotFoundException` exception. -> **Note:** Flix programs should not use exceptions. It is considered bad style. +> **Note:** Flix programs should not use exceptions: it is considered bad style. > Instead, programs should use the `Result[e, t]` type. The `try-catch` > construct should only be used on the boundary between Flix and Java code. diff --git a/src/nested-and-inner-classes.md b/src/nested-and-inner-classes.md index 8795ccf..89a3f7f 100644 --- a/src/nested-and-inner-classes.md +++ b/src/nested-and-inner-classes.md @@ -1,19 +1,36 @@ ## Nested and Inner Classes -[Java supports two different types of nested class](https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html): static nested classes and inner classes: +Java supports nested static and non-static inner classes: + +For example: ```java +package Foo.Bar; + class OuterClass { ... class InnerClass { ... } - static class StaticNestedClass { - ... + static class StaticInnerClass { + public static String hello() { return "Hi"; } } } ``` -Although these appear superficially similar, they provide very different functionality. +In Flix, we can access the `StaticInnerClass` using the import statement: + +```flix +import Foo.Bar.{OuterClass$StaticInnerClass => Inner} + +def main(): Unit \ IO = + println(Inner.hello()) +``` + +A typical example is to access the `Map.Entry` class: + +```flix +import java.util.{Map$Entry => Entry} +``` -Flix supports static nested classes, but does not yet support inner classes. To reference a static nested class, use a `$` instead of `.` in the class name, for example `java.util.Locale$Builder`. \ No newline at end of file +> **Note:** Flix does not support accessing nested non-static inner classes. From 1a1e57050f34d7e0bdbbed713e5da1fe028b35f8 Mon Sep 17 00:00:00 2001 From: Magnus Madsen Date: Wed, 31 Jul 2024 09:54:07 +0200 Subject: [PATCH 13/22] minor fixes --- src/exceptions.md | 5 ++--- src/nested-and-inner-classes.md | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/exceptions.md b/src/exceptions.md index d79c399..bae20b9 100644 --- a/src/exceptions.md +++ b/src/exceptions.md @@ -2,9 +2,8 @@ > **Note:** Requires Flix 0.49.0 -In Flix, we can catch Java exceptions using a `try-catch` construct similar to -the one in Java. The syntax is slightly different, but semantically equivalent -to Java. +In Flix, we can catch Java exceptions using the `try-catch` construct. The +construct is similar to the one in Java, but the syntax is slightly different. For example: diff --git a/src/nested-and-inner-classes.md b/src/nested-and-inner-classes.md index 89a3f7f..0d0c6b9 100644 --- a/src/nested-and-inner-classes.md +++ b/src/nested-and-inner-classes.md @@ -18,7 +18,7 @@ class OuterClass { } ``` -In Flix, we can access the `StaticInnerClass` using the import statement: +In Flix, we can access the `StaticInnerClass` using the `import` statement: ```flix import Foo.Bar.{OuterClass$StaticInnerClass => Inner} From 8948a4cba6097c278ba4aac9d633cdb67a6f9839 Mon Sep 17 00:00:00 2001 From: Magnus Madsen Date: Wed, 31 Jul 2024 10:02:18 +0200 Subject: [PATCH 14/22] write on collections --- src/java-collections.md | 51 ++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/src/java-collections.md b/src/java-collections.md index 04ff4e6..8455e97 100644 --- a/src/java-collections.md +++ b/src/java-collections.md @@ -1,6 +1,17 @@ ## Java Collections -Flix has support for conversion to and from Java collections. +Flix has support for conversion from and to Java collections. + +In the following, we use the following import aliases: + +```flix +import java.util.{List => JList} +import java.util.{Set => JSet} +import java.util.{Map => JMap} +``` + +The following functions are available in the +[Adaptor](https://api.flix.dev/Adaptor.html) module: ### Flix to Java @@ -10,26 +21,26 @@ The following functions _convert_ Flix collections to Java collections: /// /// Lists /// -def toList(ma: m[a]): ##java.util.List \ IO with Foldable[m] -def toArrayList(ma: m[a]): ##java.util.ArrayList \ IO with Foldable[m] -def toLinkedList(ma: m[a]): ##java.util.LinkedList \ IO with Foldable[m] +def toList(ma: m[a]): JList \ IO with Foldable[m] +def toArrayList(ma: m[a]): ArrayList \ IO with Foldable[m] +def toLinkedList(ma: m[a]): LinkedList \ IO with Foldable[m] /// /// Sets /// -def toSet(ma: m[a]): ##java.util.Set \ IO with Order[a], Foldable[m] -def toTreeSet(ma: m[a]): ##java.util.TreeSet \ IO with Order[a], Foldable[m] +def toSet(ma: m[a]): Set \ IO with Order[a], Foldable[m] +def toTreeSet(ma: m[a]): TreeSet \ IO with Order[a], Foldable[m] /// /// Maps /// -def toMap(m: Map[k, v]): ##java.util.Map \ IO with Order[k] -def toTreeMap(m: Map[k, v]): ##java.util.TreeMap \ IO with Order[k] +def toMap(m: Map[k, v]): JMap \ IO with Order[k] +def toTreeMap(m: Map[k, v]): TreeMap \ IO with Order[k] ``` Each function constructs a new collection and copies all its elements into it. -Hence each operation takes at least linear time. The upshot is that the result -is a normal Java collection (which can be modified). +Hence each operation takes at least linear time. The result is a normal Java +collection (which can be modified). ### Java to Flix @@ -37,26 +48,20 @@ The following functions _convert_ Java collections to Flix collections: ```flix /// Lists -def fromList(l: ##java.util.List): List[a] +def fromList(l: JList): List[a] /// Sets -def fromSet(l: ##java.util.Set): Set[a] with Order[a] +def fromSet(l: JSet): Set[a] with Order[a] /// Maps -def fromMap(m: ##java.util.Map): Map[k, v] with Order[k] +def fromMap(m: JMap): Map[k, v] with Order[k] ``` Each function constructs a new Flix collection from a Java Collection. Hence each operation takes at least linear time. Note that for `Set` and `Map`, the Flix collections use the `Order[a]` instance defined on `a`. This may not be the -same ordering as used by Java. Thus one has to be careful. - -### Wrapping Flix Collections - -TBD. - -### Primitive Values +same ordering as used by Java. -**Warning:** Converting Flix and/or Java collections with primitive values -requires extra care. In particular, values must be manually boxed or unboxed -before any conversion. \ No newline at end of file +> **Warning:** Converting Flix and/or Java collections with primitive values +> requires extra care. In particular, values must be manually boxed or unboxed +> before any conversion. From d7a704889ffd8d896f3127a109bc5a432d51c29b Mon Sep 17 00:00:00 2001 From: Magnus Madsen Date: Wed, 31 Jul 2024 10:16:46 +0200 Subject: [PATCH 15/22] write on fields --- src/reading-and-writing-fields.md | 66 +++++++++++++++---------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/src/reading-and-writing-fields.md b/src/reading-and-writing-fields.md index 2289416..998541c 100644 --- a/src/reading-and-writing-fields.md +++ b/src/reading-and-writing-fields.md @@ -1,53 +1,53 @@ ## Reading and Writing Fields -We can read and write fields by importing functions that serve as "getters" and "setters". +> **Note:** Requires Flix 0.49.0 -Assume we have the Java class: +Flix supports reading object fields and static (class) fields with standard Java +syntax. -```java -class TestClass { - boolean boolField = true; -} -``` +### Reading Object Fields + +TBD -Then here is how we can access the `boolField`: +### Reading Static Fields + +We can read a static field as follows: ```flix -import java_new flix.test.TestClass(): ##flix.test.TestClass \ IO as newObject; -import java_get_field flix.test.TestClass.boolField: Bool \ IO as getField; -let o = newObject(); -getField(o) +import java.lang.Math + +def area(radius: Float64): Float64 = (unsafe Math.PI) * radius * radius ``` -Here we import the (default, empty) constructor of `TestClass` as `newObject`. -Next, we import the field `boolField` as the function `getField`. We use -`newObject` to construct a fresh object and we call `getField` on it to -obtain the value of `o.boolField`. +We import the `java.lang.Math` class and then we access the static `PI` field. -Writing a field of an object is similar: +We know that the `PI` field will never change, hence we cast away the effect with `unsafe`. + +### Writing Object Fields + +Flix supports writing to an object field with the non-standard syntax: ```flix -import java_new flix.test.TestClass(): ##flix.test.TestClass \ IO as newObject; -import java_get_field flix.test.TestClass.boolField: Bool \ IO as getField; -import java_set_field flix.test.TestClass.boolField: Unit \ IO as setField; -let o = newObject(); -setField(o, false); -getField(o) +import java_set_field foo.bar.Baz.boolField: Unit \ IO as setField; +let o = ...; +setField(o, false) ``` -Here we import both a "getter" and "setter" for the `boolField` field. +Here the `import` *expression* creates a function named `setField` which we +call. + +> **Note**: We are currently working on a more tolerable syntax. -### Reading and Writing Static Fields +### Writing Static Fields -Reading or writing _static_ fields is similar to -reading or writing object fields. -For example: +Flix supports writing to a static field with the non-standard syntax: ```flix -import static java_get_field java.lang.Integer.MIN_VALUE: Int32 \ IO as getMinValue; -getMinValue() +import static java_set_field foo.bar.Baz.StaticBoolField: Unit \ IO as setField; +let o = ...; +setField(o, false) ``` +> **Note**: We are currently working on a more tolerable syntax. -The only difference is to write the -`static` keyword to indicate that the reference is to -a static field. +Here the `import` *expression* creates a function named `setField` which we +call. \ No newline at end of file From e908ab083ded5ae69ac820593b854e31ed3ff98f Mon Sep 17 00:00:00 2001 From: Magnus Madsen Date: Wed, 31 Jul 2024 10:58:36 +0200 Subject: [PATCH 16/22] work on boxing --- src/SUMMARY.md | 1 + src/boxing-and-unboxing.md | 40 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 src/boxing-and-unboxing.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index d0f9a48..c489861 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -52,6 +52,7 @@ - [Classes and Interfaces](./extending-classes-and-interfaces.md) - [Nested and Inner Classes](./nested-and-inner-classes.md) - [Exceptions](./exceptions.md) + - [Boxing and Unboxing](./boxing-and-unboxing.md) - [Java Collections](./java-collections.md) - [Everyday Programming](./everyday-programming.md) - [The Main Function](./main.md) diff --git a/src/boxing-and-unboxing.md b/src/boxing-and-unboxing.md new file mode 100644 index 0000000..6113135 --- /dev/null +++ b/src/boxing-and-unboxing.md @@ -0,0 +1,40 @@ +## Boxing and Unboxing + +> **Note:** Requires Flix 0.49.0 + +Unlike Java, Flix never performs implicit boxing or unboxing of values. We +believe auto boxing is a design flaw and do not plan to support it. + +Consequently, you must explicitly box and unbox values as needed. + +### Boxing + +The following example shows how to manually box a Java `Integer`: + +```flix +import java.lang.Integer +import java.util.Objects + +def hash(x: Int32): Int32 = unsafe { + let boxed = Integer.valueOf(x); + Objects.hashCode(boxed) + } +``` + +We must use `unsafe` to cast away the `IO` effect from the call to +`Integer.valueOf()` and `Objects.hashCode()`. + + +### Unboxing + +The following example shows how to manually unbox two Java `Integer`s: + +```flix +import java.lang.Integer + +def sum(x: Integer, y: Integer): Int32 = + unsafe x.intValue() + y.intValue() +``` + +We must use `unsafe` to cast away the `IO` effect from the call to `intValue()`. + From 13c5ad2ac9f95ef3578597649d3a8946547428ca Mon Sep 17 00:00:00 2001 From: Magnus Madsen Date: Wed, 31 Jul 2024 11:08:07 +0200 Subject: [PATCH 17/22] work on boxing --- src/boxing-and-unboxing.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/boxing-and-unboxing.md b/src/boxing-and-unboxing.md index 6113135..38878ee 100644 --- a/src/boxing-and-unboxing.md +++ b/src/boxing-and-unboxing.md @@ -16,15 +16,14 @@ import java.lang.Integer import java.util.Objects def hash(x: Int32): Int32 = unsafe { - let boxed = Integer.valueOf(x); - Objects.hashCode(boxed) - } + let boxed = Integer.valueOf(x); + Objects.hashCode(boxed) +} ``` We must use `unsafe` to cast away the `IO` effect from the call to `Integer.valueOf()` and `Objects.hashCode()`. - ### Unboxing The following example shows how to manually unbox two Java `Integer`s: From 7f10111c03b0a15bfea5aed8a45fe95bad50dd3d Mon Sep 17 00:00:00 2001 From: Magnus Madsen Date: Thu, 1 Aug 2024 09:13:46 +0200 Subject: [PATCH 18/22] write on calling methods --- src/calling-methods.md | 33 +++++++++++++++++++++++++++++---- src/creating-objects.md | 8 +++++--- src/interoperability.md | 11 +++++++---- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/src/calling-methods.md b/src/calling-methods.md index 7b5d6df..08c68ec 100644 --- a/src/calling-methods.md +++ b/src/calling-methods.md @@ -100,13 +100,38 @@ def main(): Unit \ IO = TBD -## Dealing with Boxing and Unboxing +## When Constructor or Method Resolution Fails -TBD +In some cases the Flix compiler is unable to determine what Java constructor or +method is called. -## When Constructor or Method Resolution Fails +For example, in the program: -TBD +```flix +import java.lang.{String => JString} + +def f(): String \ IO = + let o = ???; + JString.valueOf(o) +``` + +The type of `o` is unknown, hence Flix cannot know if we want to call +`String.valueOf(boolean)`, `String.valueOf(char)`, `String.valueOf(double)`, or +one of the other overloaded versions. + +The solution is to put a type ascription on the relevant argument: + +```flix +import java.lang.{String => JString} + +def f(): String \ IO = + let o = ???; + JString.valueOf((o: Bool)) +``` + +The type ascription specifies that `o` has type `Bool` which allows method +resolution to complete successfully. Note that the extra pair of parenthesis is +required. ## Invoking Java Methods Known to be Pure diff --git a/src/creating-objects.md b/src/creating-objects.md index 17f8da1..7687610 100644 --- a/src/creating-objects.md +++ b/src/creating-objects.md @@ -30,7 +30,7 @@ def main(): Unit \ IO = Flix resolves the constructor based on the number of arguments and their types. -As a final example, we can write: +As another example, we can write: ```flix import java.io.File @@ -58,5 +58,7 @@ Here `JString` refers to the Java class `java.lang.String` whereas `String` refers to the Flix module. Note that internally Flix and Java strings are the same. -> **Note:** In Flix, Java classes must be `import`ed before they can be used. -> Specifically, we _cannot_ write `new java.io.File(...)`. +> **Note:** Any interaction with Java code always has the `IO` effect. + +> **Note:** In Flix, Java classes must be `import`ed before they can be used. In +> particular, we _cannot_ write `new java.io.File(...)`. diff --git a/src/interoperability.md b/src/interoperability.md index dd132cf..059e939 100644 --- a/src/interoperability.md +++ b/src/interoperability.md @@ -9,13 +9,16 @@ hence: Flix supports most Java features necessary for interoperability: -- [Creating objects from existing classes](./creating-objects.md) +- [Creating objects from classes](./creating-objects.md) - [Calling methods on classes and objects](./calling-methods.md) -- [Reading and writing fields on objects](./reading-and-writing-fields.md) +- [Reading and writing fields on classes and objects](./reading-and-writing-fields.md) - [Anonymous extension of classes and interfaces](./extending-classes-and-interfaces.md) -- [Nested and inner classes](./nested-and-inner-classes.md) +- [Accessing inner classes](./nested-and-inner-classes.md) +- [Catching and throwing exceptions](./exceptions.md) +- [Boxing and unboxing of primitive values](./boxing-and-unboxing.md) -Thus Flix programs can reuse Java Class Library and have access to the Java ecosystem. +Thus Flix programs can reuse the Java Class Library. In addition, the Flix +package manager has Maven support. Flix and Java share the same base types, but they have different names, as shown in the table: From e7eb001519f50bec366f4025d70a284f4c0c67a3 Mon Sep 17 00:00:00 2001 From: Magnus Madsen Date: Thu, 1 Aug 2024 09:18:46 +0200 Subject: [PATCH 19/22] work on calling methods --- src/calling-methods.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/calling-methods.md b/src/calling-methods.md index 08c68ec..7d3836b 100644 --- a/src/calling-methods.md +++ b/src/calling-methods.md @@ -133,6 +133,26 @@ The type ascription specifies that `o` has type `Bool` which allows method resolution to complete successfully. Note that the extra pair of parenthesis is required. +## Invoking Object Methods through Static Fields + +We may want to write: + +```flix +import java.lang.System + +def main(): Unit \ IO = + System.out.println("Hello World!") +``` + +But due to a limitation of the Flix parser, the above has to be written as: + +```flix +import java.lang.System + +def main(): Unit \ IO = + (System.out).println("Hello World!") +``` + ## Invoking Java Methods Known to be Pure Any Flix expression that creates a Java object, calls a Java method, or calls a From fe3023cfb554ac71539e62fbe9e892b6e75e69c2 Mon Sep 17 00:00:00 2001 From: Magnus Madsen Date: Thu, 1 Aug 2024 09:28:45 +0200 Subject: [PATCH 20/22] write on partial application --- src/calling-methods.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/calling-methods.md b/src/calling-methods.md index 7d3836b..46ccfc5 100644 --- a/src/calling-methods.md +++ b/src/calling-methods.md @@ -183,3 +183,27 @@ the Flix `pythagoras` function as pure, i.e. without the `IO` effect. > have side-effects. Doing so breaks the type and effect system which can lead > to incorrect compiler optimizations which can change the meaning of your > program in subtle or catastrophic ways! + +## Partial Application of Java Constructors and Methods + +Flix supports partial application of Flix functions. However, Java constructors +and methods can never be partially applied. This limitation can be overcome +introducing an explicit lambda. + +For example: + +```flix +import java.lang.{String => JString} + +def main(): Unit \ IO = + def replaceAll(s, src, dst) = s.replaceAll(src, dst); + let f = replaceAll("Hello World"); + let s1 = f("World")("Galaxy"); + let s2 = f("World")("Universe"); + println(s1); + println(s2) +``` + +Here we introduce a Flix function `replaceAll` which calls `String.replaceAll`. +Since `replaceAll` is a Flix function, we can partially apply it as shown in the +example. From a5dbffcceb9960802c95e345ab44ae5ed4907b8a Mon Sep 17 00:00:00 2001 From: Magnus Madsen Date: Thu, 1 Aug 2024 09:29:44 +0200 Subject: [PATCH 21/22] fix a few words --- src/calling-methods.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/calling-methods.md b/src/calling-methods.md index 46ccfc5..1cc020c 100644 --- a/src/calling-methods.md +++ b/src/calling-methods.md @@ -1,4 +1,4 @@ -## Invoking Object Methods +## Calling Object Methods > **Note:** Requires Flix 0.49.0 @@ -66,7 +66,7 @@ def main(): Unit \ IO = } ``` -## Invoking Static Methods +## Calling Static Methods In Flix, we can call static methods (i.e. class methods) using syntax similar to Java: @@ -96,7 +96,7 @@ def main(): Unit \ IO = println(Math.abs(-123.456f64)) ``` -## Callings Methods with VarArgs +## Calling Constructors and Methods with VarArgs TBD @@ -133,7 +133,7 @@ The type ascription specifies that `o` has type `Bool` which allows method resolution to complete successfully. Note that the extra pair of parenthesis is required. -## Invoking Object Methods through Static Fields +## Calling Object Methods through Static Fields We may want to write: @@ -153,7 +153,7 @@ def main(): Unit \ IO = (System.out).println("Hello World!") ``` -## Invoking Java Methods Known to be Pure +## Calling Java Methods Known to be Pure Any Flix expression that creates a Java object, calls a Java method, or calls a Java static method has the `IO` effect. This is to be expected: Java From 2525dcf3576f829eddbf537059e94a7a43a53836 Mon Sep 17 00:00:00 2001 From: Magnus Madsen Date: Thu, 1 Aug 2024 15:07:24 +0200 Subject: [PATCH 22/22] work on boxing --- src/boxing-and-unboxing.md | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/boxing-and-unboxing.md b/src/boxing-and-unboxing.md index 38878ee..a2a5e97 100644 --- a/src/boxing-and-unboxing.md +++ b/src/boxing-and-unboxing.md @@ -2,38 +2,36 @@ > **Note:** Requires Flix 0.49.0 -Unlike Java, Flix never performs implicit boxing or unboxing of values. We -believe auto boxing is a design flaw and do not plan to support it. +Unlike Java, Flix never performs implicit boxing or unboxing of values. -Consequently, you must explicitly box and unbox values as needed. +We believe auto boxing is a design flaw and do not plan to support it. Hence, +primitive values must be manually boxed and unboxed. ### Boxing -The following example shows how to manually box a Java `Integer`: +The following example shows how to box a primitive integer: ```flix -import java.lang.Integer -import java.util.Objects - -def hash(x: Int32): Int32 = unsafe { - let boxed = Integer.valueOf(x); - Objects.hashCode(boxed) -} +def f(x: Int32): String \ IO = + let i = Box.box(x); // Integer + i.toString() ``` -We must use `unsafe` to cast away the `IO` effect from the call to -`Integer.valueOf()` and `Objects.hashCode()`. +Here the call to `Box.box(x)` returns an `Integer` object. Since `i` is an +object, we can call `toString` on it. Boxing is a pure operation, but calling +`toString` has the `IO` effect. ### Unboxing -The following example shows how to manually unbox two Java `Integer`s: +The following example shows how to unbox two Java `Integer` objects: ```flix import java.lang.Integer def sum(x: Integer, y: Integer): Int32 = - unsafe x.intValue() + y.intValue() + Box.unbox(x) + Box.unbox(y) ``` -We must use `unsafe` to cast away the `IO` effect from the call to `intValue()`. +Here the call to `Box.unbox` returns an `Int32` primitive value. +Unboxing is a pure operation.