Repository: goplus/gop
Pull request #1241 (2022-06-05 15:49:10)

README: Go+ manual


Patch:
diff --git a/README.md b/README.md
index d41a6d45..7ce0366a 100644
--- a/README.md
+++ b/README.md
@@ -48,265 +48,607 @@ all.bat
 Actually, `all.bash` and `all.bat` will use `go run cmd/make.go` underneath.
 
 
-## Code style (important)
+## Running in Go+ playground
 
-* https://tutorial.goplus.org/hello-world
+If you don't want install Go+, you can run this your Go+ program in Go+ playground.
 
+* Go+ playground based on Docker: https://play.goplus.org/
+* Go+ playground based on GopherJS: https://jsplay.goplus.org/
 
-## Summary about Go+
+And you can share your Go+ code with your friends.
+Here is my `Hello world` program: https://play.goplus.org/p/AAh_gQAKAZR.
 
-What are mainly impressions about Go+?
 
-- A static typed language.
-- Fully compatible with [the Go language](https://github.com/golang/go).
-- Script-like style, and more readable code than Go.
+## Table of Contents
 
-For example, the following is legal Go+ source code:
+<table>
+    <tr><td width=33% valign=top>
 
-```coffee
-println [1, 2, 3.4]
+* [Hello world](#hello-world)
+* [Running a project folder](#running-a-project-folder-with-several-files)
+* [Comments](#comments)
+* [Variables](#variables)
+* [Go+ types](#go-types)
+    * [Strings](#strings)
+    * [Numbers](#numbers)
+    * [Slices](#slices)
+    * [Maps](#maps)
+* [Module imports](#module-imports)
+* [Statements & expressions](#statements--expressions)
+    * [If](#if)
+    * [For loop](#for-loop)
+    * [List comprehension](#list-comprehension)
+    * [Select data from a collection](#select-data-from-a-collection)
+    * [Check if data exists in a collection](#check-if-data-exists-in-a-collection)
+* [Functions](#functions)
+    * [Returning multiple values](#returning-multiple-values)
+    * [Variable number of arguments](#variable-number-of-arguments)
+# [Structs](#structs)
+# [Error handling](#error-handling)
+# [Unix shebang](#unix-shebang)
+
+</td><td width=33% valign=top>
+
+</td><td valign=top>
+
+</td></tr>
+</table>
+
+
+## Hello World
+
+```go
+println "Hello world"
+```
+
+Save this snippet into a file named `hello.gop`. Now do: `gop run hello.gop`.
+
+Congratulations - you just wrote and executed your first Go+ program!
+
+You can compile a program without execution with `gop build hello.gop`.
+See `gop help` for all supported commands.
+
+[`println`](#println) is one of the few [built-in functions](#builtin-functions).
+It prints the value passed to it to standard output.
+
+See https://tutorial.goplus.org/hello-world for more details.
+
+
+## Running a project folder with several files
+
+Suppose you have a folder with several .gop files in it, and you want 
+to compile them all into one program. Just do: `gop run .`.
+
+Passing parameters also works, so you can do:
+`gop run . --yourparams some_other_stuff`.
+
+Your program can then use the CLI parameters like this:
+
+```go
+import "os"
+
+println os.Args
 ```
 
-How do we do this in the Go language?
+## Comments
 
 ```go
-package main
+# This is a single line comment.
 
-import "fmt"
+// This is a single line comment.
 
-func main() {
-    fmt.Println([]float64{1, 2, 3.4})
-}
+/*
+This is a multiline comment.
+*/
 ```
 
-Of course, we don't only do less-typing things.
+## Variables
 
-For example, we support [list comprehension](https://en.wikipedia.org/wiki/List_comprehension), which makes data processing easier.
+```go
+name := "Bob"
+age := 20
+largeNumber := int128(1 << 65)
+println name, age
+println largeNumber
+```
+
+Variables are declared and initialized with `:=`.
+
+The variable's type is inferred from the value on the right hand side.
+To choose a different type, use type conversion:
+the expression `T(v)` converts the value `v` to the
+type `T`.
+
+### Initialization vs assignment
+
+Note the (important) difference between `:=` and `=`.
+`:=` is used for declaring and initializing, `=` is used for assigning.
+
+```go failcompile
+age = 21
+```
+
+This code will not compile, because the variable `age` is not declared.
+All variables need to be declared in Go+.
 
 ```go
-println [x*x for x <- 1:6:2] // output: [1 9 25]
+age := 21
+```
+
+The values of multiple variables can be changed in one line.
+In this way, their values can be swapped without an intermediary variable.
 
-mapData := {"Hi": 1, "Hello": 2, "Go+": 3}
-reversedMap := {v: k for k, v <- mapData}
-println reversedMap // output: map[1:Hi 2:Hello 3:Go+]
+```go
+a, b := 0, 1
+a, b = b, a
+println a, b // 1, 0
 ```
 
-We will keep Go+ simple. This is why we call it Go+, not Go++.
+## Go+ Types
 
-Less is exponentially more.
+### Primitive types
 
-It's for Go, and it's also for Go+.
+```go ignore
+bool
 
+int8    int16   int32   int    int64    int128
+uint8   uint16  uint32  uint   uint64   uint128
 
-## Tutorials
+uintptr // similar to C's size_t
 
-* https://tutorial.goplus.org/
+byte // alias for uint8
+rune // alias for int32, represents a Unicode code point
 
+string
 
-## Playground
+float32 float64
 
-Go+ Playground based on Docker:
-* https://play.goplus.org/
+complex64 complex128
 
-Go+ Playground based on GopherJS:
-* https://jsplay.goplus.org/
+bigint bigrat
 
+unsafe.Pointer // similar to C's void*
 
-## Compatibility with Go
+any // alias for Go's interface{}
+```
 
-All Go features will be supported (including partially support `cgo`, see [below](#bytecode-vs-go-code)).
+### Strings
 
-**All Go packages (even these packages use `cgo`) can be imported by Go+.**
+```go
+name := "Bob"
+println name.len  // 3
+println name[0]   // 66
+println name[1:3] // ob
+println name[:2]  // Bo
+println name[2:]  // b
+
+// or using octal escape `\###` notation where `#` is an octal digit
+println "\141a"   // aa
+
+// Unicode can be specified directly as `\u####` where # is a hex digit
+// and will be converted internally to its UTF-8 representation
+println "\u2605"  // ★
+```
 
-```coffee
-import (
-    "fmt"
-    "strings"
-)
+String values are immutable. You cannot mutate elements:
 
-x := strings.NewReplacer("?", "!").Replace("hello, world???")
-fmt.Println "x:", x
+```go failcompile
+s := "hello 🌎"
+s[0] = `H` // not allowed
 ```
 
-**And all Go+ packages can also be imported in Go programs. What you need to do is just using `gop` command instead of `go`.**
+Note that indexing a string will produce a `byte`, not a `rune` nor another `string`.
 
-First, let's make a directory named `14-Using-goplus-in-Go`.
+Strings can be easily converted to integers:
 
-Then write a Go+ package named [foo](https://github.com/goplus/tutorial/tree/main/14-Using-goplus-in-Go/foo) in it:
+```go
+s := "12"
+a, err := s.int
+b := s.int! // will panic if s isn't a valid integer
+```
+
+#### String operators
 
 ```go
-package foo
+name := "Bob"
+bobby := name + "by" // + is used to concatenate strings
+println bobby // Bobby
 
-func ReverseMap(m map[string]int) map[int]string {
-    return {v: k for k, v <- m}
-}
+s := "Hello "
+s += "world"
+println s // Hello world
 ```
 
-Then use it in a Go package [14-Using-goplus-in-Go/gomain](https://github.com/goplus/tutorial/tree/main/14-Using-goplus-in-Go/gomain):
+All operators in Go+ must have values of the same type on both sides. You cannot concatenate an
+integer to a string:
+
+```go failcompile
+age := 10
+println "age = " + age // not allowed
+```
+
+We have to either convert `age` to a `string`:
 
 ```go
-package main
+age := 10
+println "age = " + age.string
+```
 
-import (
-    "fmt"
+### Runes
 
-    "github.com/goplus/tutorial/14-Using-goplus-in-Go/foo"
-)
+A `rune` represents a single Unicode character and is an alias for `int32`.
 
-func main() {
-    rmap := foo.ReverseMap(map[string]int{"Hi": 1, "Hello": 2})
-    fmt.Println(rmap)
-}
+```go
+rocket := '🚀'
+println rocket         // 128640
+println string(rocket) // 🚀
 ```
 
-How to build this example? You can use:
+### Numbers
 
-```bash
-gop install -v ./...
+```go
+a := 123
 ```
 
-Go [github.com/goplus/tutorial/14-Using-goplus-in-Go](https://github.com/goplus/tutorial/tree/main/14-Using-goplus-in-Go) to get the source code.
+This will assign the value of 123 to `a`. By default `a` will have the
+type `int`.
 
+You can also use hexadecimal, binary or octal notation for integer literals:
 
-## Bytecode vs. Go code
+```go
+a := 0x7B
+b := 0b01111011
+c := 0o173
+```
 
-Go+ supports bytecode backend and Go code generation.
+All of these will be assigned the same value, 123. They will all have type
+`int`, no matter what notation you used.
 
-When we use `gop` command, it generates Go code to covert Go+ package into Go packages.
+Go+ also supports writing numbers with `_` as separator:
 
-```bash
-gop run     # Run a Go+ program
-gop install # Build Go+ files and install target to GOBIN
-gop build   # Build Go+ files
-gop test    # Test Go+ packages
-gop fmt     # Format Go+ packages
-gop clean   # Clean all Go+ auto generated files
-gop go      # Convert Go+ packages into Go packages
+```go
+num := 1_000_000 // same as 1000000
+three := 0b0_11 // same as 0b11
+floatNum := 3_122.55 // same as 3122.55
+hexa := 0xF_F // same as 255
+oct := 0o17_3 // same as 0o173
 ```
 
-When we use [`igop`](https://github.com/goplus/igop) command, it generates bytecode to execute.
+If you want a different type of integer, you can use casting:
 
-```bash
-igop  # Run a Go+ program
+```go
+a := int64(123)
+b := uint8(12)
+c := int128(12345)
 ```
 
-In bytecode mode, Go+ doesn't support `cgo`. However, in Go-code-generation mode, Go+ fully supports `cgo`.
+Assigning floating point numbers works the same way:
 
+```go
+f1 := 1.0
+f2 := float32(3.14)
+```
+
+If you do not specify the type explicitly, by default float literals will have the type of `float64`.
 
-## Go+ features
+Float literals can also be declared as a power of ten:
 
-### Rational number: bigint, bigrat, bigfloat
+```go
+f0 := 42e1   // 420
+f1 := 123e-2 // 1.23
+f2 := 456e+2 // 45600
+```
 
-We introduce the rational number as native Go+ types. We use suffix `r` to denote rational literals. For example, (1r << 200) means a big int whose value is equal to 2<sup>200</sup>. And 4/5r means the rational constant 4/5.
+We introduce rational numbers as native Go+ types. We use suffix `r` to denote rational literals. For example, `1r << 200` means a big int whose value is equal to 2<sup>200</sup>.
 
 ```go
-var a bigint = 1r << 65  // bigint, large than int64
-var b bigrat = 4/5r      // bigrat
-c := b - 1/3r + 3 * 1/2r // bigrat
-println a, b, c
+a := 1r << 200
+b := bigint(1 << 200)
+```
+
+By default, `1r` will have the type of `bigint`.
+
+And `4/5r` means the rational constant `4/5`.
+It will have the type of `bigrat`.
 
-var x *big.Int = 1r << 65 // (1r << 65) is untyped bigint, and can be assigned to *big.Int
-var y *big.Rat = 4/5r
-println x, y
+```go
+a := 4/5r
+b := a - 1/3r + 3 * 1/2r
+println a, b // 4/5 59/30
 ```
 
-### Large integer: uint128, int128
+Casting rational numbers works the same way:
 
 ```go
-var x uint128 = 1 << 65
-var y = x + 1
-println x // output: 36893488147419103232
-println y // output: 36893488147419103233
+a := 1r
+b := bigrat(1r)
+c := bigrat(1)
+println a/3 // 0
+println b/3 // 1/3
+println c/3 // 1/3
 ```
 
-### Convert bool to number types
+#### Convert bool to number types
 
 ```go
-println int(true)       // output: 1
-println float64(true)   // output: 1
-println complex64(true) // output: (1+0i)
+println int(true)       // 1
+println float64(true)   // 1
+println complex64(true) // (1+0i)
 ```
 
-### Map literal
+### Slices
+
+A slice is a collection of data elements of the same type. A slice literal is a
+list of expressions surrounded by square brackets. An individual element can be
+accessed using an *index* expression. Indexes start from `0`:
 
 ```go
-x := {"Hello": 1, "xsw": 3.4}   // map[string]float64
-y := {"Hello": 1, "xsw": "Go+"} // map[string]interface{}
-z := {"Hello": 1, "xsw": 3}     // map[string]int
-empty := {}                     // map[string]interface{}
+nums := [1, 2, 3]
+println nums      // [1 2 3]
+println nums.len  // 3
+println nums[0]   // 1
+println nums[1:3] // [2 3]
+println nums[:2]  // [1 2]
+println nums[2:]  // [3]
+
+nums[1] = 5
+println nums // [1 5 3]
 ```
 
-### Slice literal
+Type of a slice literal is infered automatically.
 
 ```go
-x := [1, 3.4]       // []float64
-y := [1]            // []int
-z := [1+2i, "xsw"]  // []interface{}
-a := [1, 3.4, 3+4i] // []complex128
-b := [5+6i]         // []complex128
-c := ["xsw", 3]     // []interface{}
-empty := []         // []interface{}
+a := [1, 2, 3]   // []int
+b := [1, 2, 3.4] // []float64
+c := ["Hi"]      // []string
+d := ["Hi", 10]  // []any
+d := []          // []any
 ```
 
-### Lambda expression
+And casting slices also works.
 
 ```go
-func plot(fn func(x float64) float64) {
-    // ...
+a := []float64([1, 2, 3]) // []float64
+```
+
+### Maps
+
+```go
+a := {"Hello": 1, "xsw": 3}     // map[string]int
+b := {"Hello": 1, "xsw": 3.4}   // map[string]float64
+c := {"Hello": 1, "xsw": "Go+"} // map[string]any
+d := {}                         // map[string]any
+```
+
+If a key is not found, a zero value is returned by default:
+
+```go
+a := {"Hello": 1, "xsw": 3}
+c := {"Hello": 1, "xsw": "Go+"}
+println a["bad_key"] // 0
+println c["bad_key"] // <nil>
+```
+
+You can also check, if a key is present, and get its value.
+
+```go
+a := {"Hello": 1, "xsw": 3}
+if v, ok := a["xsw"]; ok {
+    println "its value is", v
 }
+```
 
-func plot2(fn func(x float64) (float64, float64)) {
-    // ...
+## Module imports
+
+For information about creating a module, see [Modules](#modules).
+
+Modules can be imported using the `import` keyword:
+
+```go
+import "strings"
+
+x := strings.NewReplacer("?", "!").Replace("Hello, world???")
+println x // Hello, world!!!
+```
+
+### Module import aliasing
+
+Any imported module name can be aliased:
+
+```go
+import strop "strings"
+
+x := strop.NewReplacer("?", "!").Replace("Hello, world???")
+println x // Hello, world!!!
+```
+
+
+## Statements & expressions
+
+
+### If
+
+`if` statements are pretty straightforward and similar to most other languages.
+Unlike other C-like languages,
+there are no parentheses surrounding the condition and the braces are always required.
+
+```go
+a := 10
+b := 20
+if a < b {
+	println "a < b"
+} else if a > b {
+	println "a > b"
+} else {
+	println "a == b"
 }
+```
+
+### For loop
+
+Go+ has only one looping keyword: `for`, with several forms.
+
+#### `for`/`<-`
+
+This is the most common form. You can use it with an slice, map, numeric range or custom iterators.
+
+For information about creating a custom iterators, see [Custom iterators](#custom-iterators).
 
-plot x => x * x           // plot(func(x float64) float64 { return x * x })
-plot2 x => (x * x, x + x) // plot2(func(x float64) (float64, float64) { return x * x, x + x })
+##### Slice `for`
+
+The `for value <- arr` form is used for going through elements of an slice.
+
+```go
+numbers := [1, 3, 5, 7, 11, 13, 17]
+sum := 0
+for x <- numbers {
+    sum += x
+}
+println sum // 57
 ```
 
-### Deduce struct type
+If an index is required, an alternative form `for index, value <- arr` can be used.
 
 ```go
-type Config struct {
-    Dir   string
-    Level int
+names := ["Sam", "Peter"]
+for i, name <- names {
+    println i, name
+    // 0 Sam
+    // 1 Peter
 }
+```
 
-func foo(conf *Config) {
-    // ...
+##### Map `for`
+
+```go
+m := {"one": 1, "two": 2}
+for key, val <- m {
+	println key, val
+	// one 1
+	// two 2
+}
+for key, _ <- m {
+	println key
+	// one
+	// two
 }
+for val <- m {
+	println val
+	// 1
+	// 2
+}
+```
 
-foo {Dir: "/foo/bar", Level: 1}
+##### Range `for`
+
+You can use `range expression` (`start:end:step`) in for loop.
+
+```go
+for i <- :5 {
+    println i
+    // 0
+    // 1
+    // 2
+    // 3
+    // 4
+}
+for i <- 1:5 {
+    println i
+    // 1
+    // 2
+    // 3
+    // 4
+}
+for i <- 1:5:2 {
+    println i
+    // 1
+    // 3
+}
 ```
 
-Here `foo {Dir: "/foo/bar", Level: 1}` is equivalent to `foo(&Config{Dir: "/foo/bar", Level: 1})`. However, you can't replace `foo(&Config{"/foo/bar", 1})` with `foo {"/foo/bar", 1}`, because it is confusing to consider `{"/foo/bar", 1}` as a struct literal.
+##### `for`/`<-`/`if`
 
-You also can omit struct types in a return statement. For example:
+All loops of `for`/`<-` form can have an optional `if` condition.
 
 ```go
-type Result struct {
-    Text string
+numbers := [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+for num <- numbers if num%3 == 0 {
+    println num
+    // 0
+    // 3
+    // 6
+    // 9
 }
 
-func foo() *Result {
-    return {Text: "Hi, Go+"} // return &Result{Text: "Hi, Go+"}
+for num <- :10 if num%3 == 0 {
+    println num
+    // 0
+    // 3
+    // 6
+    // 9
 }
 ```
 
+#### Condition `for`
+
+```go
+sum := 0
+i := 1
+for i <= 100 {
+	sum += i
+	i++
+}
+println sum // 5050
+```
+
+This form of the loop is similar to `while` loops in other languages.
+The loop will stop iterating once the boolean condition evaluates to false.
+Again, there are no parentheses surrounding the condition, and the braces are always required.
+
+#### C `for`
+
+```go
+for i := 0; i < 10; i += 2 {
+    // Don't print 6
+    if i == 6 {
+        continue
+    }
+    println i
+    // 0
+    // 2
+    // 4
+    // 8
+}
+```
+
+Finally, there's the traditional C style `for` loop. It's safer than the `while` form
+because with the latter it's easy to forget to update the counter and get
+stuck in an infinite loop.
+
+
+#### Bare `for`
+
+```go
+for {
+    // ...
+}
+```
+
+The condition can be omitted, resulting in an infinite loop. You can use `break` or `return` to end the loop.
 
 ### List comprehension
 
 ```go
 a := [x*x for x <- [1, 3, 5, 7, 11]]
-b := [x*x for x <- [1, 3, 5, 7, 11], x > 3]
-c := [i+v for i, v <- [1, 3, 5, 7, 11], i%2 == 1]
-d := [k+","+s for k, s <- {"Hello": "xsw", "Hi": "Go+"}]
+b := [x*x for x <- [1, 3, 5, 7, 11] if x > 3]
+c := [i+v for i, v <- [1, 3, 5, 7, 11] if i%2 == 1]
 
 arr := [1, 2, 3, 4, 5, 6]
-e := [[a, b] for a <- arr, a < b for b <- arr, b > 2]
+d := [[a, b] for a <- arr if a < b for b <- arr if b > 2]
 
 x := {x: i for i, x <- [1, 3, 5, 7, 11]}
-y := {x: i for i, x <- [1, 3, 5, 7, 11], i%2 == 1}
-z := {v: k for k, v <- {1: "Hello", 3: "Hi", 5: "xsw", 7: "Go+"}, k > 3}
+y := {x: i for i, x <- [1, 3, 5, 7, 11] if i%2 == 1}
+z := {v: k for k, v <- {1: "Hello", 3: "Hi", 5: "xsw", 7: "Go+"} if k > 3}
 ```
 
 ### Select data from a collection
@@ -319,11 +661,11 @@ type student struct {
 
 students := [student{"Ken", 90}, student{"Jason", 80}, student{"Lily", 85}]
 
-unknownScore, ok := {x.score for x <- students, x.name == "Unknown"}
-jasonScore := {x.score for x <- students, x.name == "Jason"}
+unknownScore, ok := {x.score for x <- students if x.name == "Unknown"}
+jasonScore := {x.score for x <- students if x.name == "Jason"}
 
-println unknownScore, ok // output: 0 false
-println jasonScore // output: 80
+println unknownScore, ok // 0 false
+println jasonScore // 80
 ```
 
 ### Check if data exists in a collection
@@ -336,40 +678,102 @@ type student struct {
 
 students := [student{"Ken", 90}, student{"Jason", 80}, student{"Lily", 85}]
 
-hasJason := {for x <- students, x.name == "Jason"} // is any student named Jason?
-hasFailed := {for x <- students, x.score < 60}     // is any student failed?
+hasJason := {for x <- students if x.name == "Jason"} // is any student named Jason?
+hasFailed := {for x <- students if x.score < 60}     // is any student failed?
 ```
 
-### For loop
+
+## Functions
 
 ```go
-sum := 0
-for x <- [1, 3, 5, 7, 11, 13, 17], x > 3 {
-    sum += x
+func add(x int, y int) int {
+	return x + y
 }
+
+println add(2, 3) // 5
 ```
 
+### Returning multiple values
+
+```go
+func foo() (int, int) {
+	return 2, 3
+}
+
+a, b := foo()
+println a // 2
+println b // 3
+c, _ := foo() // ignore values using `_`
+```
 
-### Range expression (`start:end:step`)
+### Variable number of arguments
 
 ```go
-for i <- :10 {
-    println i
+func sum(a ...int) int {
+    total := 0
+	for x <- a {
+		total += x
+	}
+	return total
 }
 
-for i := range :10:2 {
-    println i
+println sum(2, 3, 5) // 10
+```
+
+Output parameters can have names.
+
+```go
+func sum(a ...int) (total int) {
+	for x <- a {
+		total += x
+	}
+	return // don't need return values if they are assigned
 }
 
-for i := range 1:10:3 {
-    println i
+println sum(2, 3, 5) // 10
+```
+
+### Higher order functions
+
+Functions can also be parameters.
+
+```go
+func square(x float64) float64 {
+    return x*x
 }
 
-for range :10 {
-    println "Range expression"
+func transform(a []float64, f func(float64) float64) []float64 {
+    return [f(x) for x <- a]
 }
+
+y := transform([1, 2, 3], square)
+println y // [1 4 9]
 ```
 
+### Lambda expression
+
+You also can use `lambda expression` to define a anonymous function.
+
+```go
+func transform(a []float64, f func(float64) float64) []float64 {
+    return [f(x) for x <- a]
+}
+
+y := transform([1, 2, 3], x => x*x)
+println y // [1 4 9]
+
+z := transform([-3, 1, -5], x => {
+    if x < 0 {
+        return -x
+    }
+    return x
+})
+println z // [3 1 5]
+```
+
+
+
+## Structs
 
 ### For range of UDT
 
@@ -430,6 +834,35 @@ for k, v <- foo {
 println {v: k for k, v <- foo}
 ```
 
+### Deduce struct type
+
+```go
+type Config struct {
+    Dir   string
+    Level int
+}
+
+func foo(conf *Config) {
+    // ...
+}
+
+foo {Dir: "/foo/bar", Level: 1}
+```
+
+Here `foo {Dir: "/foo/bar", Level: 1}` is equivalent to `foo(&Config{Dir: "/foo/bar", Level: 1})`. However, you can't replace `foo(&Config{"/foo/bar", 1})` with `foo {"/foo/bar", 1}`, because it is confusing to consider `{"/foo/bar", 1}` as a struct literal.
+
+You also can omit struct types in a return statement. For example:
+
+```go
+type Result struct {
+    Text string
+}
+
+func foo() *Result {
+    return {Text: "Hi, Go+"} // return &Result{Text: "Hi, Go+"}
+}
+```
+
 ### Overload operators
 
 ```go
@@ -461,8 +894,34 @@ println a + Int(3r)
 println -a
 ```
 
+### Auto property
+
+Let's see an example written in Go+:
+
+```go
+import "gop/ast/goptest"
+
+doc := goptest.New(`... Go+ code ...`)!
+
+println doc.Any().FuncDecl().Name()
+```
+
+In many languages, there is a concept named `property` who has `get` and `set` methods.
 
-### Error handling
+Suppose we have `get property`, the above example will be:
+
+```go
+import "gop/ast/goptest"
+
+doc := goptest.New(`... Go+ code ...`)!
+
+println doc.any.funcDecl.name
+```
+
+In Go+, we introduce a concept named `auto property`. It is a `get property`, but is implemented automatically. If we have a method named `Bar()`, then we will have a `get property` named `bar` at the same time.
+
+
+## Error handling
 
 We reinvent the error handling specification in Go+. We call them `ErrWrap expressions`:
 
@@ -515,62 +974,111 @@ And the most interesting thing is, the return error contains the full error stac
 How these `ErrWrap expressions` work? See [Error Handling](https://github.com/goplus/gop/wiki/Error-Handling) for more information.
 
 
-### Auto property
+## Unix shebang
 
-Let's see an example written in Go+:
+You can use Go+ programs as shell scripts now. For example:
 
 ```go
-import "gop/ast/goptest"
+#!/usr/bin/env -S gop run
 
-doc := goptest.New(`... Go+ code ...`)!
+println "Hello, Go+"
 
-println doc.Any().FuncDecl().Name()
+println 1r << 129
+println 1/3r + 2/7r*2
+
+arr := [1, 3, 5, 7, 11, 13, 17, 19]
+println arr
+println [x*x for x <- arr, x > 3]
+
+m := {"Hi": 1, "Go+": 2}
+println m
+println {v: k for k, v <- m}
+println [k for k, _ <- m]
+println [v for v <- m]
 ```
 
-In many languages, there is a concept named `property` who has `get` and `set` methods.
+Go [20-Unix-Shebang/shebang](https://github.com/goplus/tutorial/blob/main/20-Unix-Shebang/shebang) to get the source code.
 
-Suppose we have `get property`, the above example will be:
 
-```go
-import "gop/ast/goptest"
+## Compatibility with Go
 
-doc := goptest.New(`... Go+ code ...`)!
+All Go features will be supported (including partially support `cgo`, see [below](#bytecode-vs-go-code)).
 
-println doc.any.funcDecl.name
+**All Go packages (even these packages use `cgo`) can be imported by Go+.**
+
+```coffee
+import (
+    "fmt"
+    "strings"
+)
+
+x := strings.NewReplacer("?", "!").Replace("hello, world???")
+fmt.Println "x:", x
 ```
 
-In Go+, we introduce a concept named `auto property`. It is a `get property`, but is implemented automatically. If we have a method named `Bar()`, then we will have a `get property` named `bar` at the same time.
+**And all Go+ packages can also be imported in Go programs. What you need to do is just using `gop` command instead of `go`.**
 
+First, let's make a directory named `14-Using-goplus-in-Go`.
 
-### Unix shebang
+Then write a Go+ package named [foo](https://github.com/goplus/tutorial/tree/main/14-Using-goplus-in-Go/foo) in it:
 
-You can use Go+ programs as shell scripts now. For example:
+```go
+package foo
+
+func ReverseMap(m map[string]int) map[int]string {
+    return {v: k for k, v <- m}
+}
+```
+
+Then use it in a Go package [14-Using-goplus-in-Go/gomain](https://github.com/goplus/tutorial/tree/main/14-Using-goplus-in-Go/gomain):
 
 ```go
-#!/usr/bin/env -S gop run
+package main
 
-println "Hello, Go+"
+import (
+    "fmt"
 
-println 1r << 129
-println 1/3r + 2/7r*2
+    "github.com/goplus/tutorial/14-Using-goplus-in-Go/foo"
+)
 
-arr := [1, 3, 5, 7, 11, 13, 17, 19]
-println arr
-println [x*x for x <- arr, x > 3]
+func main() {
+    rmap := foo.ReverseMap(map[string]int{"Hi": 1, "Hello": 2})
+    fmt.Println(rmap)
+}
+```
 
-m := {"Hi": 1, "Go+": 2}
-println m
-println {v: k for k, v <- m}
-println [k for k, _ <- m]
-println [v for v <- m]
+How to build this example? You can use:
+
+```bash
+gop install -v ./...
 ```
 
-Go [20-Unix-Shebang/shebang](https://github.com/goplus/tutorial/blob/main/20-Unix-Shebang/shebang) to get the source code.
+Go [github.com/goplus/tutorial/14-Using-goplus-in-Go](https://github.com/goplus/tutorial/tree/main/14-Using-goplus-in-Go) to get the source code.
+
+
+## Bytecode vs. Go code
 
+Go+ supports bytecode backend and Go code generation.
 
-### Go features
+When we use `gop` command, it generates Go code to covert Go+ package into Go packages.
 
-All Go features (including partially support `cgo`) will be supported. In bytecode mode, Go+ doesn't support `cgo`. However, in Go-code-generation mode, Go+ fully supports `cgo`.
+```bash
+gop run     # Run a Go+ program
+gop install # Build Go+ files and install target to GOBIN
+gop build   # Build Go+ files
+gop test    # Test Go+ packages
+gop fmt     # Format Go+ packages
+gop clean   # Clean all Go+ auto generated files
+gop go      # Convert Go+ packages into Go packages
+```
+
+When we use [`igop`](https://github.com/goplus/igop) command, it generates bytecode to execute.
+
+```bash
+igop  # Run a Go+ program
+```
+
+In bytecode mode, Go+ doesn't support `cgo`. However, in Go-code-generation mode, Go+ fully supports `cgo`.
 
 
 ## IDE Plugins
@@ -578,6 +1086,11 @@ All Go features (including partially support `cgo`) will be supported. In byteco
 * vscode: https://github.com/gopcode/vscode-goplus
 
 
+## Tutorials
+
+* https://tutorial.goplus.org/
+
+
 ## Contributing
 
 The Go+ project welcomes all contributors. We appreciate your help!
diff --git a/parser/parser.go b/parser/parser.go
index 6985133e..9b8e6a3f 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -813,7 +813,7 @@ func (p *parser) parseArrayTypeOrSliceLit(state int, slice ast.Expr) (expr ast.E
 				sliceLit := p.parseSliceLit(lbrack, len)
 				p.exprLev--
 				return sliceLit, resultSliceLit
-			case token.FOR: // [expr for k, v <- container, cond]
+			case token.FOR: // [expr for k, v <- container if cond]
 				phrases := p.parseForPhrases()
 				p.exprLev--
 				rbrack := p.expect(token.RBRACK)
@@ -2747,7 +2747,7 @@ func (p *parser) parseForPhraseStmtPart(lhs []ast.Expr) *ast.ForPhraseStmt {
 	tokPos := p.expect(token.ARROW) // <-
 	x := p.parseExpr(false, false, true)
 	var cond ast.Expr
-	if p.tok == token.COMMA {
+	if p.tok == token.IF || p.tok == token.COMMA {
 		p.next()
 		cond = p.parseExpr(false, false, false)
 	}
@@ -2776,7 +2776,7 @@ func (p *parser) toIdent(e ast.Expr) *ast.Ident {
 	return nil
 }
 
-func (p *parser) parseForPhrase() *ast.ForPhrase { // for k, v <- container, cond
+func (p *parser) parseForPhrase() *ast.ForPhrase { // for k, v <- container if cond
 	if p.trace {
 		defer un(trace(p, "ForPhrase"))
 	}
@@ -2796,7 +2796,7 @@ func (p *parser) parseForPhrase() *ast.ForPhrase { // for k, v <- container, con
 	x := p.parseExpr(false, false, true)
 	var init ast.Stmt
 	var cond ast.Expr
-	if p.tok == token.COMMA { // `condition` or `init; condition`
+	if p.tok == token.IF || p.tok == token.COMMA { // `condition` or `init; condition`
 		p.next()
 		init, cond = p.parseForPhraseCond()
 	}
diff --git a/printer/nodes.go b/printer/nodes.go
index ce9b8676..0cdd255d 100644
--- a/printer/nodes.go
+++ b/printer/nodes.go
@@ -1112,7 +1112,7 @@ func (p *printer) listForPhrase(prev0 token.Pos, list []*ast.ForPhrase, depth in
 		p.print(x.TokPos, token.ARROW, blank)
 		p.expr(x.X)
 		if x.Cond != nil {
-			p.print(x.Cond.Pos(), token.COMMA, blank)
+			p.print(blank, x.Cond.Pos(), token.IF, blank)
 			if x.Init != nil {
 				p.stmt(x.Init, false)
 				p.print(token.SEMICOLON, blank)
@@ -1515,7 +1515,7 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool) {
 		p.print(blank, s.TokPos, token.ARROW, blank)
 		p.expr(s.X)
 		if s.Cond != nil {
-			p.print(s.Cond.Pos(), token.COMMA, blank)
+			p.print(blank, s.Cond.Pos(), token.IF, blank)
 			p.expr(s.Cond)
 		}
 		p.print(blank)


Test patch:
diff --git a/cl/compile_test.go b/cl/compile_test.go
index 9d12df2f..1ca3f978 100644
--- a/cl/compile_test.go
+++ b/cl/compile_test.go
@@ -156,7 +156,8 @@ type foo float64
 a := []float64([1, 2])
 b := fvec([1, 2])
 c := foo([1, 2])
-println a, b, c
+d := fvec([])
+println a, b, c, d
 `, `package main
 
 import fmt "fmt"
@@ -168,7 +169,8 @@ func main() {
 	a := []float64{1, 2}
 	b := fvec{1, 2}
 	c := foo([]int{1, 2})
-	fmt.Println(a, b, c)
+	d := fvec{}
+	fmt.Println(a, b, c, d)
 }
 `)
 }
diff --git a/parser/_testdata/exists/exists.gop b/parser/_testdata/exists/exists.gop
index d846527f..7ebe18fd 100644
--- a/parser/_testdata/exists/exists.gop
+++ b/parser/_testdata/exists/exists.gop
@@ -1,2 +1,2 @@
 a := [1, 3, 5, 7, 8, 19]
-hasEven := {for x <- a, x%2 == 0}
+hasEven := {for x <- a if x%2 == 0}
diff --git a/parser/_testdata/forloop/forloop.gop b/parser/_testdata/forloop/forloop.gop
index 865fa4fd..e2cc6920 100644
--- a/parser/_testdata/forloop/forloop.gop
+++ b/parser/_testdata/forloop/forloop.gop
@@ -25,6 +25,6 @@ for x <- [1] {
 	println(x)
 }
 
-for i, x <- [1], i%2 == 0 {
+for i, x <- [1] if i%2 == 0 {
 	println(i, x)
 }
diff --git a/parser/_testdata/listcompr/listcompr.gop b/parser/_testdata/listcompr/listcompr.gop
index 5c8a5fe1..e7f61460 100644
--- a/parser/_testdata/listcompr/listcompr.gop
+++ b/parser/_testdata/listcompr/listcompr.gop
@@ -1,14 +1,14 @@
 y := [x*x for x <- [1]]
 println(y)
 
-y = [x*x for x <- [1, 3, 5, 7, 11], x > 3]
+y = [x*x for x <- [1, 3, 5, 7, 11] if x > 3]
 println(y)
 
-z := [i+v for i, v <- [1, 3, 5, 7, 11], t := i % 2; t == 1]
+z := [i+v for i, v <- [1, 3, 5, 7, 11] if t := i % 2; t == 1]
 println(z)
 
 println([k+","+s for k, s <- {"Hello": "xsw", "Hi": "Go+"}])
 
 arr := [1, 2, 3, 4, 5, 6]
-x := [[a, b] for a <- arr, a < b for b <- arr, b > 2]
+x := [[a, b] for a <- arr if a < b for b <- arr if b > 2]
 println("x:", x)
diff --git a/parser/_testdata/selectdata/select.gop b/parser/_testdata/selectdata/select.gop
index 7034a292..c5880fe6 100644
--- a/parser/_testdata/selectdata/select.gop
+++ b/parser/_testdata/selectdata/select.gop
@@ -1,2 +1,2 @@
 a := [1, 3, 5, 7, 8, 19]
-y := {x for x <- a, x%2 == 0}
+y := {x for x <- a if x%2 == 0}
diff --git a/printer/_testdata/10-List-comprehension/list_comprehens.gop b/printer/_testdata/10-List-comprehension/list_comprehens.gop
index cacd8530..7f6e6724 100644
--- a/printer/_testdata/10-List-comprehension/list_comprehens.gop
+++ b/printer/_testdata/10-List-comprehension/list_comprehens.gop
@@ -1,14 +1,14 @@
 y := [x*x for x <- [1, 3, 5, 7, 11]]
 println(y)
 
-y = [x*x for x <- [1, 3, 5, 7, 11], x > 3]
+y = [x*x for x <- [1, 3, 5, 7, 11] if x > 3]
 println(y)
 
-z := [i+v for i, v <- [1, 3, 5, 7, 11], i%2 == 1]
+z := [i+v for i, v <- [1, 3, 5, 7, 11] if i%2 == 1]
 println(z)
 
 println([k+","+s for k, s <- {"Hello": "xsw", "Hi": "Go+"}])
 
 arr := [1, 2, 3, 4, 5, 6]
-x := [[a, b] for a <- arr, a < b for b <- arr, b > 2]
+x := [[a, b] for a <- arr if a < b for b <- arr if b > 2]
 println("x:", x)
diff --git a/printer/_testdata/11-Map-comprehension/map_comprehens.gop b/printer/_testdata/11-Map-comprehension/map_comprehens.gop
index 684cbf0e..23bd1f13 100644
--- a/printer/_testdata/11-Map-comprehension/map_comprehens.gop
+++ b/printer/_testdata/11-Map-comprehension/map_comprehens.gop
@@ -1,8 +1,8 @@
 y := {x: i for i, x <- [1, 3, 5, 7, 11]}
 println(y)
 
-y = {x: i for i, x <- [1, 3, 5, 7, 11], i%2 == 1}
+y = {x: i for i, x <- [1, 3, 5, 7, 11] if i%2 == 1}
 println(y)
 
-z := {v: k for k, v <- {1: "Hello", 3: "Hi", 5: "xsw", 7: "Go+"}, k > 3}
+z := {v: k for k, v <- {1: "Hello", 3: "Hi", 5: "xsw", 7: "Go+"} if k > 3}
 println(z)
diff --git a/printer/_testdata/12-Select-comprehension/select.gop b/printer/_testdata/12-Select-comprehension/select.gop
index 25e80401..cc9ee6ad 100644
--- a/printer/_testdata/12-Select-comprehension/select.gop
+++ b/printer/_testdata/12-Select-comprehension/select.gop
@@ -1,8 +1,8 @@
 a := [2, 3, 5, 7, 9, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59]
-where := {i for i, v <- a, v == 19}
+where := {i for i, v <- a if v == 19}
 println("19 is at index", where)
 
-if at, ok := {i for i, v <- a, v == 37}; ok {
+if at, ok := {i for i, v <- a if v == 37}; ok {
 	println("37 is found! index =", at)
 }
 
diff --git a/printer/_testdata/12-Select-comprehension2/findscore.gop b/printer/_testdata/12-Select-comprehension2/findscore.gop
index 3ba439a1..a7845a1d 100644
--- a/printer/_testdata/12-Select-comprehension2/findscore.gop
+++ b/printer/_testdata/12-Select-comprehension2/findscore.gop
@@ -4,7 +4,7 @@ type student struct {
 }
 
 func findScore(a []student, name string) (score int, ok bool) {
-	return {x.score for x <- a, x.name == name}
+	return {x.score for x <- a if x.name == name}
 }
 
 a := [student{"ken", 95}, student{"john", 90}, student{"tom", 58}]
diff --git a/printer/_testdata/13-Exists-comprehension/exists.gop b/printer/_testdata/13-Exists-comprehension/exists.gop
index 55e31657..2e4cd1bb 100644
--- a/printer/_testdata/13-Exists-comprehension/exists.gop
+++ b/printer/_testdata/13-Exists-comprehension/exists.gop
@@ -5,8 +5,8 @@ type student struct {
 
 a := [student{"du", 84}, student{"wang", 70}, student{"ken", 100}]
 
-hasFullMark := {for x <- a, x.score == 100}
+hasFullMark := {for x <- a if x.score == 100}
 println("is any student full mark:", hasFullMark)
 
-hasFailed := {for x <- a, x.score < 60}
+hasFailed := {for x <- a if x.score < 60}
 println("is any student failed:", hasFailed)
diff --git a/printer/_testdata/22-For-loop/for.gop b/printer/_testdata/22-For-loop/for.gop
index 5febb6fc..4eb17153 100644
--- a/printer/_testdata/22-For-loop/for.gop
+++ b/printer/_testdata/22-For-loop/for.gop
@@ -2,7 +2,7 @@
 // for with <-
 
 sum := 0
-for x <- [1, 3, 5, 7, 11, 13, 17], x > 3 {
+for x <- [1, 3, 5, 7, 11, 13, 17] if x > 3 {
 	sum += x
 }
 println("sum(5,7,11,13,17):", sum)