├── docs ├── cn │ ├── basic │ │ ├── paclet.md │ │ ├── trait.md │ │ ├── function.md │ │ ├── operators.md │ │ ├── special-input.md │ │ ├── module.md │ │ ├── enumerate.md │ │ ├── error-handling.md │ │ ├── control-flow.md │ │ ├── readme.md │ │ ├── class.md │ │ └── pattern-match.md │ ├── advance │ │ ├── builder.md │ │ ├── closure.md │ │ ├── extractor.md │ │ ├── iterators.md │ │ ├── collections.md │ │ ├── pattern-match.md │ │ ├── operator-overloading.md │ │ ├── generic-type.md │ │ ├── adhoc-polymorphism.md │ │ ├── readme.md │ │ ├── let-statement.md │ │ ├── algebraic-data-type.md │ │ └── higher-kinded-type.md │ ├── control │ │ ├── jump-if.md │ │ ├── loop-for.md │ │ ├── jump-control.md │ │ ├── jump-switch.md │ │ └── loop-while.md │ ├── definition │ │ ├── flags.md │ │ ├── module.md │ │ ├── union.md │ │ ├── def-micro.md │ │ ├── enumerate.md │ │ ├── interface.md │ │ ├── let-binding.md │ │ ├── package.md │ │ └── structure.md │ ├── invoke │ │ └── readme.md │ ├── pattern │ │ ├── readme.md │ │ ├── case.md │ │ └── extractor.md │ ├── collections │ │ ├── readme.md │ │ ├── tuple.md │ │ ├── list.md │ │ └── dict.md │ ├── appendix │ │ ├── identifier.md │ │ └── readme.md │ ├── function │ │ ├── definition.md │ │ ├── overload.md │ │ ├── conversion.md │ │ └── position.md │ ├── package │ │ └── readme.md │ ├── literal │ │ ├── xml.md │ │ ├── template.md │ │ ├── readme.md │ │ ├── number.md │ │ └── formatter.md │ ├── typing │ │ └── cast.md │ ├── Readme.md │ └── Readme.vk ├── en │ ├── invoke │ │ ├── apply.md │ │ ├── apply-dot.md │ │ ├── slice.md │ │ ├── variable.md │ │ ├── lambda-dot.md │ │ ├── lambda.md │ │ ├── subscript.md │ │ ├── monadic.md │ │ ├── readme.md │ │ └── curry.md │ ├── advance │ │ ├── readme.md │ │ ├── extractor.md │ │ ├── builder.md │ │ ├── pattern-match.md │ │ ├── logger.md │ │ └── effect-system.md │ ├── control │ │ ├── readme.md │ │ ├── handle-catch.md │ │ ├── handle-match.md │ │ ├── jump-switch.md │ │ ├── handle-try.md │ │ ├── jump-if.md │ │ ├── loop-for.md │ │ ├── loop-while.md │ │ ├── defer-evaluate.md │ │ ├── lazy-evaluate.md │ │ └── jump-control.md │ ├── definition │ │ ├── module.md │ │ ├── def-micro.md │ │ ├── package.md │ │ ├── subtyping.md │ │ ├── associated.md │ │ ├── enumerate.md │ │ ├── let-binding.md │ │ ├── union.md │ │ ├── disjoint-union.md │ │ ├── interface.md │ │ ├── typing.md │ │ ├── flags.md │ │ ├── readme.md │ │ ├── def-macro.md │ │ └── structure.md │ ├── collection │ │ ├── tuple.md │ │ ├── list.md │ │ ├── readme.md │ │ ├── set.md │ │ ├── map.md │ │ └── dict.md │ ├── appendix │ │ ├── readme.md │ │ └── for-rust.md │ ├── literal │ │ ├── xml.md │ │ ├── template.md │ │ ├── readme.md │ │ ├── number.md │ │ └── formatter.md │ └── readme.md ├── .vuepress │ ├── public │ │ └── Readme.md │ ├── styles │ │ └── palette.styl │ ├── config.ts │ └── readme.md └── readme.md ├── .editorconfig ├── .gitignore ├── package.json ├── netlify.toml ├── profile └── readme.md ├── scripts └── halfwidth.m ├── readme.md └── License.md /docs/cn/basic/paclet.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/cn/basic/trait.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/en/invoke/apply.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/cn/advance/builder.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/cn/advance/closure.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/cn/advance/extractor.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/cn/advance/iterators.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/cn/basic/function.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/cn/basic/operators.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/cn/control/jump-if.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/cn/control/loop-for.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/cn/definition/flags.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/cn/definition/module.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/cn/definition/union.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/cn/invoke/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/cn/pattern/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/en/advance/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/en/control/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/en/definition/module.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/en/invoke/apply-dot.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/en/invoke/slice.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/en/invoke/variable.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/cn/advance/collections.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/cn/advance/pattern-match.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/cn/collections/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/cn/control/jump-control.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/cn/control/jump-switch.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/cn/control/loop-while.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/cn/definition/def-micro.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/cn/definition/enumerate.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/cn/definition/interface.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/cn/definition/let-binding.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/cn/definition/package.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/cn/definition/structure.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/en/definition/def-micro.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/en/definition/package.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/en/definition/subtyping.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/en/invoke/lambda-dot.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/en/invoke/lambda.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/en/invoke/subscript.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/cn/advance/operator-overloading.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/en/collection/tuple.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/.vuepress/public/Readme.md: -------------------------------------------------------------------------------- 1 | # Resource Root -------------------------------------------------------------------------------- /docs/cn/appendix/identifier.md: -------------------------------------------------------------------------------- 1 | ## Identifiers 2 | 3 | 这是一个示例界面 -------------------------------------------------------------------------------- /docs/en/appendix/readme.md: -------------------------------------------------------------------------------- 1 | # Appendix 2 | 3 | 4 | ## Keywords 5 | -------------------------------------------------------------------------------- /docs/en/control/handle-catch.md: -------------------------------------------------------------------------------- 1 | catch performs pattern matching on effects 2 | -------------------------------------------------------------------------------- /docs/en/control/handle-match.md: -------------------------------------------------------------------------------- 1 | match performs pattern matching on types 2 | -------------------------------------------------------------------------------- /docs/en/invoke/monadic.md: -------------------------------------------------------------------------------- 1 | There are monadic versions of most calls. 2 | 3 | -------------------------------------------------------------------------------- /docs/cn/appendix/readme.md: -------------------------------------------------------------------------------- 1 | ## Keywords 2 | 3 | 关键词分为绝对关键词和相对关键词 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /docs/cn/basic/special-input.md: -------------------------------------------------------------------------------- 1 | > 注意 2 | > 3 | > 如果你需要使用与 Swift 保留关键字相同的名称作为常量或者变量名, 你可以使用反引号 (`) 将关键字包围的方式将其作为名字使用. 无论如何, 你应当避免使用关键字作为常量或变量名, 除非你别无选择. 4 | -------------------------------------------------------------------------------- /docs/cn/advance/generic-type.md: -------------------------------------------------------------------------------- 1 | ## Parametric Polymorphism 2 | 泛型 也叫 参化多态 3 | 4 | 类型的参数化, 就是可以把类型像方法的参数那样传递. 这一点意义非凡. 5 | 泛型使编译器可以在编译期间对类型进行检查以提高类型安全, 减少运行时由于对象类型不匹配引发的异常. -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = LF 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /docs/cn/function/definition.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ```valkyrie 5 | @inline 6 | def inline function_name[Generic](arguements, <, >, ): type / effect = { 7 | @todo(${function_name}) 8 | } 9 | ``` 10 | -------------------------------------------------------------------------------- /docs/en/definition/associated.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ```valkyrie 6 | interface Interator { 7 | # The associated type 8 | type Item 9 | next(mut self) -> Self::Item? 10 | } 11 | ``` 12 | -------------------------------------------------------------------------------- /docs/en/definition/enumerate.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Enumerate 4 | 5 | ```valkyrie 6 | enumerate MyEnum { 7 | 8 | } 9 | ``` 10 | 11 | 12 | 13 | ## Flags 14 | 15 | 16 | ```valkyrie 17 | flags MyFlags { 18 | 19 | } 20 | ``` 21 | -------------------------------------------------------------------------------- /docs/cn/package/readme.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | pkg 5 | 6 | pkg a { 7 | 8 | } 9 | 10 | 11 | ```yaml 12 | project/ 13 | src/ 14 | __.vk 15 | dothing/ 16 | __index__.vk 17 | test/ 18 | 19 | ``` 20 | 21 | 孤儿规则 22 | 23 | 扩展 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IDE 2 | .idea/ 3 | .vscode/ 4 | .vs/ 5 | *.iml 6 | 7 | # Yarn 8 | yarn.lock 9 | node_modules/ 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | Thumbs.db 14 | .DS_Store 15 | *.zip 16 | 17 | # Build 18 | .build/ 19 | .cache/ 20 | .temp/ 21 | 22 | -------------------------------------------------------------------------------- /docs/en/advance/extractor.md: -------------------------------------------------------------------------------- 1 | # Extractor Pattern 2 | 3 | 4 | 5 | ```valkyrie 6 | class Uppercase { 7 | 8 | } 9 | 10 | extends Uppercase: Extractor { 11 | overload extract(input: str): String? { 12 | return Some(input.to_uppercase()) 13 | } 14 | } 15 | 16 | "my Function".match { 17 | case Uppercase(upper): 18 | print(upper) # "MY FUNCTION" 19 | } 20 | ``` 21 | -------------------------------------------------------------------------------- /docs/en/control/jump-switch.md: -------------------------------------------------------------------------------- 1 | # Switch Statement 2 | 3 | Compiling a jump table, in general, will perform better than long if-else-if statements. 4 | 5 | ```valkyrie 6 | switch { 7 | when a > 0: 8 | print("a > 0") 9 | when a < 0: 10 | print("a < 0") 11 | when a == 0: 12 | print("a == 0") 13 | else: 14 | print("a is None") 15 | } 16 | ``` 17 | -------------------------------------------------------------------------------- /docs/readme.md: -------------------------------------------------------------------------------- 1 | ## Choose your language 2 | 3 | - [简体中文](/cn/readme.md) 4 | - [English](/en/readme.md) 5 | 6 | 7 | ## Community 8 | 9 | - Advice on Github: [github.com/valkyrie-language](https://github.com/nyar-vm/valkyrie-language/discussions) 10 | - Chat in Discord: [![discord](https://img.shields.io/discord/794446776232443955.svg?logo=discord&style=flat-square)](https://discord.gg/rDScD9GyUC) 11 | -------------------------------------------------------------------------------- /docs/cn/collections/tuple.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ## OrderedMap 6 | 7 | 8 | 9 | `IndexMap` is a Map that preserves the order of the keys. 10 | 11 | 12 | ## SortedMap 13 | 14 | `BTreeMap` is a Map that preserves the order of the keys. 15 | 16 | ## HashMap 17 | 18 | 19 | 20 | ## Dict 21 | 22 | `Dict` is a `OrderedMap` with string keys. 23 | 24 | ```valkyrie 25 | type Dict[T] = OrderedMap[String, T] 26 | ``` 27 | -------------------------------------------------------------------------------- /docs/en/collection/list.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | In the Valkyrie language, `List` refers specifically to heterogeneous lists, whose length and element types are known at compile-time. 5 | 6 | For homogeneous, dynamic length data should be storage in a `Array` instead. 7 | 8 | In fact, Tuples are heterogeneous lists of fixed length. 9 | 10 | ## Syntax 11 | 12 | ```valkyrie 13 | let List = [1u8, 2u16, 3u32]; 14 | ``` 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "reset": "git reset Head~ --soft", 4 | "start": "vuepress dev docs", 5 | "update": "yarn upgrade-interactive --latest", 6 | "test": "yarn install && yarn start", 7 | "build": "vuepress build docs", 8 | "deploy": "vuepress build docs" 9 | }, 10 | "dependencies": { 11 | "@vuepress/plugin-shiki": "^2.0.0-rc.0", 12 | "vuepress": "^2.0.0-rc.19" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /docs/en/definition/let-binding.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Bind with Value 4 | 5 | let can bind values to symbols, and symbols are divided into constants and variables. 6 | 7 | The type will be automatically deduced according to the environment and literal, 8 | if it cannot be deduced, it will become [Any](). 9 | 10 | ```valkyrie 11 | let a = 1 12 | let mut a = 2 13 | ``` 14 | 15 | ## Bind with Type 16 | 17 | 18 | ```valkyrie 19 | let i1: Integer; 20 | let i2: Integer = 0; 21 | ``` 22 | 23 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # Directory to change to before starting a build. 3 | # This is where we will look for package.json/.nvmrc/etc. 4 | base = "." 5 | 6 | # Default build command. 7 | command = "npm install && vuepress build docs" 8 | 9 | # Directory (relative to root of your repo) that contains the deploy-ready 10 | # HTML files and assets generated by the build. If a base directory has 11 | # been specified, include it in the publish directory path. 12 | publish = "docs/.build" 13 | -------------------------------------------------------------------------------- /docs/en/control/handle-try.md: -------------------------------------------------------------------------------- 1 | try comes with a local effect handler 2 | 3 | ## Option Handler 4 | 5 | ```valkyrie 6 | let a = try T? { 7 | map.get("value")! + 1 8 | } 9 | ``` 10 | 11 | Option handler 会捕捉所有实现了 Error 的类型. 12 | 13 | 也就是说除了 NoneError, 其他 Error 都会被抹除. 14 | 15 | ## Maybe Handler 16 | 17 | 只会捕捉 NoneError 这一个 Error 类型的 Handler. 18 | 19 | 捕捉后可以使用 `to_option` 转为 `Option` 20 | 21 | ```valkyrie 22 | let a = try Maybe { 23 | map.get("value")! + 1 24 | } 25 | .to_option() 26 | ``` 27 | -------------------------------------------------------------------------------- /docs/cn/advance/adhoc-polymorphism.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Nominal Type System 4 | 5 | 具名类型系统 6 | 7 | ## Structure Type System 8 | 9 | 结构类型系统 10 | 11 | 逆变, 协变 12 | 13 | ```ts 14 | class A { 15 | @[mut, set, get, public] 16 | a = 0 17 | b = 1 18 | 19 | [get, private] 20 | c = 2 21 | d = 4 22 | 23 | [] 24 | e = 8 25 | } 26 | 27 | class B { 28 | set get public mut a = 0 29 | set get public mut b = 1 30 | get private c = 2 31 | get private d = 4 32 | e = 8 33 | } 34 | 35 | ``` 36 | -------------------------------------------------------------------------------- /docs/cn/pattern/case.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | case 左边是任意字面量, 中间是 `=>` 箭头, 右边是匹配成功之后的行为 5 | 6 | ```valkyrie 7 | any.match { 8 | case 0u8 => print('0') 9 | case c: char if c == 'c' => print('c') 10 | case (item, ) => print('tuple') 11 | case table @ [head, **tail] => { 12 | print(table); 13 | print(head); 14 | print(tail); 15 | } 16 | @if(body.length > 2) 17 | case Body(body) => { 18 | print('empty') 19 | } 20 | case Structure { key: k, value: _, *** } => { 21 | print(k); 22 | } 23 | else => print('else') 24 | } 25 | ``` 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /profile/readme.md: -------------------------------------------------------------------------------- 1 | ## Hi there 👋 2 | 3 | 13 | -------------------------------------------------------------------------------- /docs/cn/literal/xml.md: -------------------------------------------------------------------------------- 1 | # XML Literal 2 | 3 | no less(`<`) and great(`>`) operator, use `<|` and `|>` instead. 4 | 5 | ### XML Comment 6 | 7 | ```valkyrie 8 | x""" 9 | 10 | """ 11 | ``` 12 | 13 | ### XML Class 14 | 15 | ```valkyrie 16 | x""" 17 | 18 | text 19 | 20 | """ 21 | ``` 22 | 23 | desugars to 24 | 25 | ```valkyrie 26 | Tag(attribute: true, a: 1) { 27 | "text" 28 | } 29 | ``` 30 | 31 | 32 | ## XML Inline 33 | 34 | ```valkyrie 35 | x""" 36 | 37 | """ 38 | ``` 39 | 40 | desugars to 41 | 42 | ```valkyrie 43 | Tag(attribute: true, a: 1); 44 | ``` 45 | -------------------------------------------------------------------------------- /docs/en/literal/xml.md: -------------------------------------------------------------------------------- 1 | # XML Literal 2 | 3 | no less(`<`) and great(`>`) operator, use `<|` and `|>` instead. 4 | 5 | ### XML Comment 6 | 7 | ```valkyrie 8 | x""" 9 | 10 | """ 11 | ``` 12 | 13 | ### XML Class 14 | 15 | ```valkyrie 16 | x""" 17 | 18 | text 19 | 20 | """ 21 | ``` 22 | 23 | desugars to 24 | 25 | ```valkyrie 26 | Tag(attribute: true, a: 1) { 27 | "text" 28 | } 29 | ``` 30 | 31 | 32 | ## XML Inline 33 | 34 | ```valkyrie 35 | x""" 36 | 37 | """ 38 | ``` 39 | 40 | desugars to 41 | 42 | ```valkyrie 43 | Tag(attribute: true, a: 1); 44 | ``` 45 | -------------------------------------------------------------------------------- /scripts/halfwidth.m: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | SetDirectory@FileNameJoin[{ParentDirectory@NotebookDirectory[], "docs"}]; 4 | 5 | 6 | files = FileNames["*.md", ".", Infinity]; 7 | halfwidth[path_] := Export[path, 8 | StringReplace[ 9 | Import[path, "Text"], 10 | { 11 | "\:ff08" -> " (", 12 | "\:ff09" -> ") ", 13 | "\[OpenCurlyDoubleQuote]" -> "\"", 14 | "\[CloseCurlyDoubleQuote]" -> "\"", 15 | "\:ff01" -> "! ", 16 | "\:3002" -> ". ", 17 | "\:ff0c" -> ", ", 18 | "\:ff1a" -> ": " 19 | } 20 | ], 21 | "Text" 22 | ]; 23 | 24 | 25 | halfwidth /@ files 26 | -------------------------------------------------------------------------------- /docs/cn/advance/readme.md: -------------------------------------------------------------------------------- 1 | # Handler Operators 2 | 3 | 这是一个示例界面 4 | 5 | 高级字符串用法 6 | 7 | ## 字符控制算符 8 | 9 | ### 转义 10 | 11 | ```ts 12 | "\u{12 123 1234}" 13 | ``` 14 | 15 | 16 | ### 多行字符串 17 | 18 | ```py 19 | """ 20 | multiline 21 | """ 22 | ``` 23 | 24 | ### 反转义 25 | 26 | ```py 27 | r""" 28 | multiline 29 | """ 30 | ``` 31 | 32 | ## 数值控制算符 33 | 34 | 35 | 36 | ```rs 37 | let byte = 0u8; 38 | let complex = 1 + 2i; 39 | ``` 40 | 41 | | 长度 | 有符号 | 无符号 | 42 | | ------- | ------- | ------- | 43 | | 8-bit | `i8` | `u8` | 44 | | 16-bit | `i16` | `u16` | 45 | | 32-bit | `i32` | `u32` | 46 | | 64-bit | `i64` | `u64` | 47 | | 128-bit | `i128` | `u128` | 48 | | arch | `isize` | `usize` | -------------------------------------------------------------------------------- /docs/en/literal/template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | Use the `t` prefix for template strings 4 | 5 | ### Template Comment 6 | 7 | ```valkyrie 8 | t""" 9 | {# This is comment #} 10 | 11 | "" 12 | ``` 13 | 14 | ### Template Expression 15 | 16 | ```valkyrie 17 | t""" 18 | {@ x @} 19 | {@ None @} 20 | {@ "string" @} 21 | "" 22 | ``` 23 | 24 | ### Template If 25 | 26 | ```valkyrie 27 | t""" 28 | {% if a > 0%} 29 | yes 30 | {% else %} 31 | no 32 | {% end if %} 33 | "" 34 | ``` 35 | 36 | 37 | ### Template While 38 | 39 | ```valkyrie 40 | t""" 41 | {% while a > 0 %} 42 | {% if a > 1 %} 43 | yes 44 | {@ a -= 1 @} 45 | {% else %} 46 | no 47 | {% end if %} 48 | {% end while %} 49 | ``` 50 | -------------------------------------------------------------------------------- /docs/cn/literal/template.md: -------------------------------------------------------------------------------- 1 | # 模板字符串 2 | 3 | 当一个字符前面加 `t` 或者被三个 `"` 包围时, 会被解析为模板字符串 4 | 5 | ## 模板注释 6 | 7 | 确切地说, 模板没有注释, 但是模板表达式中可以放置注释 8 | 9 | ```valkyrie 10 | """ 11 | <%/* This is comment */%> 12 | """ 13 | ``` 14 | 15 | ## Template Expression 16 | 17 | ```valkyrie 18 | """ 19 | <% x %> 20 | <% None %> 21 | <% "string" %> 22 | """ 23 | ``` 24 | 25 | ## Template If 26 | 27 | ```valkyrie 28 | """ 29 | <% if a > 0 %> 30 | yes 31 | <% else %> 32 | no 33 | <% end if %> 34 | """ 35 | ``` 36 | 37 | 38 | ### Template While 39 | 40 | ```valkyrie 41 | """ 42 | <% while a > 0 %> 43 | <% if a > 1 %> 44 | yes 45 | <% a -= 1 %> 46 | <% else %} 47 | no 48 | <% end if %> 49 | <% end while %> 50 | """ 51 | ``` 52 | -------------------------------------------------------------------------------- /docs/en/definition/union.md: -------------------------------------------------------------------------------- 1 | 在 VK 中, 有如下四种声明和类型的方式 2 | 3 | | Keywords | Layout | Detail | 4 | |---------------|------------------|-------------------------------------------------------------| 5 | | [enumerate]() | `[u8; N]` | A number, used to represent different states | 6 | | [flags]() | `[u8; N]` | A number used to represent different binary opposite states | 7 | | [union]() | `[u8; X]` | Some bytes, used to represent different states | 8 | | [unite]() | `[u8; N][u8; X]` | Some bytes, used to represent different states | 9 | 10 | - X is the largest size of all variant types 11 | - N is the given size of the representation 12 | -------------------------------------------------------------------------------- /docs/en/collection/readme.md: -------------------------------------------------------------------------------- 1 | | Type | Heterogeneous | Extensible | 2 | |--------|---------------|------------| 3 | | Array | Yes | Yes | 4 | | Tuple | Yes | Yes | 5 | | Vector | Yes | Yes | 6 | | List | Yes | Yes | 7 | 8 | ## Array 9 | 10 | `IndexMap` is a Map that preserves the order of the keys. 11 | 12 | ## Tuple 13 | 14 | Aka `Heterogeneous Array` 15 | 16 | Tuple 能够储存不同的类型 17 | 18 | ## Vector 19 | 20 | Aka `Extensible Array` 21 | 22 | `BTreeMap` is a Map that preserves the order of the keys. 23 | 24 | ## List 25 | 26 | Aka `Heterogeneous Vector` 27 | 28 | ## Dict 29 | 30 | `Dict` is a `OrderedMap` with string keys. 31 | 32 | ```valkyrie 33 | type Dict[T] = OrderedMap[String, T] 34 | ``` 35 | -------------------------------------------------------------------------------- /docs/cn/collections/list.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | | 类型 | 异质 | 扩张 | 5 | |----------|-----|-----| 6 | | `Array` | Yes | Yes | 7 | | `Tuple` | Yes | Yes | 8 | | `Vector` | Yes | Yes | 9 | | `List` | Yes | Yes | 10 | 11 | ## Array 12 | 13 | `IndexMap` is a Map that preserves the order of the keys. 14 | 15 | ## Tuple 16 | 17 | Aka `Heterogeneous Array` 18 | 19 | Tuple 能够储存不同的类型 20 | 21 | 22 | ## Vector 23 | 24 | Aka `Extensible Array` 25 | 26 | `BTreeMap` is a Map that preserves the order of the keys. 27 | 28 | ## List 29 | 30 | 在其他语言里也被叫做 [HList (Haskell)](https://hackage.haskell.org/package/HList), [HList (Scala)](https://www.scala-exercises.org/shapeless/heterogenous_lists). 31 | 32 | 33 | ## Dict 34 | 35 | `Dict` is a `OrderedMap` with string keys. 36 | 37 | ```valkyrie 38 | type Dict[T] = OrderedMap[String, T] 39 | ``` 40 | -------------------------------------------------------------------------------- /docs/cn/typing/cast.md: -------------------------------------------------------------------------------- 1 | ### Type Cast Hook 2 | 3 | ```valkyrie 4 | @type_cast.implicit: Graph 5 | @type_cast.implicit.clear: () 6 | @type_cast.implicit.try {Integer => Decimal} : bool 7 | @type_cast.implicit.insert {Integer8 => Integer => Decimal}: () 8 | @type_cast.implicit.remove {Integer => Decimal}: () 9 | ``` 10 | 11 | 12 | 类型转换是一个有向图, 前后节点之间实现了 `After: From[Before]`. 13 | 14 | 转换选取的函数会是节点中的最短路径, 该选取在编译期发生. 15 | 16 | ``` 17 | class macro type_cast { 18 | implicit_graph: Graph[Type] 19 | constructor() { 20 | this.implicit_graph = Graph() 21 | } 22 | @inline 23 | def implicit(): Graph { implicit_graph } 24 | def implicit.clear() { } 25 | def implicit.try(input: Expression): bool { } 26 | def implicit.insert(input: Expression) { } 27 | def implicit.remove(input: Expression) { } 28 | } 29 | ``` 30 | -------------------------------------------------------------------------------- /docs/cn/basic/module.md: -------------------------------------------------------------------------------- 1 | 2 | Valkyrie 中的导入非常简单, 一共就一个 import 3 | 4 | ``` 5 | import paclet.utils.{ 6 | a.* 7 | b as c 8 | d.e.{self, f.g as h} 9 | i.{self, j} 10 | } 11 | ``` 12 | 13 | 或者你可能觉得全部展开比较爽 14 | 15 | ``` 16 | import paclet.utils.a.* 17 | import paclet.utils.b as c 18 | import paclet.utils.b.d.e 19 | import paclet.utils.b.d.e.f.g as h 20 | import paclet.utils.i 21 | import paclet.utils.i.j 22 | ``` 23 | 24 | 不支持直接 import 然后一个括号 25 | 26 | 你至少得有一级包名 27 | 28 | 也不能直接一个路径或者 url 29 | 30 | 那也太大道至简了 31 | 32 | ``` 33 | //! 想啥呢, 不存在的 34 | @go import ( 35 | "fmt" as fmt 36 | "github.com/go/die" as die 37 | ) 38 | ``` 39 | 40 | Valkyrie 中你确实可以用一个网络包, 不过不是这种形式 41 | 42 | --- 43 | 44 | 导出就更简单了, 连 public 和 export 都不用 45 | 46 | module 标记存在一个包, `!` 表示可以导出, 完事 47 | 48 | ``` 49 | module a; 50 | import! a.f as g 51 | module! b; 52 | ``` -------------------------------------------------------------------------------- /docs/en/control/jump-if.md: -------------------------------------------------------------------------------- 1 | # If Statement 2 | 3 | 4 | ## Simple If Statement 5 | 6 | ```valkyrie 7 | if a > 0 { 8 | print("a > 0") 9 | } 10 | ``` 11 | 12 | 13 | 14 | ## If Else Statement 15 | 16 | ```valkyrie 17 | if a > 0 { 18 | print("a > 0") 19 | } 20 | else { 21 | print("a <= 0") 22 | } 23 | ``` 24 | 25 | 26 | ## If Else Statement 27 | 28 | ```valkyrie 29 | if a > 0 { 30 | print("a > 0") 31 | } 32 | else if b != 0 { 33 | print("a <= 0 and b != 0") 34 | } 35 | else { 36 | print("a <= 0 and b == 0") 37 | } 38 | ``` 39 | 40 | ## If Let Statement 41 | 42 | ```valkyrie 43 | if let Some(a) = a { 44 | print(a) 45 | } 46 | ``` 47 | 48 | 49 | ## If Let Else Statement 50 | 51 | ```valkyrie 52 | if let Some(a) = a { 53 | print(a) 54 | } 55 | else { 56 | print("a is None") 57 | } 58 | ``` 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /docs/en/advance/builder.md: -------------------------------------------------------------------------------- 1 | # Builder Pattern 2 | 3 | The builder pattern is a design pattern designed to provide a flexible solution to various object creation problems in object-oriented programming. 4 | 5 | The traditional builder pattern needs to define get, set, with, which is cumbersome. 6 | 7 | ```rust 8 | A::new(1) 9 | .with_b(2) 10 | .with_c("c") 11 | ``` 12 | 13 | Valkyrie Language provides an easy way 14 | 15 | ```valkyrie 16 | class A { 17 | a: int 18 | b: int = 1 19 | 20 | @get 21 | _c: String = "C" 22 | set c(value: String) { 23 | _c = value.to_uppercase() 24 | } 25 | 26 | constructor(a: int) { 27 | this.a = a 28 | } 29 | 30 | 31 | } 32 | 33 | new A(1) { 34 | b: 2, 35 | c: "c" 36 | } 37 | ``` 38 | 39 | 40 | 41 | 42 | ```valkyrie 43 | let mut a = A(1) 44 | a.b = 2 45 | a.c = "c" 46 | 47 | # Result 48 | A { 49 | a: 1, 50 | b: 2, 51 | _c: "C" 52 | } 53 | ``` 54 | -------------------------------------------------------------------------------- /docs/cn/literal/readme.md: -------------------------------------------------------------------------------- 1 | # 注释 2 | 3 | Valkyrie 使用 `//` 进行行注释, 使用 `/* */` 进行块注释, 使用 `///` 与 `/** */` 进行文档注释. 4 | 5 | ```valkyrie 6 | #! /usr/bin/env valkyrie 7 | // 这是一条行内注释 8 | 9 | /// 这是一套文档注释 10 | type MyResult = Result 11 | ``` 12 | 13 | # 字面量 14 | 15 | 这里是一些原始字面量: 16 | 17 | | 类型 | 示例 | 18 | |:--------|:---------------------------------| 19 | | Boolean | `true`, `false` | 20 | | Integer | `0`, `0u8`, `0int` | 21 | | Decimal | `0.0`, `0.0f32`, `0.0dec` | 22 | | String | `""`, `"Hello world!"`, `"'\n'"` | 23 | 24 | ## 字面量多态 25 | 26 | 同一个字面量对应多种可能的类型, 除非左边加上类型限定, 或者使用 `as` 转换. 27 | 28 | ```valkyrie 29 | let s1: str = "c" 30 | let s2 = "c" as String 31 | let c1: char = "c" 32 | let c2: = "c" as Character 33 | ``` 34 | 35 | 36 | 37 | # 进阶阅读 38 | 39 | - [数字字面量](./number.md) 40 | - [字符串插值](./formatter.md) 41 | - [字符串模板](./template.md) 42 | - [字符串标记](./xml.md) 43 | -------------------------------------------------------------------------------- /docs/en/appendix/for-rust.md: -------------------------------------------------------------------------------- 1 | # Rust user migration guide 2 | 3 | | rs type | vk type | description | 4 | |:------------------|:---------------|-------------------| 5 | | `bool` | `Boolean`, | alias is `bool` | 6 | | `char` | `Unicode`, | alias is `char` | 7 | | `&str` | `UTF8View`, | alias is `str` | 8 | | `String` | `UTF8Text`, | alias is `String` | 9 | | `&[T]` | `ArrayView` | 10 | | `Vec` | `Array` | 11 | | `&T` | `Reference` | 12 | | `Box` | `Pointer` | 13 | | `Rc`, `Arc` | `Shared` | 14 | | `Option` | `Option` | 15 | | `Result` | `Result` | 16 | 17 | | rs variant | vk variant | description | 18 | |:-----------|:-------------|-------------| 19 | | `Some(T)` | `Some(T)` | 20 | | `None` | `None` | 21 | | `Ok(T)` | `Success(T)` | 22 | | `Err(E)` | `Failure(E)` | 23 | -------------------------------------------------------------------------------- /docs/cn/function/overload.md: -------------------------------------------------------------------------------- 1 | ## Overloading Function 2 | 3 | ```valkyrie 4 | def overload print_number(number: Integer) { 5 | print("Integer: ${context}") 6 | } 7 | def overload print_number(number: Integer) { 8 | print("Decimal: ${context}") 9 | } 10 | ``` 11 | 12 | - 重载函数必须标记 `overload` 13 | - 重载函数必须写在同一个文件或者同一个扩展中 14 | - 重载函数的返回值必须和第一个完全相同 15 | - 如果没有合适的重载函数会在调用出产生编译错误 16 | 17 | 注意编译器总是按照定义顺序从上到下尝试匹配, 除此以外没有任何解析规则. 18 | 19 | 如果第一个函数匹配成功, 则编译器将不再尝试第二个函数. 20 | 21 | 任何复杂的解析过程都是人脑难以记忆的, 这样简单的规则有利于排查问题. 22 | 23 | ## Dynamic Invoke Function 24 | 25 | 26 | 27 | ## Multiple Dispatch Function 28 | 29 | 多派发也被称为运行时动态重载 30 | 31 | 多派发使用通过定义序 32 | 33 | ```valkyrie 34 | @dispatch(for_int, order: 100) 35 | def print_number(number: Integer) { 36 | print("Integer: ${context}") 37 | } 38 | @dispatch(for_dec, before: for_int) 39 | def print_number(number: Decimal) { 40 | print("Decimal: ${context}") 41 | } 42 | @dispatch(any) 43 | def print_number(number: Any) { 44 | print("Unknown Type") 45 | } 46 | ``` 47 | 48 | - 必须添加 `@dispatch(any)` 49 | -------------------------------------------------------------------------------- /docs/cn/collections/dict.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ## OrderedMap 6 | 7 | 在其他语言中也被叫做 [IndexMap (Rust)](https://hackage.haskell.org/package/ordered-containers), 8 | 9 | 有序表 10 | 11 | ```valkyrie 12 | let mut map: OrderedMap[String, Int] = [] 13 | map.insert("a", 1) 14 | map.insert("z", 26) 15 | map.insert("c", 3) 16 | print_line("${map}{:?}") 17 | # OrderedMap { "a" => 1, "z" => 26, "c" => 3 } 18 | ``` 19 | 20 | 21 | ## SortedMap 22 | 23 | 在其他语言中也被叫做 [BTreeMap (Rust)](https://hackage.haskell.org/package/ordered-containers) 24 | 25 | 排序表 26 | 27 | ```valkyrie 28 | let mut map: SortedMap[String, Int] = [] 29 | map.insert("a", 1) 30 | map.insert("z", 26) 31 | map.insert("c", 3) 32 | print_line("${map}{:?}") 33 | # SortedMap { "a" => 1, "c" => 3, "z" => 26 } 34 | ``` 35 | 36 | ## HashMap 37 | 38 | 哈希表 39 | 40 | ## Dict 41 | 42 | `Dict` is a `OrderedMap` with string keys. 43 | 44 | ```valkyrie 45 | type Dict[T] = OrderedMap[String, T] 46 | ``` 47 | 48 | 49 | ```valkyrie 50 | let map = [ 51 | a: 1, 52 | z: 26, 53 | c: 3 54 | ] 55 | print_line("${map}{:?}") 56 | # OrderedMap { "a" => 1, "c" => 3, "z" => 26 } 57 | ``` 58 | -------------------------------------------------------------------------------- /docs/.vuepress/styles/palette.styl: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 代码库重置 4 | .content pre{ 5 | margin: 0!important; 6 | } 7 | 8 | .extra-class::before { 9 | display: none; 10 | } 11 | 12 | 13 | /* 14 | $accentColor = #ff4757 15 | $textColor = #2c3e50 16 | $borderColor = #eaecef 17 | $codeBgColor = #282c34 18 | 19 | 20 | 21 | h1 { 22 | // color: #000000 23 | } 24 | 25 | h2 { 26 | font-size: 2rem 27 | color: #5352ed 28 | } 29 | 30 | h3 { 31 | font-size: 1.2rem 32 | margin-top: 0px 33 | padding-top: 10px 34 | padding-bottom: 0px 35 | } 36 | 37 | h4 { 38 | color: #6196CC 39 | font-size: 14px 40 | margin-top: 0px 41 | padding-top: 10px 42 | padding-bottom: 10px 43 | } 44 | 45 | blockquote { 46 | padding: 0.5rem 1.5rem 47 | border-left-width: 0.5rem 48 | border-left-style: solid 49 | margin: 1rem 0rem 50 | background-color: #f3f5f7 51 | border-color: #42b983 52 | color: black 53 | } 54 | 55 | blockquote p { 56 | margin-top: 4px 57 | margin-bottom: 4px 58 | margin-left: 0px 59 | margin-right: 0px 60 | } 61 | 62 | */ 63 | div.theme-default-content:not(.custom) { 64 | max-width: 100%; 65 | } 66 | -------------------------------------------------------------------------------- /docs/cn/function/conversion.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ```valkyrie 5 | @inline 6 | def inline function_name[Generic](arguements, <, >, ): type / effect = { 7 | todo("${function_name}") 8 | } 9 | ``` 10 | 11 | ### Implicit Type Conversion 12 | 13 | 14 | 15 | 隐式类型转换 16 | 17 | `Character => String`, `Integer => Decimal` 18 | 19 | ```valkyrie 20 | def print_string(string: String) { 21 | print(string) 22 | } 23 | print_string('c') 24 | # nothing wrong 25 | ``` 26 | 27 | 28 | ```valkyrie 29 | def print_string(explicit string: String) { 30 | print(string) 31 | } 32 | print_string('c') 33 | # CompileError: type mismatch; 34 | ``` 35 | 36 | 也可以用宏从上下文中移除自动转换规则. 37 | 38 | ```valkyrie 39 | @type_cast.implicit.remove { 40 | Character => String, 41 | Integer8 => Integer => Decimal 42 | } 43 | ``` 44 | 45 | ### Explicit Type Conversion 46 | 47 | ```valkyrie 48 | def print_string(string: String) { 49 | print(string) 50 | } 51 | print_string(1) 52 | # CompileError: type mismatch; 53 | ``` 54 | 55 | 56 | 57 | 58 | ```valkyrie 59 | def print_string(into string: String) { 60 | print(string) 61 | } 62 | print_string(1) 63 | # nothing wrong, same as 64 | print_string(1.into.[String]()) 65 | ``` 66 | 67 | ### Coercion 68 | 69 | -------------------------------------------------------------------------------- /docs/en/literal/readme.md: -------------------------------------------------------------------------------- 1 | ## Comment 2 | 3 | The Valkyrie language uses `#` for line comments, `#!` for shebangs, `#?` for documentation comments. 4 | 5 | ```valkyrie 6 | #! /usr/bin/env vk 7 | 8 | # This is a line comment 9 | 10 | #? This is a documentation comment 11 | type MyResult = Result 12 | ``` 13 | 14 | ## Literals 15 | 16 | Here are some primitive types: 17 | 18 | | Atom | Example | 19 | |:----------|:---------------------------------| 20 | | Boolean | `true`, `false` | 21 | | Integer | `0`, `0u8`, `0int` | 22 | | Decimal | `0.0`, `0.0f32`, `0.0dec` | 23 | | Character | `'n'`, `'\n'` | 24 | | String | `""`, `"Hello world!"` | 25 | 26 | Here are some common collections: 27 | 28 | | Collection | Example | 29 | |:-----------|:---------------------------------| 30 | | List | `List()`, `[1, 2, 3]` | 31 | | Dict | `Dict()`, `[a: 1, z: 26]` | 32 | | Tuple | `()`, `(1,)`, `(1, "Hello")` | 33 | | Option | `None`, `Some(1)` | 34 | | Result | `Success(1)`, `Failure("Error")` | 35 | 36 | ### Literal polymorphism 37 | 38 | The content represented by the literal value will be affected by the type on the left 39 | 40 | ```valkyrie 41 | let string: str = "c" 42 | let char: char = "c" 43 | let list: List[String] = [] 44 | let dict: Dict[String] = [] 45 | ``` 46 | 47 | -------------------------------------------------------------------------------- /docs/en/advance/pattern-match.md: -------------------------------------------------------------------------------- 1 | 2 | ## Literal Pattern Match 3 | 4 | 5 | ```valkyrie 6 | terms.match { 7 | case true: 8 | branch_a 9 | case "string": 10 | branch_b 11 | case 0: 12 | branch_c 13 | case _: 14 | branch_d 15 | } 16 | ``` 17 | 18 | 19 | ## Conditional Pattern Match 20 | 21 | 22 | ```valkyrie 23 | terms.match { 24 | when > 0: 25 | branch_a 26 | when b == 0: 27 | branch_b 28 | else: 29 | branch_c 30 | } 31 | ``` 32 | 33 | 34 | ## Type Pattern Match 35 | 36 | 37 | ```valkyrie 38 | terms.match { 39 | # same as `case a if a is Integer:` 40 | type Integer a: 41 | branch_a 42 | case a if a is Integer: 43 | branch_c 44 | # a can cast to T 45 | case a is Integer: 46 | branch_b 47 | # a can convert to T 48 | case a as Integer: 49 | branch_b 50 | } 51 | ``` 52 | 53 | ## Tuple Pattern Match 54 | 55 | 56 | ```valkyrie 57 | terms.match { 58 | case (a, _): 59 | branch_a 60 | case Named(_, b): 61 | branch_b 62 | } 63 | ``` 64 | 65 | ## Class Pattern Match 66 | 67 | ```valkyrie 68 | terms.match { 69 | case { a, *** }: 70 | branch_a 71 | case Named{ a: _, b }: 72 | branch_b 73 | } 74 | ``` 75 | 76 | ## Union Pattern Match 77 | 78 | All captures with the same name on the same branch must be type-compatible to first capture. 79 | 80 | ```valkyrie 81 | terms.match { 82 | case Some(a) | Success { value: a }: 83 | branch_a 84 | } 85 | ``` 86 | -------------------------------------------------------------------------------- /docs/en/control/loop-for.md: -------------------------------------------------------------------------------- 1 | Another commonly used looping method is the iterator traversal. 2 | 3 | ## For Loop 4 | 5 | ```valkyrie 6 | for pattern in iterator { 7 | # ... 8 | } 9 | ``` 10 | 11 | equivalent to 12 | 13 | ```valkyrie 14 | loop { 15 | let pattern = iterator.next() 16 | if pattern.is_none() { 17 | break 18 | } 19 | let pattern = pattern.unwrap() 20 | # ... 21 | } 22 | ``` 23 | 24 | ## For Loop Guard 25 | 26 | `if` can be added later as a shorthand for select conditions 27 | 28 | ```valkyrie 29 | for a in iterator if a > 0 { 30 | # ... 31 | } 32 | ``` 33 | 34 | equivalent to 35 | 36 | ```valkyrie 37 | for a in iterator.select({ $a > 0 }) { 38 | # ... 39 | } 40 | ``` 41 | 42 | ## Empty For Loop 43 | 44 | `otherwise` is used to indicate that the loop never happened once. 45 | 46 | ```valkyrie 47 | for x in iterator if check(x) { 48 | # ... 49 | } 50 | otherwise { 51 | print("empty") 52 | } 53 | ``` 54 | 55 | equivalent to 56 | 57 | ```valkyrie 58 | let mut no_run = true 59 | for x in iterator { 60 | guard check(x) else { 61 | continue 62 | } 63 | no_run = false 64 | # ... 65 | } 66 | if no_run { 67 | print("empty") 68 | } 69 | ``` 70 | 71 | That is to say, if the if-guard fails, it will not count as executing a loop. 72 | 73 | 74 | ## For Loop with Index 75 | 76 | When you need numbered traversal, don't create a new `index` then `index++`. 77 | 78 | use the zipper method. 79 | 80 | ```valkyrie 81 | for (index, value) in iterator.zip_indexer(0) { 82 | 83 | } 84 | ``` 85 | 86 | -------------------------------------------------------------------------------- /docs/en/advance/logger.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | `print`/`trace`/`debug`/`alert`/`error`/`fatal`/`panic` 15 | 16 | `std::io::Logger` 17 | 18 | 19 | 20 | ```valkyrie 21 | singleton class Logger { 22 | #? The normal log event callbacks. 23 | event logging(messages: UTF8Text, level: LogLevel) { 24 | let time = std::time::now().format("%Y-%m-%d %H:%M:%S"); 25 | Self::print("[{time}][{level}] {messages}") 26 | } 27 | #? The panic event callback, program must exit after this event. 28 | unique event panic(messages: UTF8Text) { 29 | let mut io = std::io::out(); 30 | write(io, messages); 31 | std::process::exit(1); 32 | } 33 | trace(messages: UTF8Text) { Self::logging(messages, LogLevel::Trace) } 34 | debug(messages: UTF8Text) { Self::logging(messages, LogLevel::Debug) } 35 | #? Write messages to the standard output stream. 36 | print(**messages: UTF8Text, <, >, separator: UTF8Text = '', eos: UTF8Text = '\n') { 37 | let mut io = std::io::out().lock(); 38 | write(io, messages, separator: separator); 39 | write(io, eos); 40 | } 41 | alert(messages: UTF8Text) { Self::logging(messages, LogLevel::Alert) } 42 | error(messages: UTF8Text) { Self::logging(messages, LogLevel::Error) } 43 | fatal(messages: UTF8Text) { Self::logging(messages, LogLevel::Fatal) } 44 | initialize(self) { } 45 | reinitialize(self) { self.logging.clear() } 46 | deinitialize(self) { @compile_error("Logger cannot be deinitialized.") } 47 | } 48 | ``` 49 | -------------------------------------------------------------------------------- /docs/cn/function/position.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```valkyrie 4 | def function_name(positional, <, free, >, named) { 5 | 6 | } 7 | ``` 8 | 9 | 10 | ## Position only arguments 11 | 12 | Positional arguments 13 | 14 | 位置参数 15 | 16 | ```valkyrie 17 | def f(a, <) {} 18 | 19 | f() # Error, argument required 20 | f(1) # Allowed, it's a positional argument 21 | f(a=1) # Error, positional only argument 22 | 23 | def f(a=2, <) {} 24 | 25 | f() # Allowed, argument is optional 26 | f(1) # Allowed, it's a positional argument 27 | f(a=1) # Error, positional only argument 28 | ``` 29 | 30 | ## Named only arguments 31 | 32 | Aka [Keyword arguments (Python)]() 33 | 34 | 具名参数 35 | 36 | ```valkyrie 37 | def f(>, a) {} 38 | 39 | f() # Error, argument required 40 | f(1) # Error, keyword only arguments 41 | f(a=1) # Allowed, it's a keyword argument 42 | 43 | def f(>, a=1) {} 44 | 45 | f() # Allowed 46 | f(1) # Error, keyword only argument 47 | f(a=1) # Allowed, it's a keyword argument 48 | ``` 49 | 50 | ## Context Receiver 51 | 52 | 隐式类型转换 53 | 54 | `Character => String`, `Integer => Decimal` 55 | 56 | ```valkyrie 57 | def print_context(>, receiver context: String) { 58 | print(context) 59 | } 60 | 61 | let context = "test" 62 | print_context() #> test 63 | # same as 64 | print_context(context: context) 65 | ``` 66 | 67 | ## 其他定义 68 | 69 | ### 省略规则 70 | 71 | - `(arg1) -> (arg1, <, >)` 72 | - `(arg1, <, arg2) -> (arg1, <, arg2, >)` 73 | - `(arg1, >, arg2) -> (arg1, <, >, arg2)` 74 | 75 | Keyword arguments are arguments that can be called by their name. 76 | 77 | Required arguments are arguments that must passed to the function. 78 | 79 | Optional arguments are arguments that can be not passed to the function. In Python, optional arguments are arguments that have a default value. 80 | 81 | -------------------------------------------------------------------------------- /docs/en/control/loop-while.md: -------------------------------------------------------------------------------- 1 | 2 | Conditional loops are divided into two types: `while` and `until`. 3 | 4 | `until condition` is essentially `while !condition`. 5 | 6 | ## Unconditional Loop 7 | 8 | Unconditional loop, if there is no expression after the loop keyword, then it is an unconditional loop. 9 | 10 | ```valkyrie 11 | while { 12 | 13 | 14 | } 15 | until { 16 | 17 | 18 | } 19 | ``` 20 | 21 | or the expression is a boolean constant. 22 | 23 | 24 | ```valkyrie 25 | while true { 26 | 27 | 28 | } 29 | until false { 30 | 31 | 32 | } 33 | ``` 34 | 35 | If the unconditional loop does not produce any side effects, the entire expression is simply removed. 36 | 37 | ## Conditional Loop 38 | 39 | If there is some non-constant expression after the loop keyword, then it is a conditional loop. 40 | 41 | ```valkyrie 42 | while a > 0 { 43 | a -= 1 44 | } 45 | 46 | until a < 0 { 47 | a -= 1 48 | } 49 | ``` 50 | 51 | In general, the loop will not end until the statement condition is not satisfied. 52 | 53 | You can also use [non-local return](./jump-control.md#non-local-return) to break out of the loop early 54 | 55 | ## Pattern Loop 56 | 57 | You can also use pattern matching as a loop condition: 58 | 59 | ```valkyrie 60 | while let Some(x) = iter.next() { 61 | # ... 62 | } 63 | until let None = iter.next() { 64 | # ... 65 | } 66 | ``` 67 | 68 | 69 | ## Empty Loop 70 | 71 | `otherwise` is used to indicate that the loop never happened once 72 | 73 | ```valkyrie 74 | while a > 0 { 75 | # ... 76 | } 77 | otherwise { 78 | print("empty") 79 | } 80 | ``` 81 | 82 | equivalent to 83 | 84 | ```valkyrie 85 | let mut no_run = true 86 | while a > 0 { 87 | no_run = false 88 | # ... 89 | } 90 | if no_run { 91 | print("empty") 92 | } 93 | ``` 94 | 95 | Often used to indicate the behavior of an iterator when it is empty 96 | -------------------------------------------------------------------------------- /docs/en/collection/set.md: -------------------------------------------------------------------------------- 1 | The set in the valkyrie language can be divided into three kinds: 2 | 3 | - [IndexSet](#indexset): A set that maintains insertion order. 4 | - [OrderSet](#orderset): A set rearranged according to the [Order]() trait 5 | - [HashSet](#hashset): A set rearranged according to [Hash]() trait 6 | 7 | ## IndexSet 8 | 9 | The `IndexSet` is a set that maintains the insertion order of its elements. 10 | 11 | It ensures that each element is unique and retains the order in which the elements were inserted. 12 | 13 | Here's an example: 14 | 15 | ```valkyrie 16 | let map = new IndexMap() { 17 | "orange" 18 | "apple" 19 | "banana" 20 | } 21 | for item in map { 22 | print("{item}"); 23 | } 24 | ``` 25 | 26 | Output should be: 27 | 28 | ```yaml 29 | orange 30 | apple 31 | banana 32 | ``` 33 | 34 | ## OrderSet 35 | 36 | The OrderSet is a set that orders its elements according to the Order trait. 37 | 38 | It allows custom ordering of the elements based on a specified criterion. 39 | 40 | Here's an example: 41 | 42 | ```valkyrie 43 | let map = new OrderSet() { 44 | "orange" 45 | "apple" 46 | "banana" 47 | } 48 | for item in map { 49 | print("{item}"); 50 | } 51 | ``` 52 | 53 | Output should be: 54 | 55 | ```yaml 56 | apple 57 | banana 58 | orange 59 | ``` 60 | 61 | ## HashSet 62 | 63 | The HashSet is an unordered set that arranges its elements based on the Hash trait. 64 | 65 | It provides efficient lookups and ensures uniqueness of elements. 66 | 67 | Here's an example: 68 | 69 | ```valkyrie 70 | let map = new HashSet() { 71 | "orange" 72 | "apple" 73 | "banana" 74 | } 75 | for item in map { 76 | print("{item}"); 77 | } 78 | ``` 79 | 80 | Output may be: 81 | 82 | ```yaml 83 | banana 84 | apple 85 | orange 86 | ``` 87 | 88 | The traversal order of the hash set is random. 89 | -------------------------------------------------------------------------------- /docs/en/advance/effect-system.md: -------------------------------------------------------------------------------- 1 | 2 | ```valkyrie 3 | class DivideError: Error { 4 | numerator: Integer 5 | # The left side represents the uploaded parameters, 6 | # and the right side represents the downloaded parameters 7 | effect divide_zero(numerator: Integer) -> Integer { 8 | # Must return Self, must be a Pure function 9 | new Self { numerator } 10 | } 11 | } 12 | ``` 13 | 14 | The type signature part is divided into two parts `Type` / `Effect`, both of which can be omitted, the default value of `Type` is Unit, and the default value of `Effect` is Pure. 15 | 16 | 17 | There are two types of pattern matching, match matches `Type`, catch matches `Effect`, raise changes `Type` to `Effect`, resume changes `Effect` to `Type`. 18 | 19 | ```valkyrie 20 | micro div2(a: Integer, b: Integer): Unit / DivideError { 21 | if (b == 0) { 22 | let fill = raise DivideError::divide_zero(a); 23 | print("can't divide zero, default as { fill }") 24 | } 25 | else { 26 | print("{a} / {b} = {a / b}") 27 | } 28 | } 29 | 30 | try { 31 | div2(0, 0) 32 | div2(1, 0) 33 | } 34 | .catch { 35 | case DivideError(numerator): 36 | delay(100ms) { 37 | resume numerator 38 | } 39 | } 40 | ``` 41 | 42 | 43 | ## Implement Details 44 | 45 | The core idea is that Valkyrie will do cps transformation for all functions with effect. 46 | 47 | ```valkyrie 48 | // cps form 49 | micro div2(a: Integer, b: Integer, divide_error: DivideError?): Unit / DivideError { 50 | if (b == 0) { 51 | let fill = divide_error; 52 | print("can't divide zero, default as { fill }") 53 | } 54 | else { 55 | print("{a} / {b} = {a / b}") 56 | } 57 | } 58 | 59 | try { 60 | div2(0, 0, k) 61 | div2(1, 0, k) 62 | } 63 | .catch { 64 | case DivideError(numerator): 65 | delay(100ms) { 66 | resume numerator 67 | } 68 | } 69 | ``` 70 | -------------------------------------------------------------------------------- /docs/en/literal/number.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Sized Number 4 | 5 | The default integer type is `Integer`, short alias is `int`. 6 | 7 | It is a reference type, which represents an arbitrary precision integer. 8 | 9 | If you want some fixed size number, you can write 255u8 10 | 11 | Here are some other primitive numeric type 12 | 13 | | postfix | description | example | 14 | |--------:|--------------------------|-----------------------------------------------:| 15 | | `u8` | unsigned 8 bit integer | `255u8` | 16 | | `i8` | signed 8 bit integer | `-127i8` | 17 | | `u16` | unsigned 16 bit integer | `65535u16` | 18 | | `i16` | signed 16 bit integer | `-32767i16` | 19 | | `b16` | truncated 32-bit float | `0.5f16` | 20 | | `u32` | unsigned 32 bit integer | `4294967295u32` | 21 | | `i32` | signed 32 bit integer | `-2147483647i32` | 22 | | `f32` | single precision float | `0.5f32` | 23 | | `u64` | unsigned 64 bit integer | `18446744073709551615u64` | 24 | | `i64` | signed 64 bit integer | `-9223372036854775807i64` | 25 | | `f64` | double precision float | `0.5f64` | 26 | | `u128` | unsigned 128 bit integer | `340282366920938463463374607431768211455u128` | 27 | | `i128` | signed 128 bit integer | `-170141183460469231731687303715884105727i128` | 28 | | `d128` | 128 bit decimal | `0.5d128` | 29 | 30 | ## Unit number 31 | 32 | In addition, you can customize other postfix number types 33 | 34 | ```valkyrie 35 | 1m + 2cm 36 | 1kg * 9.8`m/s^2` 37 | ``` 38 | 39 | 40 | -------------------------------------------------------------------------------- /docs/cn/literal/number.md: -------------------------------------------------------------------------------- 1 | # 数字类型 2 | 3 | ## Sized Number 4 | 5 | The default integer type is `Integer`, short alias is `int`. 6 | 7 | It is a reference type, which represents an arbitrary precision integer. 8 | 9 | If you want some fixed size number, you can write 255u8 10 | 11 | Here are some other primitive numeric type 12 | 13 | | postfix | description | example | 14 | |--------:|--------------------------|-----------------------------------------------:| 15 | | `u8` | unsigned 8 bit integer | `255u8` | 16 | | `i8` | signed 8 bit integer | `-127i8` | 17 | | `u16` | unsigned 16 bit integer | `65535u16` | 18 | | `i16` | signed 16 bit integer | `-32767i16` | 19 | | `b16` | truncated 32-bit float | `0.5f16` | 20 | | `u32` | unsigned 32 bit integer | `4294967295u32` | 21 | | `i32` | signed 32 bit integer | `-2147483647i32` | 22 | | `f32` | single precision float | `0.5f32` | 23 | | `u64` | unsigned 64 bit integer | `18446744073709551615u64` | 24 | | `i64` | signed 64 bit integer | `-9223372036854775807i64` | 25 | | `f64` | double precision float | `0.5f64` | 26 | | `u128` | unsigned 128 bit integer | `340282366920938463463374607431768211455u128` | 27 | | `i128` | signed 128 bit integer | `-170141183460469231731687303715884105727i128` | 28 | | `d128` | 128 bit decimal | `0.5d128` | 29 | 30 | ## Unit number 31 | 32 | In addition, you can customize other postfix number types 33 | 34 | ```valkyrie 35 | 1m + 2cm 36 | 1kg * 9.8`m/s^2` 37 | ``` 38 | 39 | 40 | -------------------------------------------------------------------------------- /docs/en/collection/map.md: -------------------------------------------------------------------------------- 1 | The map in the valkyrie language can be divided into three kinds: 2 | 3 | - [IndexMap](#indexmap): A map table that maintains insertion order. 4 | - [OrderMap](#ordermap): A map table rearranged according to the [Order]() trait 5 | - [HashMap](#hashmap): A map table rearranged according to [Hash]() trait 6 | 7 | ## IndexMap 8 | 9 | The `IndexMap` is a map table that maintains insertion order. 10 | 11 | It allows you to store key-value pairs and retrieve them in the order they were inserted. 12 | 13 | Here's an example: 14 | 15 | ```valkyrie 16 | let map = new IndexMap() { 17 | Pair("orange", 7), 18 | Pair("apple", 10), 19 | Pair("banana", 5), 20 | } 21 | for (key, value) in map { 22 | print("{key}: value"); 23 | } 24 | ``` 25 | 26 | Output should be: 27 | 28 | ```yaml 29 | orange: 7 30 | apple: 10 31 | banana: 5 32 | ``` 33 | 34 | ## OrderMap 35 | 36 | The OrderMap is a map table that rearranges its elements according to the Order trait. 37 | 38 | It provides a customizable way to order the map's entries. 39 | 40 | Here's an example: 41 | 42 | ```valkyrie 43 | let map = new OrderMap() { 44 | Pair("orange", 7), 45 | Pair("apple", 10), 46 | Pair("banana", 5), 47 | } 48 | for (key, value) in map { 49 | print("{key}: value"); 50 | } 51 | ``` 52 | 53 | Output should be: 54 | 55 | ```yaml 56 | apple: 10 57 | banana: 5 58 | orange: 7 59 | ``` 60 | 61 | ## HashMap 62 | 63 | The HashMap is a map table that rearranges its elements according to the Hash trait. 64 | 65 | It provides fast key-value lookups based on hash values. 66 | 67 | Here's an example: 68 | 69 | ```valkyrie 70 | let map = new HashMap() { 71 | Pair("orange", 7), 72 | Pair("apple", 10), 73 | Pair("banana", 5), 74 | } 75 | for (key, value) in map { 76 | print("{key}: value"); 77 | } 78 | ``` 79 | 80 | Output may be: 81 | 82 | ```yaml 83 | banana: 5 84 | apple: 10 85 | orange: 7 86 | ``` 87 | 88 | The traversal order of the hash table is random. 89 | -------------------------------------------------------------------------------- /docs/cn/basic/enumerate.md: -------------------------------------------------------------------------------- 1 | ## 定义枚举 2 | 3 | 让我们看看一个需要诉诸于代码的场景, 来考虑为何此时使用枚举更为合适且实用. 假设我们要处理 IP 地址. 目前被广泛使用的两个主要 IP 标准: IPv4 (version four) 和 IPv6 (version six) . 这是我们的程序可能会遇到的所有可能的 IP 地址类型: 所以可以 **枚举** 出所有可能的值, 这也正是此枚举名字的由来. 4 | 5 | 任何一个 IP 地址要么是 IPv4 的要么是 IPv6 的, 而且不能两者都是. IP 地址的这个特性使得枚举数据结构非常适合这个场景, 因为枚举值只可能是其中一个成员. IPv4 和 IPv6 从根本上讲仍是 IP 地址, 所以当代码在处理适用于任何类型的 IP 地址的场景时应该把它们当作相同的类型. 6 | 7 | 可以通过在代码中定义一个 `IpAddrKind` 枚举来表现这个概念并列出可能的 IP 地址类型, `V4` 和 `V6`. 这被称为枚举的 **成员** (*variants*) : 8 | 9 | ```valkyrie 10 | enum IpAddrKind { 11 | V4, 12 | V6, 13 | } 14 | ``` 15 | 16 | 现在 `IpAddrKind` 就是一个可以在代码中使用的自定义数据类型了. 17 | 18 | ### 枚举值 19 | 20 | 可以像这样创建 `IpAddrKind` 两个不同成员的实例: 21 | 22 | ```valkyrie 23 | # enum IpAddrKind { 24 | # V4, 25 | # V6, 26 | # } 27 | # 28 | let four = IpAddrKind::V4; 29 | let six = IpAddrKind::V6; 30 | ``` 31 | 32 | 注意枚举的成员位于其标识符的命名空间中, 并使用两个冒号分开. 这么设计的益处是现在 `IpAddrKind::V4` 和 `IpAddrKind::V6` 都是 `IpAddrKind` 类型的. 例如, 接着可以定义一个函数来获取任何 `IpAddrKind`: 33 | 34 | ```valkyrie 35 | # enum IpAddrKind { 36 | # V4, 37 | # V6, 38 | # } 39 | # 40 | fn route(ip_type: IpAddrKind) { } 41 | ``` 42 | 43 | 现在可以使用任一成员来调用这个函数: 44 | 45 | ```valkyrie 46 | # enum IpAddrKind { 47 | # V4, 48 | # V6, 49 | # } 50 | # 51 | # fn route(ip_type: IpAddrKind) { } 52 | # 53 | route(IpAddrKind::V4); 54 | route(IpAddrKind::V6); 55 | ``` 56 | 57 | 我们可以使用一种更简洁的方式来表达相同的概念, 仅仅使用枚举并将数据直接放进每一个枚举成员而不是将枚举作为结构体的一部分. `IpAddr` 枚举的新定义表明了 `V4` 和 `V6` 成员都关联了 `String` 值: 58 | 59 | ```valkyrie 60 | enum IpAddr { 61 | V4(String), 62 | V6(String), 63 | } 64 | 65 | let home = IpAddr::V4(String::from("127.0.0.1")); 66 | 67 | let loopback = IpAddr::V6(String::from("::1")); 68 | ``` 69 | 70 | 我们直接将数据附加到枚举的每个成员上, 这样就不需要一个额外的结构体了. 71 | 72 | 用枚举替代结构体还有另一个优势: 每个成员可以处理不同类型和数量的数据. IPv4 版本的 IP 地址总是含有四个值在 0 和 255 之间的数字部分. 如果我们想要将 `V4` 地址存储为四个 `u8` 值而 `V6` 地址仍然表现为一个 `String`, 这就不能使用结构体了. 枚举则可以轻易处理的这个情况: 73 | 74 | ```valkyrie 75 | enum IpAddr { 76 | V4(u8, u8, u8, u8), 77 | V6(String), 78 | } 79 | 80 | let home = IpAddr::V4(127, 0, 0, 1); 81 | 82 | let loopback = IpAddr::V6(String::from("::1")); 83 | ``` 84 | 85 | 86 | 让我们看看标准库中的另一个非常常见且实用的枚举: `Option`. 87 | -------------------------------------------------------------------------------- /docs/en/definition/disjoint-union.md: -------------------------------------------------------------------------------- 1 | Another important element in algebraic data types is the sum type, 2 | which is represented by `unite` in the Valkyrie language. 3 | 4 | unite Each variant can have its own field, or it can have no field 5 | 6 | ```valkyrie 7 | unite Test { 8 | A { } 9 | B { 10 | b: int 11 | } 12 | C { 13 | c1: int 14 | c2: str 15 | } 16 | } 17 | ``` 18 | 19 | Unite can easily use pattern matching to implement interfaces 20 | 21 | ```valkyrie 22 | extends Test { 23 | to_string(self) { 24 | self.match { 25 | case A: "A" 26 | case B(b): "B: integer is {b}" 27 | case C(c1, c2): "C: integer is {c1}, string is {c2}" 28 | } 29 | } 30 | } 31 | ``` 32 | 33 | ## Implementation Details 34 | 35 | Disjoint union types will be expanded to `trait` + `subtype`. 36 | 37 | ```valkyrie 38 | unite Result { 39 | Success { 40 | value: T 41 | } 42 | Failure { 43 | error: E 44 | } 45 | } 46 | 47 | extends Result { 48 | unwrap(self) -> T / E { 49 | match self { 50 | Success(value) => value 51 | Failure(error) => raise error 52 | } 53 | } 54 | } 55 | ``` 56 | 57 | You can also manually implement an extensible Σ type based on this. 58 | 59 | ```valkyrie 60 | @unite 61 | @implements.in(file) 62 | trait Result { 63 | unwrap(self): T / E 64 | } 65 | 66 | @variant 67 | class Success: Result { 68 | value: T 69 | construct(value: T) { 70 | self.value = value 71 | } 72 | extract(self) -> [value: T]? { 73 | Some([self.value]) 74 | } 75 | unwrap(self) -> T { 76 | self.value 77 | } 78 | } 79 | 80 | @variant 81 | class Failure: Result { 82 | error: E 83 | construct(error: E) { 84 | self.error = error 85 | } 86 | extract(self) -> [error: E]? { 87 | Some([self.error]) 88 | } 89 | unwrap(self) / E { 90 | raise self.error 91 | } 92 | } 93 | ``` 94 | 95 | Note that the `construct` and `extract` here are not written in the trait constraints, 96 | so that subclasses can implement them according to their needs 97 | -------------------------------------------------------------------------------- /docs/en/control/defer-evaluate.md: -------------------------------------------------------------------------------- 1 | ## Defer Evaluation 2 | 3 | Defer evaluation is a programming technique that allows you to delay the execution of a code block until a specific point, usually when the enclosing scope is exited. 4 | 5 | In Valkyrie, you can use the `defer` keyword to achieve defer evaluation. 6 | 7 | Here's an example of defer evaluation in Valkyrie: 8 | 9 | ```valkyrie 10 | fn read_file(path: Path): String { 11 | let mut file = File::open(path); 12 | defer { 13 | print("Closing file..."); 14 | file.close(); 15 | } 16 | file.read_to_string() 17 | } 18 | ``` 19 | 20 | In the above code, the `defer` keyword is used to define a code block that will be executed when the function `processFile` is exited. The code block closes the file using the `close()` method. By using `defer`, we ensure that the file is always closed, regardless of how the function is exited (e.g., normal return, exception, etc.). 21 | 22 | ## `defer` in `defer` 23 | 24 | ## Exit Hooks 25 | 26 | The defer mechanism in Valkyrie works based on the concept of exit hooks. 27 | 28 | An exit hook is a piece of code that is executed when a specific scope is exited. 29 | 30 | In Valkyrie, the `defer` keyword is implemented as a macro that adds an exit hook to the current scope. 31 | 32 | Here's an example of how the `defer` macro can be implemented to add an exit hook: 33 | 34 | ```valkyrie 35 | keyword macro defer(k: Continuation<()>) { 36 | @scope(caller).exit += k; 37 | } 38 | ``` 39 | 40 | In the above code, the `defer` macro takes a continuation `k` as an argument, which represents the code block to be executed on exit. The macro then adds the continuation `k` to the exit hooks of the current scope using the `+=` operator. 41 | 42 | Exit hooks can be added manually to a scope as well, without using the `defer` macro. 43 | 44 | For example: 45 | 46 | ```valkyrie 47 | @scope.exit += { 48 | print("Exiting Scope..."); 49 | } 50 | ``` 51 | 52 | In this code, we add an exit hook to the current scope using the `+=` operator. 53 | 54 | The exit hook is a closure that prints a message indicating that the scope is being exited. 55 | 56 | The defer mechanism in Valkyrie provides a convenient way to ensure that necessary cleanup or finalization code is executed when exiting a scope, improving code reliability and maintainability. 57 | -------------------------------------------------------------------------------- /docs/en/invoke/readme.md: -------------------------------------------------------------------------------- 1 | # Invoke Syntax 2 | 3 | | syntax | description | 4 | |----------------------|----------------------------------------------------------------| 5 | | `a` | get the variable `a` | 6 | | `a::Value` | get associated constant of `a` | 7 | | `a(b)` | function `a` applied to `b` | 8 | | `a?(b)` | apply if a is not null, else null | 9 | | `a.b()` | call method `b` of `a` | 10 | | `a.b` | same as `a.b()` | 11 | | `a?.b` | call `b` if `a` is not null, else null | 12 | | `a!` | unwrap if `a` is not null, else raise error | 13 | | `a!!` | unwrap `a` unchecked | 14 | | `a[index]` | get the item at index `b` of `a` | 15 | | `a?[index]` | get the item at index `b` of `a` if `a` is not null, else null | 16 | | `a[index] = c` | set the item at index `b` of `a` to `c` | 17 | | `a[start:end]` | get the slice of `a` from `start` to `end` | 18 | | `a[start:]` | get the slice of `a` from `start` to the end | 19 | | `a[:end]` | get the slice of `a` from the start to `end` | 20 | | `a[start:end:step]` | get the slice of `a` from `start` to `end` with `step` | 21 | | `a {}` | call lambda function, same a `a({})` | 22 | | `a? {}` | call lambda function if `a` is not null, else null | 23 | | `a { lambda (b): T}` | call lambda function complete form | 24 | | `a.{ $x + 1 }` | called by lambda function, same as `{ lambda(x) x + 1}(a)` | 25 | | `a?.{ $x + 1 }` | called by lambda function if `a` is not null, else null | 26 | | `a::` | Fill generic type of `a` | 27 | | `a?::` | ❌ No such syntax | 28 | -------------------------------------------------------------------------------- /docs/en/control/lazy-evaluate.md: -------------------------------------------------------------------------------- 1 | ## Lazy Evaluation 2 | 3 | Lazy evaluation is a programming technique where the evaluation of an expression or computation is delayed until its 4 | result is actually needed. 5 | 6 | In Valkyrie, lazy evaluation can be achieved using the `lazy` keyword. 7 | 8 | Here are some examples of lazy evaluation in Valkyrie: 9 | 10 | ```valkyrie 11 | // by `let` modifiers 12 | let lazy x = evaluate(); 13 | // by `lazy` block 14 | let lazy x = lazy { 15 | evaluate() 16 | }; 17 | ``` 18 | 19 | In the above code, the `lazy` keyword is used to create a lazy expression. 20 | 21 | The expression is only evaluated when its value is required. 22 | 23 | This can be useful in scenarios where the evaluation of an expression is time-consuming and should be 24 | deferred until necessary. 25 | 26 | ```valkyrie 27 | let mut a = 1; 28 | let b = lazy { 29 | // Some time-consuming operations 30 | a + 1 31 | }; 32 | @assert(b == 2, "lazy evaluated"); 33 | ``` 34 | 35 | In this example, the value of `b` is lazily evaluated as `a + 1`. 36 | 37 | The actual evaluation is deferred until the value of `b` is accessed. 38 | 39 | This allows for potential performance optimizations by avoiding unnecessary computations. 40 | 41 | ```valkyrie 42 | let mut a = 1; 43 | let lazy b = a + 1; 44 | a += 1; 45 | @assert(b == 3, "lazy evaluated"); 46 | ``` 47 | 48 | Here, the lazy expression `a + 1` is assigned to `b`. 49 | 50 | Before accessing the value of `b`, the value of `a` is incremented. 51 | 52 | When the value of `b` is finally accessed, the lazy expression is evaluated, taking into account the updated value of `a`. 53 | 54 | ## Lazy Trigger 55 | 56 | Lazy triggering is another feature provided by Valkyrie for controlling the evaluation of lazy expressions. 57 | 58 | It allows you to explicitly trigger the evaluation of a lazy expression using the `lazy` keyword followed by parentheses containing any necessary dependencies. 59 | 60 | ```valkyrie 61 | let mut dirty = true; 62 | let b = lazy(dirty) { 63 | a += 1; 64 | a 65 | } 66 | ``` 67 | 68 | In the above code, the lazy expression `a += 1` is wrapped in a `lazy` block with the dependency `dirty`. 69 | 70 | This means that the expression will be evaluated whenever `dirty` changes its value. 71 | 72 | Lazy triggering can be useful in scenarios where you want to control when a lazy expression is evaluated based on specific conditions or changes in the program state. 73 | 74 | -------------------------------------------------------------------------------- /docs/cn/Readme.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | actionText: 开始阅读 4 | actionLink: /cn/basic/ 5 | footer: CC0 Licensed | Copyright © 2012-2019 Aster™ 6 | --- 7 | 8 | ## 介绍 9 | 10 | Valkyrie 是一种实验性的多阶段编程语言, 结合了函数式编程和面向对象编程的特性,并探索了新的基于代数效应的范式。 11 | 12 | ## 特性 13 | 14 | - **代数数据类型** 15 | - 空安全,基于ADT模型的语言严格遵循空安全规则 16 | - **代数子类型** 17 | - 受限的传统面向对象范式 18 | - **代数效应(计划中)** 19 | - **渐进式结构类型** 20 | - 局域类型推导 21 | - **AST 宏** 22 | - 包隔离级别的多阶段编程 23 | 24 | ## 工具和社区 25 | 26 | - [Jupyter Runner](https://github.com/nyar-vm/valkyrie-jupyter/blob/dev/projects/playground/main.ipynb):交互式执行环境 27 | - [Idea Intellij](https://plugins.jetbrains.com/plugin/19452-valkyrie-language):IDEA 支持 28 | - [AST Visitor](https://docs.rs/valkyrie-ast/0.1.7/valkyrie_ast/):AST 定义和打印 29 | 30 | ## 语言参考 31 | 32 | ### 基本概念 33 | 34 | - [字面量](./literal/readme.md): 布尔值, 整数值以及字符串 35 | - [常用数据结构](./collections/readme.md): 列表, 字典以及集合 36 | - [函数调用](./invoke/readme.md): 37 | - [下标](./invoke/readme.md): 38 | - [变量定义](./definition/let-binding.md): 39 | - [函数定义](./definition/def-micro.md): 40 | - [模块系统](./definition/module.md) 41 | - [包管理系统](./definition/package.md): 42 | 43 | ### 习数据控制流 44 | 45 | - [If](./control/jump-if.md): 条件语句 46 | - [Switch](./control/jump-switch.md): 跳转表 47 | - [While Loop](./control/loop-while.md): 条件循环 48 | - [For Loop](./control/loop-for.md): 迭代器 49 | - [Return, Break and Continue](./control/jump-control.md): 控制流 50 | - [Try, Catch and Resume](./control/jump-control.md): 控制流 51 | 52 | ### 自定义代数结构 53 | 54 | - [`class`](./definition/structure.md): 基于引用的积类型 55 | - [`structure`](./definition/structure.md): 基于值的积类型 56 | - [`interface`](./definition/interface.md): 接口与子类型多态 57 | - [`trait`](./definition/interface.md): 特质系统 58 | - [`enumerate`](./definition/enumerate.md): 相容编码 59 | - [`flags`](./definition/flags.md): 互斥编码 60 | - [`unite`](./definition/union.md): 不交并, 和类型 61 | 62 | 63 | - [Extractor](./advance/extractor.md): 提取器模式 64 | - [Constructor and Builder](./advance/builder.md): 构造器模式 65 | - [Pattern Match](./advance/pattern-match.md): 模式匹配 66 | 67 | 68 | 69 | - Define Effect 70 | - `Io` effect and `Logging` effect 71 | - Generics, covariance, contravariance and invariance 72 | - Type conversion, type reinterpretation and type transmutation 73 | - Iterators and Generators 74 | - Closures, callbacks and asynchronous programming 75 | - Raw pointers and unsafe programming 76 | - Value types and the stack allocation 77 | - Macro and package isolation compile 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /docs/en/definition/interface.md: -------------------------------------------------------------------------------- 1 | ## Interface 2 | 3 | An interface describes a set of methods that a structure needs to implement. 4 | 5 | Interface is roughly equivalent to virtual class, but the difference is that it does not participate in mro sorting, and 6 | its call always points to the end of mro. 7 | 8 | ```valkyrie 9 | virtual class A { 10 | who(self) { print("A") } 11 | } 12 | interface IA { 13 | who(self) { print("A") } 14 | } 15 | class B(A) { 16 | who(self) { print("B") } 17 | } 18 | class IB { } 19 | extends IB: IA { 20 | who(self) { print("B") } 21 | } 22 | 23 | let b1 = new B(); # MRO = [B, A] 24 | let b2 = new IB(); # MRO = [IB] 25 | 26 | A::who(b1); # A 27 | B::who(b1); # B 28 | IA::who(b2); # B 29 | IB::who(b2); # B 30 | ``` 31 | 32 | Valkyrie language does not recommend the use of virtual base classes, in fact most of these requirements can be met by 33 | interface and trait. 34 | 35 | The second difference is that there is no inheritance of interfaces, there is no interface `A` inheriting 36 | interface `B`(`A(B)`), only interface `A` satisfies interface `B`(`A:B`) 37 | 38 | ```valkyrie 39 | interface DividedBy4: DividedBy2 { 40 | divided_by_4(self) -> bool; 41 | } 42 | interface DividedBy2 { 43 | divided_by_2(self) -> bool; 44 | } 45 | 46 | extends int: DividedBy4 { 47 | divided_by_4(self) -> bool { self % 4 == 0; } 48 | divided_by_2(self) -> bool { self % 2 == 0;} 49 | } 50 | 51 | 6.divided_by_2(); # true 52 | 6.divided_by_4(); # false 53 | DividedBy2::divided_by_2(6); # true 54 | DividedBy4::divided_by_4(6); # false 55 | DividedBy4::divided_by_2(6); # ERROR: `DividedBy4::divided_by_2` does not exist. 56 | ``` 57 | 58 | This makes interface more like type class. 59 | 60 | ## Trait 61 | 62 | Certain interfaces require all fields to participate, and obviously for each subtype, the interface must be overridden. 63 | 64 | Such an interface needs to be declared using the trait. 65 | 66 | For example, [Show::show]() needs to be re-implemented by each subclass, and it must not inherit the display method of 67 | the parent class by default. 68 | 69 | And the base class also has this method, and it is not a virtual method, so it cannot force the subclass to rewrite it. 70 | 71 | That's why we need the trait. 72 | 73 | The declaration syntax of trait and interface is the same 74 | 75 | ```valkyrie 76 | trait Show { 77 | show(self, f: Formatter) -> Unit; 78 | } 79 | ``` 80 | 81 | A trait can be associated with a derive macro. 82 | -------------------------------------------------------------------------------- /docs/en/definition/typing.md: -------------------------------------------------------------------------------- 1 | ## Type System 2 | 3 | The type system used by the Valkyrie language is the Extensible Row Type System 4 | 5 | All types will be compiled to things like: `[field: P -> Q -> R, ..]`. 6 | 7 | Because most records are open, we omit `..`. 8 | 9 | If you want to express a closed record, you can write `[ field1: T, !! ]` 10 | 11 | The difference between the row type and the general structure type is that no additional meta information will be lost 12 | 13 | ## Declarations 14 | 15 | Every define keyword in the Valkyrie language is syntactic sugar for `TypeRecord`. 16 | 17 | These declarative keywords are mainly for some engineering constraints. 18 | 19 | - [structure + extends](./structure.md): Separate `Record`'s data and implementation. 20 | - [class](./structure.md#class): Automatically implement some interfaces and traits. 21 | - [interfaces](./interface.md): Virtual base class that does not participate in mro ordering. 22 | - [trait](./interface.md#trait): The interface that every subclass is forced to reimplement. 23 | - [enumerate](./enumerate.md): A number which various methods are appended. 24 | - [flags](./flags.md): A number used to label different dualistic behaviors. 25 | - [unite](./disjoint-union.md): Disjoint union of types, closed subtype. 26 | 27 | In fact, these keywords can be absent. 28 | 29 | You can create anonymous Object directly using `type`. 30 | 31 | ```valkyrie 32 | type A = [ 33 | call: (Int, ) -> String 34 | ] 35 | let a = new A [ 36 | call: { $x.to_string() } 37 | ] 38 | A::call(10) # "10" 39 | ``` 40 | 41 | The subtype will only determine whether all the fields in `B` exist in `A`. 42 | 43 | It does not care where the `B` type comes from, whether B is an original type, an inherited subtype, or the result of a type expression 44 | 45 | ## Shadow Row Type 46 | 47 | Another feature is that Record in the valkyrie language supports repeated row. 48 | 49 | ```valkyrie 50 | type A = [ 51 | field: Integer, 52 | field: String, 53 | ] 54 | 55 | A::field # Integer 56 | ``` 57 | 58 | Querying the field directly will return the first matching row type. 59 | 60 | In fact Valkyrie will track the source of each row. 61 | 62 | ```valkyrie 63 | type A = [ 64 | field: Integer, 65 | ] 66 | type B = [ 67 | field: String, 68 | ] 69 | type C = A & B { 70 | A::field: Integer, 71 | B::field: String, 72 | } 73 | 74 | C::field # Integer 75 | ::field # Integer 76 | ::field # String 77 | ``` 78 | 79 | This ability is very useful, 80 | because the effect system will throw out many effects with the same type signatures, 81 | and we need to accurately identify which layer call each Effect is passed from. 82 | 83 | 84 | ## Type Inference (unimplemented) 85 | 86 | Reference: [Gradual Typing for Extensibility by Rows](https://arxiv.org/pdf/1910.08480.pdf) 87 | -------------------------------------------------------------------------------- /docs/en/definition/flags.md: -------------------------------------------------------------------------------- 1 | 2 | ```valkyrie 3 | # Takes 16 bits of space, but only uses the last 4 bits 4 | flags FontStyle(16, 4): Show { 5 | # Nothing will be generated even if it is not written, you can rename `0` to any other name 6 | Nothing = 0, 7 | Bold = 2 ^ 0, 8 | Italic = 2 ^ 1, 9 | Underline = 2 ^ 2, 10 | Red = 2 ^ 3, 11 | RedBold = 2 ^ 3 + 2 ^ 0, 12 | RedItalic = Red | Italic, 13 | # Everything will be generated according to the upper limit even if it is not written 14 | Everything = 2 ^ 4 - 1, 15 | } 16 | ``` 17 | 18 | Valkyrie has no bit level operator, so you can only use `2 ^ 0` instead of `1 << 0`, use `2 ^ 3 + 2 ^ 0` instead of `1 << 3 | 1 << 0`. 19 | 20 | The numbers or operations on the right are compile time big integers, instead of dynamic values. 21 | 22 | `Flags` cannot extend a value or function that takes no arguments and returns its own type, as this would lead to confusion 23 | 24 | All associated constant values must be written in the `flags` declaration, and does not support duplicate values. 25 | 26 | Global variables are not subject to this. 27 | 28 | ## Operators 29 | 30 | - `a | b`: bit set or, returns a new `Flags` with the bits of both `Flags` set to 1. 31 | - `a & b`: bit set and, returns a new `Flags` with the bits of both `Flags` set to 1. 32 | - `a + b`: same as `a | b` 33 | - `a - b`: bit set difference, returns a new `Flags` with the bits of the left `Flags` set to 1 and the bits of the right `Flags` set to 0. 34 | - `-a`: bit set negation, invert 0 and 1 bits according to `Flags::Everything` 35 | - `!a`: same as `-a` 36 | - `Flags::Name`: create a new `Flags` with bits according to the definition. 37 | 38 | ## Type Conversion 39 | 40 | ### As Integer 41 | 42 | `Flags` and `Integer` cannot be replaced arbitrarily, you need to use `as?` to convert. In Valkyrie, `as?` returns `Result`. 43 | 44 | Converting between `Integer` and `Flags` is not free, the layout of Integer is `[u32]`, and cannot be directly truncated to `[u8]`. 45 | 46 | Also, you can pass `Integer` to any function parameter that accepts `Flags`. 47 | 48 | ```valkyrie 49 | micro render(text: String, style: FontStyle): String { 50 | if style == FontStyle::RedBold { 51 | return '{text}' 52 | } 53 | if style.has(FontStyle::Red) { 54 | return '{text}' 55 | } 56 | if style.has(FontStyle::Bold) { 57 | return '{text}' 58 | } 59 | } 60 | render('Hello, World!', 9) #ok 61 | ``` 62 | 63 | But if the `Integer` is greater than the maximum value of `Flags`, the large integer will be truncated according to the `Layout` of `Flags`, **and no error will be raised**. 64 | 65 | ## As String 66 | 67 | ```valkyrie 68 | imply FontStyle: Show { 69 | show(self) 70 | } 71 | ``` 72 | 73 | 74 | -------------------------------------------------------------------------------- /docs/en/invoke/curry.md: -------------------------------------------------------------------------------- 1 | ## Curry Function 2 | 3 | A curry function cannot be an overloaded function at the same time. 4 | 5 | ```valkyrie 6 | curry micro add(a: int, b: int): int { 7 | a + b 8 | } 9 | ``` 10 | 11 | Without any mark, `a, b` represent mixed parameters, so this definition is equivalent to. 12 | 13 | ```valkyrie 14 | curry micro add(<, a: int, b: int, >): int { 15 | a + b 16 | } 17 | ``` 18 | 19 | - If no name key is provided, the positional parameters will be filled first. 20 | - And then the mixed parameters will be filled. 21 | - If it is redundant, an error will be reported 22 | - If the name key is provided, the corresponding parameters will be filled first, 23 | - and cannot be filled repeatedly 24 | 25 | ```valkyrie 26 | add(1)(2) # 3, filled a = 1, b = 2 27 | add(a: 1)(2) # 3, filled a = 1, b = 2 28 | add(b: 1)(2) # 3, filled a = 2, b = 1 29 | add(b: 1, a: 2) # 3, filled a = 2, b = 1 30 | add(0)(1, 2) # error, except 2 parameters, but 3 parameters are provided 31 | add(0)(a: 1) # error, mixed parameters `a` had been filled 32 | ``` 33 | 34 | ## Defer function 35 | 36 | If you need to overwrite the named key multiple times, you can use the defer function 37 | 38 | ```valkyrie 39 | defer micro add(<, a: int, b: int, >): int { 40 | a + b 41 | } 42 | ``` 43 | 44 | 45 | 46 | ```valkyrie 47 | add(1)(2) # 3, filled a = 1, b = 2 48 | add(a: 1)(2) # 3, filled a = 1, b = 2 49 | add(b: 1)(2) # 3, filled a = 2, b = 1 50 | add(b: 1, a: 2) # 3, filled a = 2, b = 1 51 | add(0)(1, 2) # error, except 2 parameters, but 3 parameters are provided 52 | add(0)(a: 2)(1) # 3, filled a = 2, b = 1 53 | ``` 54 | 55 | 56 | ## Curry with `**args` 57 | 58 | If a curry function adds `**args`, it will never fire with unnamed arguments. 59 | 60 | ```valkyrie 61 | curry micro count(<, **args: Array, >, invoke: bool): int { 62 | if invoke { 63 | args.length 64 | } 65 | else { 66 | 0 67 | } 68 | } 69 | ``` 70 | 71 | ```valkyrie 72 | count()(0)(1, 2)(invoke: false) # 0 73 | count()(0)(1, 2)(3, 4, 5)(invoke: true) # 6 74 | ``` 75 | 76 | 77 | 78 | ## Curry with `partial` 79 | 80 | `std::functional::partial` can be used to fill in the parameters of any function. 81 | 82 | No need to mark `curry` or `defer` in advance. 83 | 84 | ```valkyrie 85 | micro add(a: int, b: int): int { 86 | a + b 87 | } 88 | 89 | let add1: CurryFunction = std::functional::partial(add, [b: 1], override: true) 90 | ``` 91 | 92 | 93 | 94 | ## Curry with Continuation 95 | 96 | If the last parameter is Action type, then this parameter function must be filled before calling. 97 | 98 | ```valkyrie 99 | micro add(a: int, b: int, k: Action): int { 100 | cont(a + b) 101 | } 102 | ``` 103 | 104 | 105 | ```valkyrie 106 | micro add(<, a: int, b: int, >, k: Action): int { 107 | cont(a + b) 108 | } 109 | ``` 110 | 111 | 112 | ```valkyrie 113 | add(1)(2) { 114 | print($it) 115 | } 116 | 117 | add(1)(2, k: { print($it)}) 118 | ``` 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /docs/en/control/jump-control.md: -------------------------------------------------------------------------------- 1 | # Non-local Return 2 | 3 | | control | description | 4 | |---------------|---------------------------------------------| 5 | | `break` | exit the loop | 6 | | `continue` | skip the current iteration | 7 | | `fallthrough` | execute the next case in a switch statement | 8 | | `return` | exit the function | 9 | | `raise` | raise an effect | 10 | | `yield` | yield a value | 11 | | `yield from` | yield a value from an iterator | 12 | | `yield break` | exit the iterator | 13 | 14 | ## Implementation Details 15 | 16 | ```valkyrie 17 | micro check235(items: [int]): bool { 18 | for item in items { 19 | if item % 2 == 0 { 20 | break 21 | } 22 | if item % 3 == 0 { 23 | continue 24 | } 25 | if item % 5 == 0 { 26 | return true 27 | } 28 | print("next") 29 | } 30 | print("no such item") 31 | false 32 | } 33 | ``` 34 | 35 | The first stage will program all control jumps to goto 36 | 37 | ```valkyrie 38 | micro check235(items: [int]): bool { 39 | let mut next = items.next() 40 | let mut ret = false; 41 | loop { 42 | @1 43 | let i = next.unwrap() 44 | @label 2 45 | if item % 2 == 0 { 46 | @goto 4 47 | } 48 | if item % 3 == 0 { 49 | @goto 1 50 | } 51 | if item % 5 == 0 { 52 | ret = true 53 | @goto 5 54 | } 55 | @label 3 56 | print("next") 57 | next = items.next() 58 | @goto 2 59 | } 60 | @2 61 | print("no such item") 62 | @label 5 63 | # drops here 64 | ret 65 | } 66 | ``` 67 | 68 | The second stage will execute the relooper, compiling goto into a state machine 69 | 70 | ```valkyrie 71 | micro check235(items: [int]): bool { 72 | let mut state = 1; 73 | let mut next = items.next() 74 | let mut ret = false; 75 | while true { 76 | switch { 77 | case 1: 78 | if next.is_some() { 79 | state = 2 80 | } 81 | else { 82 | break 83 | } 84 | case 2: 85 | let i = next.unwrap() 86 | state = 3 87 | case 3: 88 | if item % 2 == 0 { 89 | state = 7 90 | } 91 | else { 92 | state = 2 93 | } 94 | case 4: 95 | if item % 3 == 0 { 96 | state = 2 97 | } 98 | else { 99 | state = 5 100 | } 101 | case 5: 102 | if item % 5 == 0 { 103 | ret = true 104 | break 105 | } 106 | else { 107 | state = 6 108 | } 109 | case 6: 110 | print("next") 111 | next = items.next() 112 | state = 1 113 | case 7: 114 | print("no such item") 115 | break 116 | } 117 | } 118 | ret 119 | } 120 | ``` 121 | 122 | 123 | -------------------------------------------------------------------------------- /docs/cn/pattern/extractor.md: -------------------------------------------------------------------------------- 1 | # Extractor 2 | 3 | 提取器模式代表类被写在 `let` 或者 `case` 之后的行为. 4 | 5 | 6 | ```valkyrie 7 | trait Extractor { 8 | type Input; 9 | type Output: TupleType; 10 | extractor(object: Self::Input): Self::Output? 11 | } 12 | ``` 13 | 14 | 输入值为任意类型, 但是返回值需要是 tuple 15 | 16 | --- 17 | 18 | ## 常规提取器 19 | 假如我们想实现以下功能: 20 | 21 | ```valkyrie 22 | # a = ['A', 'B', 'C'] 23 | let MyName(a) := 'A.B.C' 24 | ``` 25 | 26 | 那么可以定义如下提取器: 27 | 28 | ```valkyrie 29 | class MyName {} 30 | 31 | extends MyName: Extractor { 32 | @never_null 33 | extract(string: StringView): Extractor::Output? { 34 | return string.split('.') 35 | } 36 | } 37 | ``` 38 | 39 | 由于 Extractor 已被注册为 `auto_trait`, 所以也可以直接定义 extract 函数 40 | 41 | 上面的代码可以简写为: 42 | 43 | ```valkyrie 44 | class MyName { 45 | extract(string: StringView): String { 46 | return string.split('.') 47 | } 48 | } 49 | ``` 50 | 51 | 展开过程等价于如下代码 52 | 53 | ```valkyrie 54 | let MyName(a) := 'A.B.C' 55 | 56 | # _tmp = (['A', 'B', 'C'],) 57 | let _tmp = MyName::extract('A::B::C')! 58 | # a = ['A', 'B', 'C'] 59 | let a: [String] = _tmp.1 60 | ``` 61 | 62 | 如果不加 `@never_null`, let 下的返回值会是可空值, case 下没有返回值. 63 | 64 | ```valkyrie 65 | class MyName { 66 | extract(one: String): (String, String)? { 67 | let [one, two, **rest] = string.split('::'); 68 | if rest.is_empty() { 69 | return (one, two) 70 | } 71 | else { 72 | return null 73 | } 74 | } 75 | } 76 | ``` 77 | 78 | 注意结果可能为空, 此时展开过程为: 79 | 80 | ```valkyrie 81 | let MyName(a, b) := 'A::B' 82 | let _tmp: (String, String)? = MyName::extract('A::B'); 83 | let a: String? = _tmp.map { $1.1 } 84 | let b: String? = _tmp.map { $1.2 } 85 | ``` 86 | 87 | --- 88 | 89 | ## 不交并的提取器 90 | 91 | 92 | ```valkyrie 93 | # 定义不交并 94 | union Result { 95 | Success { 96 | value: E 97 | } 98 | Failure { 99 | error: T 100 | } 101 | } 102 | 103 | # 定义成功情况的提取器 104 | extends Result::Success: Extractor, Output=(T,)> { 105 | extractor(result: Result) -> (T,)? { 106 | result.match { 107 | case Result::Success { value }: 108 | value 109 | else: 110 | null 111 | } 112 | } 113 | } 114 | 115 | # 定义失败情况的提取器 116 | extends Result::Failure: Extractor, Output=(E,)> { 117 | extractor(result: Result) -> (E,)? { 118 | result.match { 119 | case Result::Success { value: _ }: 120 | null 121 | case Result::Failure { error }: 122 | error 123 | } 124 | } 125 | } 126 | 127 | # 于是可以写出如下调用 128 | result.match { 129 | case Success(v): print("success: {v}") 130 | case Failure(e): print("failure: {e}") 131 | } 132 | ``` 133 | 134 | 展开过程等价于如下代码: 135 | 136 | ```valkyrie 137 | let v = Result::Success::extract(result); 138 | if v != null { 139 | return print("success: {v}") 140 | } 141 | let e = Result::Failure::extract(result); 142 | if e != null { 143 | return print("failure: {e}") 144 | } 145 | ``` 146 | 147 | 该实现较为繁琐, 可以使用宏来简化 148 | 149 | ```valkyrie 150 | union Result { 151 | @construct(value) 152 | @extract(value) 153 | Success { 154 | value: E 155 | } 156 | @construct(error) 157 | @extract(error) 158 | Failure { 159 | error: T 160 | } 161 | } 162 | ``` 163 | 164 | -------------------------------------------------------------------------------- /docs/cn/advance/let-statement.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ## 重新认识 let 绑定 5 | 6 | let binding 7 | 8 | 9 | ## 可变量与不变量 10 | 11 | 变量默认是不可改变的 (immutable) . 这是推动你以充分利用 Rust 提供的安全性和简单并发性来编写代码的众多方式之一. 不过, 你仍然可以使用可变变量. 让我们探讨一下 Rust 拥抱不可变性的原因及方法, 以及何时你不想使用不可变性. 12 | 13 | 当变量不可变时, 一旦值被绑定一个名称上, 你就不能改变这个值. 14 | 15 | 16 | ```valkyrie 17 | fn main() { 18 | let x = 5; 19 | println!("The value of x is: {}", x); 20 | x = 6; 21 | println!("The value of x is: {}", x); 22 | } 23 | ``` 24 | 25 | 26 | 这个例子展示了编译器如何帮助你找出程序中的错误. 虽然编译错误令人沮丧, 但那只是表示程序不能安全的完成你想让它完成的工作;并 **不能** 说明你不是一个好程序员! 经验丰富的 Rustacean 们一样会遇到编译错误. 27 | 28 | 错误信息指出错误的原因是 `不能对不可变变量 x 二次赋值` (`cannot assign twice to immutable variable x`) , 因为你尝试对不可变变量 `x` 赋第二个值. 29 | 30 | 在尝试改变预设为不可变的值时, 产生编译时错误是很重要的, 因为这种情况可能导致 bug. 如果一部分代码假设一个值永远也不会改变, 而另一部分代码改变了这个值, 第一部分代码就有可能以不可预料的方式运行. 不得不承认这种 bug 的起因难以跟踪, 尤其是第二部分代码只是 **有时** 会改变值. 31 | 32 | Rust 编译器保证, 如果声明一个值不会变, 它就真的不会变. 这意味着当阅读和编写代码时, 不需要追踪一个值如何和在哪可能会被改变, 从而使得代码易于推导. 33 | 34 | 不过可变性也是非常有用的. 变量只是默认不可变;正如在第二章所做的那样, 你可以在变量名之前加 `mut` 来使其可变. 除了允许改变值之外, `mut` 向读者表明了其他代码将会改变这个变量值的意图. 35 | 36 | 37 | ```valkyrie 38 | fn main() { 39 | let mut x = 5; 40 | println!("The value of x is: {}", x); 41 | x = 6; 42 | println!("The value of x is: {}", x); 43 | } 44 | ``` 45 | 46 | 通过 `mut`, 允许把绑定到 `x` 的值从 `5` 改成 `6`. 在一些情况下, 你会想用可变变量, 因为与只用不可变变量相比, 它会让代码更容易编写. 47 | 48 | 除了防止出现 bug 外, 还有很多地方需要权衡取舍. 例如, 使用大型数据结构时, 适当地使用可变变量, 可能比复制和返回新分配的实例更快. 对于较小的数据结构, 总是创建新实例, 采用更偏向函数式的编程风格, 可能会使代码更易理解, 为可读性而牺牲性能或许是值得的. 49 | 50 | ## Constant 51 | 52 | 不允许改变值的变量, 可能会使你想起另一个大部分编程语言都有的概念: **常量** (*constants*) . 类似于不可变变量, 常量是绑定到一个名称的不允许改变的值, 不过常量与变量还是有一些区别. 53 | 54 | 首先, 不允许对常量使用 `mut`. 常量不光默认不能变, 它总是不能变. 55 | 56 | 声明常量使用 `const` 关键字而不是 `let`, 并且 **必须** 注明值的类型. 在下一部分, ["数据类型"][data-types] 中会介绍类型和类型注解, 现在无需关心这些细节, 记住总是标注类型即可. 57 | 58 | 常量可以在任何作用域中声明, 包括全局作用域, 这在一个值需要被很多部分的代码用到时很有用. 59 | 60 | 最后一个区别是, 常量只能被设置为常量表达式, 而不能是函数调用的结果, 或任何其他只能在运行时计算出的值. 61 | 62 | 这是一个声明常量的例子, 它的名称是 `MAX_POINTS`, 值是 100,000. (Rust 常量的命名规范是使用下划线分隔的大写字母单词, 并且可以在数字字面值中插入下划线来提升可读性) : 63 | 64 | ```valkyrie 65 | const MAX_POINTS: u32 = 100_000; 66 | ``` 67 | 68 | 在声明它的作用域之中, 常量在整个程序生命周期中都有效, 这使得常量可以作为多处代码使用的全局范围的值, 例如一个游戏中所有玩家可以获取的最高分或者光速. 69 | 70 | 将遍布于应用程序中的硬编码值声明为常量, 能帮助后来的代码维护人员了解值的意图. 如果将来需要修改硬编码值, 也只需修改汇聚于一处的硬编码值. 71 | 72 | ## Shadowing 73 | 74 | 我们可以定义一个与之前变量同名的新变量, 而新变量会 **隐藏** 之前的变量. 75 | 76 | Rustacean 们称之为第一个变量被第二个 **隐藏** 了, 这意味着使用这个变量时会看到第二个值. 可以用相同变量名称来隐藏一个变量, 以及重复使用 `let` 关键字来多次隐藏, 如下所示: 77 | 78 | ```valkyrie 79 | fn main() { 80 | let x = 5; 81 | 82 | let x = x + 1; 83 | 84 | let x = x * 2; 85 | 86 | println!("The value of x is: {}", x); 87 | } 88 | ``` 89 | 90 | 这个程序首先将 `x` 绑定到值 `5` 上. 接着通过 `let x =` 隐藏 `x`, 获取初始值并加 `1`, 这样 `x` 的值就变成 `6` 了. 第三个 `let` 语句也隐藏了 `x`, 将之前的值乘以 `2`, `x` 最终的值是 `12`. 运行这个程序, 它会有如下输出: 91 | 92 | 93 | 94 | 隐藏与将变量标记为 `mut` 是有区别的. 当不小心尝试对变量重新赋值时, 如果没有使用 `let` 关键字, 就会导致编译时错误. 通过使用 `let`, 我们可以用这个值进行一些计算, 不过计算完之后变量仍然是不变的. 95 | 96 | `mut` 与隐藏的另一个区别是, 当再次使用 `let` 时, 实际上创建了一个新变量, 我们可以改变值的类型, 但复用这个名字. 例如, 假设程序请求用户输入空格字符来说明希望在文本之间显示多少个空格, 然而我们真正需要的是将输入存储成数字 (多少个空格) : 97 | 98 | ```valkyrie 99 | let spaces = " "; 100 | let spaces = spaces.len(); 101 | ``` 102 | 103 | 这里允许第一个 `spaces` 变量是字符串类型, 而第二个 `spaces` 变量, 它是一个恰巧与第一个变量同名的崭新变量, 是数字类型. 隐藏使我们不必使用不同的名字, 如 `spaces_str` 和 `spaces_num`;相反, 我们可以复用 `spaces` 这个更简单的名字. 然而, 如果尝试使用 `mut`, 将会得到一个编译时错误, 如下所示: 104 | 105 | ```valkyrie 106 | let mut spaces = " "; 107 | spaces = spaces.len(); 108 | ``` 109 | 110 | 这个错误说明, 我们不能改变变量的类型: 111 | 112 | ```text 113 | error[E0308]: mismatched types 114 | --> src/main.rs:3:14 115 | | 116 | 3 | spaces = spaces.len(); 117 | | ^^^^^^^^^^^^ expected &str, found usize 118 | | 119 | = note: expected type `&str` 120 | found type `usize` 121 | ``` 122 | -------------------------------------------------------------------------------- /docs/en/definition/readme.md: -------------------------------------------------------------------------------- 1 | The Valkyrie language has the following keywords as definitions 2 | 3 | - [type](./typing.md): Define a new type alias 4 | - [let](./let-binding.md): Define a local variable 5 | - [micro](./def-micro.md): Define a process of precomputing parameters 6 | - [macro](./def-macro.md): Define a process of lazy computing parameters 7 | - [interface](./interface.md): The conditions that the structure needs to obey 8 | - [trait](./interface.md#trait): Interface which the subtype needs to reimplement 9 | - [structure](./structure.md): The original structure of the stored data 10 | - [class](./structure.md#class): Structure with automatic derive traits 11 | - [enumerate](./enumerate.md): A number used to label different behaviors 12 | - [flags](./flags.md): A number used to label different dualistic behaviors 13 | - [union](./union.md): A piece of data that can be interpreted in multiple ways 14 | - [unite](./disjoint-union.md): Disjoint union of types 15 | 16 | It looks like a lot, but in fact it can be simply divided into two types: value declaration and type declaration. 17 | 18 | ## Term System 19 | 20 | 21 | 22 | ## Type System 23 | 24 | The type system used by the Valkyrie language is the Extensible Row Type System 25 | 26 | All types will be compiled to things like: `[field: P -> Q -> R, ..]`. 27 | 28 | Because most records are open, we omit `..`. 29 | 30 | If you want to express a closed record, you can write `[ field1: T, !! ]` 31 | 32 | The difference between the row type and the general structure type is that no additional meta information will be lost 33 | 34 | ## Declarations 35 | 36 | Every define keyword in the Valkyrie language is syntactic sugar for `TypeRecord`. 37 | 38 | These declarative keywords are mainly for some engineering constraints. 39 | 40 | - [structure + extends](./structure.md): Separate `Record`'s data and implementation. 41 | - [class](./structure.md#class): Automatically implement some interfaces and traits. 42 | - [interfaces](./interface.md): Virtual base class that does not participate in mro ordering. 43 | - [trait](./interface.md#trait): The interface that every subclass is forced to reimplement. 44 | - [enumerate](./enumerate.md): A number which various methods are appended. 45 | - [flags](./flags.md): A number used to label different dualistic behaviors. 46 | - [unite](./disjoint-union.md): Disjoint union of types, closed subtype. 47 | 48 | In fact, these keywords can be absent. 49 | 50 | You can create anonymous Object directly using `type`. 51 | 52 | ```valkyrie 53 | type A = [ 54 | call: (Int, ) -> String 55 | ] 56 | let a = new A [ 57 | call: { $x.to_string() } 58 | ] 59 | A::call(10) # "10" 60 | ``` 61 | 62 | The subtype will only determine whether all the fields in `B` exist in `A`. 63 | 64 | It does not care where the `B` type comes from, whether B is an original type, an inherited subtype, or the result of a type expression 65 | 66 | ## Shadow Row Type 67 | 68 | Another feature is that Record in the valkyrie language supports repeated row. 69 | 70 | ```valkyrie 71 | type A = [ 72 | field: Integer, 73 | field: String, 74 | ] 75 | 76 | A::field # Integer 77 | ``` 78 | 79 | Querying the field directly will return the first matching row type. 80 | 81 | In fact Valkyrie will track the source of each row. 82 | 83 | ```valkyrie 84 | type A = [ 85 | field: Integer, 86 | ] 87 | type B = [ 88 | field: String, 89 | ] 90 | type C = A & B { 91 | A::field: Integer, 92 | B::field: String, 93 | } 94 | 95 | C::field # Integer 96 | ::field # Integer 97 | ::field # String 98 | ``` 99 | 100 | This ability is very useful, 101 | because the effect system will throw out many effects with the same type signatures, 102 | and we need to accurately identify which layer call each Effect is passed from. 103 | 104 | 105 | ## Type Inference (unimplemented) 106 | 107 | Reference: [Gradual Typing for Extensibility by Rows](https://arxiv.org/pdf/1910.08480.pdf) 108 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Valkyrie Language 2 | 3 | [![discord](https://img.shields.io/discord/794446776232443955.svg?logo=discord&style=flat-square)](https://discord.gg/rDScD9GyUC) 4 | 5 | ## Introduce 6 | 7 | Valkyrie is a experimental multi-stage gradual typed language, hoping to combine the respective advantages of static 8 | typing and dynamic typing. 9 | 10 | Integrate functional programming and object-oriented programming paradigms, and explore new effect-based paradigms. 11 | 12 | The grammar mainly borrows from scala and swift, But many runtime concepts come from rust. 13 | 14 | In fact, you can think of it as scala implemented in rust. 15 | 16 | ## Features 17 | 18 | - **Algebraic Data Types** 19 | - Your favorite functional paradigm 20 | - Null-safe, languages under the ADT model are strictly null-safe 21 | - **Algebraic subtyping** 22 | - Restricted traditional object-oriented paradigm 23 | - **Algebraic Effects** (planned, semantically unclear) 24 | - **Gradual Structure Typing** 25 | - Intelligent automatic type inference (in progress) 26 | - **AST Macros** (in progress) 27 | - Multi-stage programming with package isolation (not yet implemented) 28 | - **Concurrent Garbage Collection** (in plan, use rc for now) 29 | - **Lossless Rust FFI** (in plan, waiting for crABI) 30 | 31 | ## Tools & Community 32 | 33 | - [Jupyter Runner](https://github.com/nyar-vm/valkyrie-jupyter/blob/dev/projects/playground/main.ipynb): interactive execution 34 | environment 35 | - [Idea Intellij](https://plugins.jetbrains.com/plugin/19452-valkyrie-language): IDEA support 36 | - [AST Visitor](https://docs.rs/valkyrie-ast/0.1.7/valkyrie_ast/): AST definition and pretty print 37 | 38 | 39 | - Advice on Github: [github.com/valkyrie-language](https://github.com/nyar-vm/valkyrie-language/discussions) 40 | - Chat in Discord: [![discord](https://img.shields.io/discord/794446776232443955.svg?logo=discord&style=flat-square)](https://discord.gg/rDScD9GyUC) 41 | 42 | 43 | ## Tutorial 44 | 45 | Learn basic syntax first 46 | 47 | - [Literals](./literal/readme.md): learn basic literals like bool, string and integer 48 | - [Collections](./collection/readme.md): learn common collections like list, map, set 49 | - [Calls and subscripts](./invoke/readme.md): learn how to call functions and get items 50 | - [Let Bind](./definition/let-binding.md): learn how to define variables 51 | - [Define Function](./definition/def-micro.md): learn how to define functions 52 | - [Module](./definition/module.md) & [Package](./definition/package.md): learn how to organize code 53 | 54 | Then learn how to combine FP and OOP 55 | 56 | - [Structure and Class](./definition/structure.md): learn polymorphism through subtyping 57 | - [Interface and Trait](./definition/interface.md): learn polymorphism through type class 58 | - [Enumerate](./definition/enumerate.md) & [Flags](./definition/flags.md): Learn to code in a more readable way 59 | - [Disjoint Union and Variants](./definition/disjoint-union.md): learn polymorphism through ADT 60 | - Macro: learn meta programming through AST transformation 61 | 62 | Then learn funky control flow 63 | 64 | - [If](./control/jump-if.md) & [Switch](./control/jump-switch.md): learn how to jump by condition 65 | - [While Loop](./control/loop-while.md) & [For Loop](./control/loop-for.md): learn how to loop with condition and 66 | iterator 67 | - [Return, Break and Continue](./control/jump-control.md): learn how to return non-locally 68 | - Try, Catch and Resume: learn how to return non-locally 69 | 70 | Finally, a never-ending journey of advancement 71 | 72 | - [Pattern Match](./advance/pattern-match.md) & [Extractor](./advance/extractor.md) 73 | - [Constructor and Builder](./advance/builder.md) 74 | - Define Effect 75 | - `Io` effect and `Logging` effect 76 | - Generics, covariance, contravariance and invariance 77 | - Type conversion, type reinterpretation and type transmutation 78 | - Iterators and Generators 79 | - Closures, callbacks and asynchronous programming 80 | - Raw pointers and unsafe programming 81 | - Value types and the stack allocation 82 | - Macro and package isolation compile 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /docs/en/readme.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | actionText: Start to read 4 | actionLink: /en/literal/readme.md 5 | footer: CC0 Licensed | Copyright © 2012-2023 Aster™ 6 | --- 7 | 8 | ## Introduce 9 | 10 | Valkyrie is a experimental multi-stage gradual typed language, hoping to combine the respective advantages of static 11 | typing and dynamic typing. 12 | 13 | Integrate functional programming and object-oriented programming paradigms, and explore new effect-based paradigms. 14 | 15 | The grammar mainly borrows from scala and swift, But many runtime concepts come from rust. 16 | 17 | In fact, you can think of it as scala implemented in rust. 18 | 19 | ## Features 20 | 21 | - **Algebraic Data Types** 22 | - Your favorite functional paradigm 23 | - Null-safe, languages under the ADT model are strictly null-safe 24 | - **Algebraic subtyping** 25 | - Restricted traditional object-oriented paradigm 26 | - **Algebraic Effects** (planned, semantically unclear) 27 | - **Gradual Structure Typing** 28 | - Intelligent automatic type inference (in progress) 29 | - **AST Macros** (in progress) 30 | - Multi-stage programming with package isolation (not yet implemented) 31 | - **Concurrent Garbage Collection** (in plan, use rc for now) 32 | - **Lossless Rust FFI** (in plan, waiting for crABI) 33 | 34 | ## Tools & Community 35 | 36 | - [Jupyter Runner](https://github.com/nyar-vm/valkyrie-jupyter/blob/dev/projects/playground/main.ipynb): interactive execution 37 | environment 38 | - [Idea Intellij](https://plugins.jetbrains.com/plugin/19452-valkyrie-language): IDEA support 39 | - [AST Visitor](https://docs.rs/valkyrie-ast/0.1.7/valkyrie_ast/): AST definition and pretty print 40 | 41 | 42 | - Advice on Github: [github.com/valkyrie-language](https://github.com/nyar-vm/valkyrie-language/discussions) 43 | - Chat in Discord: [![discord](https://img.shields.io/discord/794446776232443955.svg?logo=discord&style=flat-square)](https://discord.gg/rDScD9GyUC) 44 | 45 | 46 | ## Tutorial 47 | 48 | Learn basic syntax first 49 | 50 | - [Literals](./literal/readme.md): learn basic literals like bool, string and integer 51 | - [Collections](./collection/readme.md): learn common collections like list, map, set 52 | - [Calls and subscripts](./invoke/readme.md): learn how to call functions and get items 53 | - [Let Bind](./definition/let-binding.md): learn how to define variables 54 | - [Define Function](./definition/def-micro.md): learn how to define functions 55 | - [Module](./definition/module.md) & [Package](./definition/package.md): learn how to organize code 56 | 57 | Then learn how to combine FP and OOP 58 | 59 | - [Structure and Class](./definition/structure.md): learn polymorphism through subtyping 60 | - [Interface and Trait](./definition/interface.md): learn polymorphism through type class 61 | - [Enumerate](./definition/enumerate.md) & [Flags](./definition/flags.md): Learn to code in a more readable way 62 | - [Disjoint Union and Variants](./definition/disjoint-union.md): learn polymorphism through ADT 63 | - Macro: learn meta programming through AST transformation 64 | 65 | Then learn funky control flow 66 | 67 | - [If](./control/jump-if.md) & [Switch](./control/jump-switch.md): learn how to jump by condition 68 | - [While Loop](./control/loop-while.md) & [For Loop](./control/loop-for.md): learn how to loop with condition and 69 | iterator 70 | - [Return, Break and Continue](./control/jump-control.md): learn how to return non-locally 71 | - Try, Catch and Resume: learn how to return non-locally 72 | 73 | Finally, a never-ending journey of advancement 74 | 75 | - [Pattern Match](./advance/pattern-match.md) & [Extractor](./advance/extractor.md) 76 | - [Constructor and Builder](./advance/builder.md) 77 | - Define Effect 78 | - `Io` effect and `Logging` effect 79 | - Generics, covariance, contravariance and invariance 80 | - Type conversion, type reinterpretation and type transmutation 81 | - Iterators and Generators 82 | - Closures, callbacks and asynchronous programming 83 | - Raw pointers and unsafe programming 84 | - Value types and the stack allocation 85 | - Macro and package isolation compile 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /docs/cn/basic/error-handling.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | Option 4 | 5 | Result 6 | 7 | 8 | 9 | 10 | 11 | | 错误类型 | 场景 | 12 | | :------- | :--------------------------- | 13 | | Result | | 14 | | Option | | 15 | | int | 返回状态码 | 16 | | bool | 返回是否成功 | 17 | | panic | 无法恢复的错误, 立即退出程序 | 18 | 19 | 20 | | 函数 | 简写 | 21 | | :------------------ | :--- | 22 | | unwrap | `!` | 23 | | unwrap_or | | 24 | | unwrap_or_default() | `!!` | 25 | | throw | `?` | 26 | 27 | 28 | 29 | ### `Option` 枚举和其相对于空值的优势 30 | 31 | 在之前的部分, 我们看到了 `IpAddr` 枚举如何利用 Rust 的类型系统在程序中编码更多信息而不单单是数据. 接下来我们分析一个 `Option` 的案例, `Option` 是标准库定义的另一个枚举. `Option` 类型应用广泛因为它编码了一个非常普遍的场景, 即一个值要么有值要么没值. 从类型系统的角度来表达这个概念就意味着编译器需要检查是否处理了所有应该处理的情况, 这样就可以避免在其他编程语言中非常常见的 bug. 32 | 33 | 编程语言的设计经常要考虑包含哪些功能, 但考虑排除哪些功能也很重要. Rust 并没有很多其他语言中有的空值功能. **空值** (*Null* ) 是一个值, 它代表没有值. 在有空值的语言中, 变量总是这两种状态之一: 空值和非空值. 34 | 35 | Tony Hoare, null 的发明者, 在他 2009 年的演讲 "Null References: The Billion Dollar Mistake" 中曾经说到: 36 | 37 | > I call it my billion-dollar mistake. At that time, I was designing the first 38 | > comprehensive type system for references in an object-oriented language. My 39 | > goal was to ensure that all use of references should be absolutely safe, with 40 | > checking performed automatically by the compiler. But I couldn't resist the 41 | > temptation to put in a null reference, simply because it was so easy to 42 | > implement. This has led to innumerable errors, vulnerabilities, and system 43 | > crashes, which have probably caused a billion dollars of pain and damage in 44 | > the last forty years. 45 | > 46 | > 我称之为我十亿美元的错误. 当时, 我在为一个面向对象语言设计第一个综合性的面向引用的类型系统. 我的目标是通过编译器的自动检查来保证所有引用的使用都应该是绝对安全的. 不过我未能抵抗住引入一个空引用的诱惑, 仅仅是因为它是这么的容易实现. 这引发了无数错误、漏洞和系统崩溃, 在之后的四十多年中造成了数十亿美元的苦痛和伤害. 47 | 48 | 空值的问题在于当你尝试像一个非空值那样使用一个空值, 会出现某种形式的错误. 因为空和非空的属性无处不在, 非常容易出现这类错误. 49 | 50 | 然而, 空值尝试表达的概念仍然是有意义的: 空值是一个因为某种原因目前无效或缺失的值. 51 | 52 | 问题不在于概念而在于具体的实现. 为此, Rust 并没有空值, 不过它确实拥有一个可以编码存在或不存在概念的枚举. 这个枚举是 `Option`, 而且它[定义于标准库中][option], 如下: 53 | 54 | [option]: https://doc.rust-lang.org/std/option/enum.Option.html 55 | 56 | ```valkyrie 57 | enum Option { 58 | Some(T), 59 | None, 60 | } 61 | ``` 62 | 63 | `Option` 枚举是如此有用以至于它甚至被包含在了 prelude 之中, 你不需要将其显式引入作用域. 另外, 它的成员也是如此, 可以不需要 `Option::` 前缀来直接使用 `Some` 和 `None`. 即便如此 `Option` 也仍是常规的枚举, `Some(T)` 和 `None` 仍是 `Option` 的成员. 64 | 65 | `` 语法是一个我们还未讲到的 Rust 功能. 它是一个泛型类型参数, 第十章会更详细的讲解泛型. 目前, 所有你需要知道的就是 `` 意味着 `Option` 枚举的 `Some` 成员可以包含任意类型的数据. 这里是一些包含数字类型和字符串类型 `Option` 值的例子: 66 | 67 | ```valkyrie 68 | let some_number = Some(5); 69 | let some_string = Some("a string"); 70 | 71 | let absent_number: Option = None; 72 | ``` 73 | 74 | 如果使用 `None` 而不是 `Some`, 需要告诉 Rust `Option` 是什么类型的, 因为编译器只通过 `None` 值无法推断出 `Some` 成员保存的值的类型. 75 | 76 | 当有一个 `Some` 值时, 我们就知道存在一个值, 而这个值保存在 `Some` 中. 当有个 `None` 值时, 在某种意义上, 它跟空值具有相同的意义: 并没有一个有效的值. 那么, `Option` 为什么就比空值要好呢? 77 | 78 | 简而言之, 因为 `Option` 和 `T` (这里 `T` 可以是任何类型) 是不同的类型, 编译器不允许像一个肯定有效的值那样使用 `Option`. 例如, 这段代码不能编译, 因为它尝试将 `Option` 与 `i8` 相加: 79 | 80 | ```valkyrie 81 | let x: i8 = 5; 82 | let y: Option = Some(5); 83 | 84 | let sum = x + y; 85 | ``` 86 | 87 | 如果运行这些代码, 将得到类似这样的错误信息: 88 | 89 | ```text 90 | error[E0277]: the trait bound `i8: std::ops::Add>` is 91 | not satisfied 92 | --> 93 | | 94 | 5 | let sum = x + y; 95 | | ^ no implementation for `i8 + std::option::Option` 96 | | 97 | ``` 98 | 99 | 很好! 事实上, 错误信息意味着 Rust 不知道该如何将 `Option` 与 `i8` 相加, 因为它们的类型不同. 当在 Rust 中拥有一个像 `i8` 这样类型的值时, 编译器确保它总是有一个有效的值. 我们可以自信使用而无需做空值检查. 只有当使用 `Option` (或者任何用到的类型) 的时候需要担心可能没有值, 而编译器会确保我们在使用值之前处理了为空的情况. 100 | 101 | 换句话说, 在对 `Option` 进行 `T` 的运算之前必须将其转换为 `T`. 通常这能帮助我们捕获到空值最常见的问题之一: 假设某值不为空但实际上为空的情况. 102 | 103 | 不再担心会错误的假设一个非空值, 会让你对代码更加有信心. 为了拥有一个可能为空的值, 你必须要显式的将其放入对应类型的 `Option` 中. 接着, 当使用这个值时, 必须明确的处理值为空的情况. 只要一个值不是 `Option` 类型, 你就 **可以** 安全的认定它的值不为空. 这是 Rust 的一个经过深思熟虑的设计决策, 来限制空值的泛滥以增加 Rust 代码的安全性. 104 | 105 | 那么当有一个 `Option` 的值时, 如何从 `Some` 成员中取出 `T` 的值来使用它呢?`Option` 枚举拥有大量用于各种情况的方法: 你可以查看[它的文档][docs]. 熟悉 `Option` 的方法将对你的 Rust 之旅非常有用. 106 | 107 | [docs]: https://doc.rust-lang.org/std/option/enum.Option.html 108 | 109 | 总的来说, 为了使用 `Option` 值, 需要编写处理每个成员的代码. 你想要一些代码只当拥有 `Some(T)` 值时运行, 允许这些代码使用其中的 `T`. 也希望一些代码在值为 `None` 时运行, 这些代码并没有一个可用的 `T` 值. `match` 表达式就是这么一个处理枚举的控制流结构: 它会根据枚举的成员运行不同的代码, 这些代码可以使用匹配到的值中的数据. 110 | -------------------------------------------------------------------------------- /docs/cn/basic/control-flow.md: -------------------------------------------------------------------------------- 1 | 根据条件是否为真来决定是否执行某些代码, 以及根据条件是否为真来重复运行一段代码是大部分编程语言的基本组成部分. 2 | 3 | ## `if` Expression 4 | 5 | `if` 表达式允许根据条件执行不同的代码分支. 你提供一个条件并表示 "如果条件满足, 运行这段代码;如果条件不满足, 不运行这段代码. " 6 | 7 | ```rs 8 | let secret = 42; 9 | if secret > 0 { 10 | > "The secret is positive!"; 11 | } 12 | else { 13 | > "The secret is negative!"; 14 | } 15 | ``` 16 | 17 | 所有的 `if` 表达式都以 `if` 关键字开头, 其后跟一个条件. 18 | 19 | `if` 表达式中与条件关联的代码块有时被叫做 *arms*, . 20 | 21 | 也可以包含一个可选的 `else` 表达式来提供一个在条件为假时应当执行的代码块, 这里我们就这么做了. 22 | 23 | 如果不提供 `else` 表达式并且条件为假时, 程序会直接忽略 `if` 代码块并继续执行下面的代码. 24 | 25 | 另外值得注意的是代码中的条件 **必须** 是 `bool` 值. 如果条件不是 `bool` 值, 我们将得到一个错误. 例如, 尝试运行以下代码: 26 | 27 | 28 | 这个错误表明 Rust 期望一个 `bool` 却得到了一个整数. 不像 Ruby 或 JavaScript 这样的语言, Rust 并不会尝试自动地将非布尔值转换为布尔值. 必须总是显式地使用布尔值作为 `if` 的条件. 例如, 如果想要 `if` 代码块只在一个数字不等于 `0` 时执行, 可以把 `if` 表达式修改成下面这样: 29 | 30 | 31 | ```rs 32 | let number = 3; 33 | if number != 0 { 34 | println!("number was something other than zero"); 35 | } 36 | ``` 37 | 38 | 运行代码会打印出 `number was something other than zero`. 39 | 40 | ## `if else-if` Expression 41 | 42 | 可以将 `else if` 表达式与 `if` 和 `else` 组合来实现多重条件. 例如: 43 | 44 | 45 | ```valkyrie 46 | let number = 6; 47 | if number % 2 == 0 { 48 | println!("number is divisible by 2"); 49 | } 50 | else if number % 3 == 0 { 51 | println!("number is divisible by 3"); 52 | } 53 | else if number % 4 == 0 { 54 | println!("number is divisible by 4"); 55 | } 56 | else { 57 | println!("number is not divisible by 2, 3, or 4"); 58 | } 59 | ``` 60 | 61 | 这个程序有四个可能的执行路径. 运行后应该能看到如下输出: 62 | 63 | ```text 64 | $ cargo run 65 | Compiling branches v0.1.0 (file:#?projects/branches) 66 | Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs 67 | Running `target/debug/branches` 68 | number is divisible by 3 69 | ``` 70 | 71 | 当执行这个程序时, 它按顺序检查每个 `if` 表达式并执行第一个条件为真的代码块. 注意即使 6 可以被 2 整除, 也不会输出 `number is divisible by 2`, 更不会输出 `else` 块中的 `number is not divisible by 4, 3, or 2`. 原因是 Rust 只会执行第一个条件为真的代码块, 并且一旦它找到一个以后, 甚至都不会检查剩下的条件了. 72 | 73 | 使用过多的 `else if` 表达式会使代码显得杂乱无章, 所以如果有多于一个 `else if` 表达式, 最好重构代码. 为此, 第六章会介绍一个强大的 Rust 分支结构 (branching construct) , 叫做 `match`. 74 | 75 | 76 | ### 使用循环重复执行 77 | 78 | 多次执行同一段代码是很常用的, Rust 为此提供了多种 **循环** (*loops*) . 一个循环执行循环体中的代码直到结尾并紧接着回到开头继续执行. 为了实验一下循环, 让我们新建一个叫做 *loops* 的项目. 79 | 80 | Rust 有三种循环: `loop`、`while` 和 `for`. 我们每一个都试试. 81 | 82 | #### `while` 条件循环 83 | 84 | 在程序中计算循环的条件也很常见. 当条件为真, 执行循环. 当条件不再为真, 调用 `break` 停止循环. 这个循环类型可以通过组合 `loop`、`if`、`else` 和 `break` 来实现;如果你喜欢的话, 现在就可以在程序中试试. 85 | 86 | 然而, 这个模式太常用了, Rust 为此内置了一个语言结构, 它被称为 `while` 循环. 示例 3-3 使用了 `while`: 程序循环三次, 每次数字都减一. 接着, 在循环结束后, 打印出另一个信息并退出. 87 | 88 | 文件名: src/main.rs 89 | 90 | ```valkyrie 91 | fn main() { 92 | let mut number = 3; 93 | 94 | while number != 0 { 95 | println!("{}!", number); 96 | 97 | number = number - 1; 98 | } 99 | 100 | println!("LIFTOFF!!!"); 101 | } 102 | ``` 103 | 104 | 示例 3-3: 当条件为真时, 使用 `while` 循环运行代码 105 | 106 | 这种结构消除了很多使用 `loop`、`if`、`else` 和 `break` 时所必须的嵌套, 这样更加清晰. 当条件为真就执行, 否则退出循环. 107 | 108 | 109 | #### 从循环返回 110 | 111 | `loop` 的一个用例是重试可能会失败的操作, 比如检查线程是否完成了任务. 然而你可能会需要将操作的结果传递给其它的代码. 如果将返回值加入你用来停止循环的 `break` 表达式, 它会被停止的循环返回: 112 | 113 | ```valkyrie 114 | fn main() { 115 | let mut counter = 0; 116 | 117 | let result = loop { 118 | counter += 1; 119 | 120 | if counter == 10 { 121 | break counter * 2; 122 | } 123 | }; 124 | 125 | println!("The result is {}", result); 126 | } 127 | ``` 128 | 129 | 在循环之前, 我们声明了一个名为 `counter` 的变量并初始化为 `0`. 接着声明了一个名为 `result` 来存放循环的返回值. 在循环的每一次迭代中, 我们将 `counter` 变量加 `1`, 接着检查计数是否等于 `10`. 当相等时, 使用 `break` 关键字返回值 `counter * 2`. 循环之后, 我们通过分号结束赋值给 `result` 的语句. 最后打印出 `result` 的值, 也就是 20. 130 | 131 | 132 | ## `for` Expression 133 | 可以使用 `for` 循环来对一个集合的每个元素执行一些代码. `for` 循环看起来如示例 3-5 所示: 134 | 135 | 文件名: src/main.rs 136 | 137 | ```valkyrie 138 | fn main() { 139 | let a = [10, 20, 30, 40, 50]; 140 | 141 | for element in a.iter() { 142 | println!("the value is: {}", element); 143 | } 144 | } 145 | ``` 146 | 147 | 示例 3-5: 使用 `for` 循环遍历集合中的元素 148 | 149 | 当运行这段代码时, 将看到与示例 3-4 一样的输出. 更为重要的是, 我们增强了代码安全性, 并消除了可能由于超出数组的结尾或遍历长度不够而缺少一些元素而导致的 bug. 150 | 151 | 例如, 在示例 3-4 的代码中, 如果从数组 `a` 中移除一个元素但忘记将条件更新为 `while index < 4`, 代码将会 panic. 使用 `for` 循环的话, 就不需要惦记着在改变数组元素个数时修改其他的代码了. 152 | 153 | `for` 循环的安全性和简洁性使得它成为 Rust 中使用最多的循环结构. 即使是在想要循环执行代码特定次数时, 例如示例 3-3 中使用 `while` 循环的倒计时例子, 大部分 Rustacean 也会使用 `for` 循环. 这么做的方式是使用 `Range`, 它是标准库提供的类型, 用来生成从一个数字开始到另一个数字之前结束的所有数字的序列. 154 | 155 | 下面是一个使用 `for` 循环来倒计时的例子, 它还使用了一个我们还未讲到的方法, `rev`, 用来反转 range: 156 | 157 | 文件名: src/main.rs 158 | 159 | ```valkyrie 160 | fn main() { 161 | for number in (1..4).rev() { 162 | println!("{}!", number); 163 | } 164 | println!("LIFTOFF!!!"); 165 | } 166 | ``` 167 | 168 | 这段代码看起来更帅气不是吗? 169 | -------------------------------------------------------------------------------- /docs/en/definition/def-macro.md: -------------------------------------------------------------------------------- 1 | ## Macro 2 | 3 | The Valkyrie language provides the ability to define macros, which are reusable code snippets that can be expanded at compile-time. 4 | 5 | Macros allow for code generation and metaprogramming. 6 | 7 | The `macro` keyword is used to define a macro that can take in different types of arguments, such as `IntegerNode` or `ExpressionNode`, and perform operations on them. 8 | 9 | ```valkyrie 10 | micro f1(e: Integer) { 11 | print(e); 12 | } 13 | macro m1(e: IntegerNode) { 14 | print(e); 15 | } 16 | macro m2(e: ExpressionNode) { 17 | print(e); 18 | } 19 | ``` 20 | 21 | The above code snippet defines two `macro` macros named `m1` and `m2`. 22 | 23 | The `m1` macro takes an `IntegerNode` argument `e` and prints its value. 24 | 25 | The `m2` macro takes an `ExpressionNode` argument `e` and prints its value. 26 | 27 | ```valkyrie 28 | f1(1 + 1) // 2 29 | m1(1 + 1) // fatal error: macro f21: expected IntegerNode, found ExpressionNode 30 | m2(1 + 1) // ExpressionNode { left: IntegerNode { value: 1 }, operator: Add, right: IntegerNode { value: 1 } } 31 | ``` 32 | 33 | The above code snippet demonstrates the usage of the previously defined macros. 34 | 35 | - `f1(1 + 1)` invokes the `f1` macro with the argument `1 + 1` and prints the result, which is `2`. 36 | - `m1(1 + 1)` tries to invoke the `m1` macro with the argument `1 + 1`. 37 | - However, it results in a fatal error because the `m1` macro expects an `IntegerNode` argument, but an `ExpressionNode` is provided instead. 38 | - `m2(1 + 1)` successfully invokes the `m2` macro with the argument `1 + 1`. 39 | - It prints an `ExpressionNode` object representing the expression `1 + 1`, which consists of two `IntegerNode` objects representing the values `1` and `1`, and an `Add` operator. 40 | 41 | ### Evaluation Macro 42 | 43 | ```valkyrie 44 | macro m1(lhs: ExpressionNode, micro rhs: Integer) { 45 | lhs.evaluate() + rhs 46 | } 47 | ``` 48 | 49 | The above code snippet shows the definition of a `macro` named `m1` that takes an `ExpressionNode` argument `lhs` and a `micro` argument `rhs` of type `Integer`. 50 | 51 | This macro evaluates the `lhs` expression using the `evaluate` method and adds it to the `rhs` value. 52 | 53 | Please note that the specific implementation details of the `evaluate` method and how the addition is performed are not provided in the code snippet. 54 | 55 | ## Continuation Capture 56 | 57 | Continuation capture in Valkyrie allows macros to receive and manipulate continuations, which represent the remaining code to be executed. 58 | 59 | ```valkyrie 60 | #continuation 61 | macro f1(e: ExpressionNode, c: ContinuationNode) { 62 | // continuation is required 63 | } 64 | #continuation 65 | macro f2(e: ExpressionNode, c: ContinuationNode?) { 66 | // continuation is optional 67 | } 68 | #continuation 69 | macro f3(e: ExpressionNode) { 70 | // fatal error: expected ContinuationNode, found Nothing 71 | } 72 | ``` 73 | 74 | The last parameter defined must be a `ContinuationNode`. 75 | 76 | 77 | ```valkyrie 78 | f1(1 + 1) // fatal error: macro f: expected ContinuationNode, found None 79 | f1(1 + 1) { 80 | body 81 | } 82 | f2(1 + 1) 83 | f2(1 + 1) { 84 | class A { } // syntax error: declaration not allowed in continuation. 85 | body 86 | } 87 | ``` 88 | 89 | The above code snippet demonstrates the usage of the previously defined continuation capture macros. 90 | 91 | - `f1(1 + 1)` tries to invoke the `f1` macro without providing a continuation. However, this macro expects a continuation to be provided, so it results in a fatal error. 92 | - `f1(1 + 1) { body }` invokes the `f1` macro with the argument `1 + 1` and a continuation block `body`. The macro implementation can manipulate the continuation block as needed. 93 | - `f2(1 + 1)` invokes the `f2` macro with the argument `1 + 1` but without providing a continuation. Since the continuation is optional, this usage is valid. 94 | - `f2(1 + 1) { class A { } body }` invokes the `f2` macro with the argument `1 + 1` and a continuation block containing a class declaration `class A { }` and a `body`. However, this usage results in a syntax error because declarations are not allowed within a continuation. 95 | 96 | ### `@Procedural` Macro 97 | 98 | The `@procedural` macro in Valkyrie allows macros to capture and manipulate domain-specific language (DSL) constructs. 99 | 100 | ```valkyrie 101 | #procedural 102 | macro f1(e: ExpressionNode, dsl: StatementBlock) { 103 | // domain is required 104 | } 105 | #procedural 106 | macro f2(e: ExpressionNode, dsl: StatementBlock?) { 107 | // domain is optional 108 | } 109 | #procedural 110 | macro f3(e: ExpressionNode) { 111 | // fatal error: expected DomainNode, found Nothing 112 | } 113 | ``` 114 | 115 | 116 | ```valkyrie 117 | @f(1 + 1) { 118 | class A { } // ok 119 | body 120 | } 121 | ``` 122 | 123 | 124 | `@f(1 + 1)` invokes the `f` macro with the argument `1 + 1` and a domain construct enclosed in curly braces `{}`. 125 | 126 | ### `#Attribute` Macro 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /docs/cn/basic/readme.md: -------------------------------------------------------------------------------- 1 | ## 基本字面量 2 | 3 | 在源代码中能直接输入的类型叫做字面量 4 | 5 | V 有以下五种基本字面量 6 | 7 | | 类型 | 类型名称 | 缩写 | 默认值 | 8 | | :--- | --------- | -------- | --------- | 9 | | 布尔 | Boolean | **bool** | `false` | 10 | | 字符 | Character | **char** | `'\u{0}'` | 11 | | 字符 | String | `/` | `""` | 12 | | 整数 | Integer | `/` | `0` | 13 | | 小数 | Decimal | `/` | `0.0` | 14 | 15 | 16 | 17 | ### 布尔值 18 | 19 | 布尔类型简称 bool, 用于表示逻辑上的真假. 20 | 21 | 布尔类型只有两种 `true` 和 `false`. 22 | 23 | 注意, V 中的 `if` 条件并不一定要布尔值才能触发, 任何实现了 `Into` 的结构体都可以作为跳转判定条件. 24 | 25 | if [] { 26 | 27 | } 28 | 29 | 30 | 31 | 32 | 33 | ### 整数 & 小数 34 | 35 | 整数类型简称 int, 用于表示一个任意精度带正负的整数 36 | 37 | 整数还能使用比特输入, 在[高级算符](../advance)部分会深入介绍 38 | 39 | 还有一种特殊的数字算符输入, 这个比较复杂, 在[高级算符](../advance)部分会深入介绍 40 | 41 | ### 整数范围 {#integer-bounds} 42 | 43 | 你可以访问不同整数类型的 `min` 和 `max` 属性来获取对应类型的最小值和最大值: 44 | 45 | ```valkyrie 46 | let minValue = UInt8.min # minValue 为 0, 是 UInt8 类型 47 | let maxValue = UInt8.max # maxValue 为 255, 是 UInt8 类型 48 | ``` 49 | 50 | `min` 和 `max` 所传回值的类型, 正是其所对的整数类型 (如上例 UInt8, 所传回的类型是 UInt8) , 可用在表达式中相同类型值旁. 51 | 52 | ### Int {#Int} 53 | 54 | 一般来说, 你不需要专门指定整数的长度. Swift 提供了一个特殊的整数类型 `Int`, 长度与当前平台的原生字长相同: 55 | 56 | * 在32位平台上, `Int` 和 `Int32` 长度相同. 57 | * 在64位平台上, `Int` 和 `Int64` 长度相同. 58 | 59 | 除非你需要特定长度的整数, 一般来说使用 `Int` 就够了. 这可以提高代码一致性和可复用性. 即使是在32位平台上, `Int` 可以存储的整数范围也可以达到 `-2,147,483,648` ~ `2,147,483,647`, 大多数时候这已经足够大了. 60 | 61 | ### UInt {#UInt} 62 | 63 | Swift 也提供了一个特殊的无符号类型 `UInt`, 长度与当前平台的原生字长相同: 64 | 65 | * 在32位平台上, `UInt` 和 `UInt32` 长度相同. 66 | * 在64位平台上, `UInt` 和 `UInt64` 长度相同. 67 | 68 | > 注意 69 | > 70 | > 尽量不要使用 `UInt`, 除非你真的需要存储一个和当前平台原生字长相同的无符号整数. 除了这种情况, 最好使用 `Int`, 即使你要存储的值已知是非负的. 统一使用 `Int` 可以提高代码的可复用性, 避免不同类型数字之间的转换, 并且匹配数字的类型推断, 请参考 [类型安全和类型推断](#type-safety-and-type-inference). 71 | 72 | ## 浮点数 {#floating-point-numbers} 73 | 74 | 浮点数是有小数部分的数字, 比如 `3.14159`、`0.1` 和 `-273.15`. 75 | 76 | 浮点类型比整数类型表示的范围更大, 可以存储比 `Int` 类型更大或者更小的数字. Swift 提供了两种有符号浮点数类型: 77 | 78 | * `Double` 表示64位浮点数. 当你需要存储很大或者很高精度的浮点数时请使用此类型. 79 | * `Float` 表示32位浮点数. 精度要求不高的话可以使用此类型. 80 | 81 | > 注意 82 | > 83 | > `Double` 精确度很高, 至少有 15 位小数, 而 `Float` 只有 6 位小数. 选择哪个类型取决于你的代码需要处理的值的范围, 在两种类型都匹配的情况下, 将优先选择 `Double`. 84 | 85 | 86 | 87 | ### 字符 & 字符串 88 | 89 | 字符类型简称 char, 默认用于表示一个 unicode 字符 90 | 91 | 字符类型用成对的 `'` 包裹, 支持使用 `\` 转义 92 | 93 | 可转义 94 | 95 | ```ts 96 | let a = '\u{12}' 97 | let b = '\U{AF}' 98 | ``` 99 | 100 | 如果有三个以上的引号那么就是多行字符串 101 | 102 | 多行字符串比较复杂, 这部分在[高级算符](../advance)部分会深入介绍 103 | 104 | ## 复合字面量 105 | 106 | ### 元组 107 | 108 | **元组 (tuples)** 指把多个值组合成一个复合值. 109 | 110 | 元组内的值可以是任意类型, 并不要求是相同类型. 111 | 112 | 下面这个例子中, `(404, "Not Found")` 是一个描述 *HTTP 状态码 (HTTP status code)* 的元组. 113 | 114 | HTTP 状态码是当你请求网页的时候 web 服务器返回的一个特殊值. 115 | 116 | 如果你请求的网页不存在就会返回一个 `404 Not Found` 状态码. 117 | 118 | ```valkyrie 119 | let http404 = (404, "Not Found") 120 | #? http404 的类型是 (Integer, String), 值是 (404, "Not Found") 121 | ``` 122 | 123 | `(404, "Not Found")` 元组把一个 `Integer` 值和一个 `String` 值组合起来表示 HTTP 状态码的两个部分: 一个数字和一个人类可读的描述. 124 | 125 | 这个元组可以被描述为: "一个类型为 `(Integer, String)` 的元组". 126 | 127 | 你可以把任意顺序的类型组合成一个元组, 这个元组可以包含所有类型. 128 | 129 | 你可以将一个元组的内容分解 (decompose) 成单独的常量和变量, 然后你就可以正常使用它们了: 130 | 131 | ```valkyrie 132 | let (status_code, status_message) = http404Error; 133 | print("The status code is ${status_code}") 134 | #? 输出 "The status code is 404" 135 | print("The status message is ${status_message}") 136 | #? 输出 "The status message is Not Found" 137 | ``` 138 | 139 | 如果你只需要一部分元组值, 分解的时候可以把要忽略的部分用下划线 (`_`) 标记: 140 | 141 | ```valkyrie 142 | let (just_status_code, _) = http404; 143 | print("The status code is ${just_status_code}") 144 | #? 输出 "The status code is 404" 145 | ``` 146 | 147 | 此外, 你还可以通过下标来访问元组中的单个元素, 下标从零开始: 148 | 149 | ```valkyrie 150 | print("The status code is ${http404.0}") 151 | #? 输出"The status code is 404" 152 | print("The status message is ${http404.1}") 153 | #? 输出"The status message is Not Found" 154 | ``` 155 | 156 | 元组一般被用于轻量级的数据结构, 比如函数的返回值. 157 | 158 | ### List 159 | 160 | 内部数据结构是 VectorDeque 161 | 162 | ### Object 163 | 164 | 对象(Object)表示一个可扩展的匿名结构体. 165 | 166 | 这一结构在不同的语言中有不同的叫法, 例如对象(Object), 字典(Dictionary), 记录(Record), 映射(Map)等等. 167 | 168 | ## 字面量多态 169 | 170 | Literal Polymorphism 171 | 172 | Valkyrie 拥有字面量多态(Literal Polymorphism), 也就是说一个字面量的类型不止取决于本身, 也取决于上下文 173 | 174 | ```ts 175 | let a = 0 //? 这是整数 176 | let b = 0 //? 这是小数 177 | 178 | a + 1 179 | b + 1.0 180 | ``` 181 | 182 | 还有一种方法就是标记类型, 这样也能自动转换输入的字面量 183 | 184 | ```ts 185 | let a: Integer = 0 186 | let b: Decimal = 0 187 | ``` 188 | 189 | 190 | ## 注释 191 | 192 | 某种程度上来讲, 注释也是一种字面量, 但是注释无法影响到程序的解析和执行 193 | 194 | 注释分为单行注释和多行注释 195 | 196 | ```rs 197 | #? NORMAL: 这是普通注释 198 | //* ATTENTION: 这里需要引起注意 199 | //! FIXME: 这里是急需修复的部分 200 | //? TODO: 这里是待办事项 201 | 202 | %%% 203 | 这里是多行注释, 用于表示文档 204 | %%% 205 | ``` 206 | 207 | ## 分号 {#semicolons} 208 | 209 | 与其他大部分编程语言不同, Swift 并不强制要求你在每条语句的结尾处使用分号 (`;`) , 当然, 你也可以按照你自己的习惯添加分号. 有一种情况下必须要用分号, 即你打算在同一行内写多条独立的语句: 210 | 211 | ```valkyrie 212 | let cat = "🐱"; print(cat) 213 | # 输出"🐱" 214 | ``` 215 | -------------------------------------------------------------------------------- /docs/cn/literal/formatter.md: -------------------------------------------------------------------------------- 1 | # 格式化字符串 2 | 3 | The String Formatter is a utility that allows you to customize the formatting of strings in Valkyrie programming language. 4 | 5 | It provides various options to format strings based on different parameters. 6 | 7 | ## `{v}` 8 | 9 | The `{v}` placeholder is used to insert the value of variable `v` into a string. It is equivalent to calling `v.display()`. 10 | 11 | ```valkyrie 12 | "{v}" // v.display() 13 | ``` 14 | 15 | To explicitly initialize the string with the default formatter, it is recommended to add `f` before the string. 16 | 17 | ```valkyrie 18 | f"{v}" // v.display() 19 | ``` 20 | 21 | ## `{v:formatter}` 22 | 23 | The `{v:formatter}` syntax allows you to specify a custom formatter for the variable `v`. This enables you to format the value according to specific rules or patterns. 24 | 25 | ```valkyrie 26 | "{v:?}" // v.display("?") 27 | ``` 28 | 29 | For example, you can format a `DateTime` object to display the date and time in a specific format. 30 | 31 | ```valkyrie 32 | let time = DateTime("2020-02-29 20:02:29"); 33 | "{time:dd.MM.yyyy HH:mm:ss}" 34 | // Output: 02.29.2020 20:02:29 35 | ``` 36 | 37 | Alternatively, you can use the `display` function of the `DateTime` object to achieve the same result. 38 | 39 | ```valkyrie 40 | time.display("dd.MM.yyyy HH:mm:ss") 41 | ``` 42 | 43 | ## `{v, ...arguments}` 44 | 45 | The `{v, ...arguments}` syntax allows you to provide additional arguments to the formatter. This can be useful when you need to specify formatting options such as culture or locale. 46 | 47 | ```valkyrie 48 | let time = DateTime("2020-02-29 20:02:29"); 49 | "{time, 'dd.MM.yyyy HH:mm:ss', culture: 'en-US'}" 50 | // Output: 02/29/2020 20:02:29 51 | ``` 52 | 53 | In the above example, the `culture` argument is used to format the date and time according to the English (United States) locale. 54 | 55 | You can achieve the same result by calling the `display` function of the `DateTime` object and providing the desired formatting options. 56 | 57 | ```valkyrie 58 | time.display("%Y-%m-%d %H:%M:%S", culture: 'en-US') 59 | ``` 60 | 61 | ## Formatter 62 | 63 | The `Display` trait provides the underlying implementation for formatting. 64 | 65 | It defines the following methods: 66 | 67 | ```valkyrie 68 | trait ToString { 69 | format(self, mut formatter: Formatter, ...args): Result<(), DisplayError>; 70 | #hide(completion) 71 | format_hint(self): u64 { 0 } 72 | final display(self, ...args) { 73 | let mut f = Formatter(UTF8Text(capacity: self.format_hint())); 74 | self.format(f) 75 | f.buffer 76 | } 77 | } 78 | ``` 79 | 80 | - The `format` method is responsible for formatting the value and writing it to the `formatter` object. 81 | - It takes the value itself (`self`) and additional formatting arguments (`args`). 82 | - It returns a `Result` indicating whether the formatting was successful or not. 83 | - The `format_hint` method provides a hint about the expected capacity of the formatter. 84 | - This can be useful for optimizing memory allocation when formatting large strings. 85 | - The `display` method is a convenience function that initializes a `Formatter` object, calls `format` on the value, and returns the formatted string as a buffer. 86 | 87 | By implementing the `Display` trait and providing custom formatting logic, you can customize the display of objects in Valkyrie. 88 | 89 | ## Formatter Derive 90 | 91 | The `Formatter Derive` feature in Valkyrie allows you to automatically generate the implementation of the `Display` trait for a custom class or structure. This eliminates the need to manually implement the `format` method for formatting the object's display. 92 | 93 | To use the `Formatter Derive`, you need to annotate your class or structure with `#derive(Display)`. This tells the Valkyrie compiler to generate the necessary code for formatting the object. 94 | 95 | Here's an example of using the `Formatter Derive` for a `Point` class: 96 | 97 | ```valkyrie 98 | #derive(Display) 99 | class Point { 100 | x: f64, 101 | y: f64, 102 | } 103 | ``` 104 | 105 | With the `#derive(Display)` annotation, the Valkyrie compiler will automatically generate the implementation of the `Display` trait for the `Point` class. This implementation will include the `format` method, which formats the object's display based on the provided formatting options. 106 | 107 | After applying the `Formatter Derive` to the `Point` class, you can use the `display` function or the `{}` placeholder to format and display `Point` objects. 108 | 109 | ```valkyrie 110 | let p = new Point { x: 2.5, y: 3.7 }; 111 | f"{p}" // Output: "Point { x: 2.5, y: 3.7 }" 112 | ``` 113 | 114 | The generated implementation of the `Display` trait will use the default formatting options for the class members. If you want to customize the formatting, you can override the `format` method in your class and provide your own logic. 115 | 116 | By using the `Formatter Derive`, you can simplify the implementation of the `Display` trait for your custom classes and structures, making it easier to format and display objects in a desired way. 117 | -------------------------------------------------------------------------------- /docs/en/literal/formatter.md: -------------------------------------------------------------------------------- 1 | # String Formatter 2 | 3 | The String Formatter is a utility that allows you to customize the formatting of strings in Valkyrie programming language. 4 | 5 | It provides various options to format strings based on different parameters. 6 | 7 | ## `{v}` 8 | 9 | The `{v}` placeholder is used to insert the value of variable `v` into a string. It is equivalent to calling `v.display()`. 10 | 11 | ```valkyrie 12 | "{v}" // v.display() 13 | ``` 14 | 15 | To explicitly initialize the string with the default formatter, it is recommended to add `f` before the string. 16 | 17 | ```valkyrie 18 | f"{v}" // v.display() 19 | ``` 20 | 21 | ## `{v:formatter}` 22 | 23 | The `{v:formatter}` syntax allows you to specify a custom formatter for the variable `v`. This enables you to format the value according to specific rules or patterns. 24 | 25 | ```valkyrie 26 | "{v:?}" // v.display("?") 27 | ``` 28 | 29 | For example, you can format a `DateTime` object to display the date and time in a specific format. 30 | 31 | ```valkyrie 32 | let time = DateTime("2020-02-29 20:02:29"); 33 | "{time:dd.MM.yyyy HH:mm:ss}" 34 | // Output: 02.29.2020 20:02:29 35 | ``` 36 | 37 | Alternatively, you can use the `display` function of the `DateTime` object to achieve the same result. 38 | 39 | ```valkyrie 40 | time.display("dd.MM.yyyy HH:mm:ss") 41 | ``` 42 | 43 | ## `{v, ...arguments}` 44 | 45 | The `{v, ...arguments}` syntax allows you to provide additional arguments to the formatter. This can be useful when you need to specify formatting options such as culture or locale. 46 | 47 | ```valkyrie 48 | let time = DateTime("2020-02-29 20:02:29"); 49 | "{time, 'dd.MM.yyyy HH:mm:ss', culture: 'en-US'}" 50 | // Output: 02/29/2020 20:02:29 51 | ``` 52 | 53 | In the above example, the `culture` argument is used to format the date and time according to the English (United States) locale. 54 | 55 | You can achieve the same result by calling the `display` function of the `DateTime` object and providing the desired formatting options. 56 | 57 | ```valkyrie 58 | time.display("%Y-%m-%d %H:%M:%S", culture: 'en-US') 59 | ``` 60 | 61 | ## Formatter 62 | 63 | The `Display` trait provides the underlying implementation for formatting. 64 | 65 | It defines the following methods: 66 | 67 | ```valkyrie 68 | trait ToString { 69 | format(self, mut formatter: Formatter, ...args): Result<(), DisplayError>; 70 | #hide(completion) 71 | format_hint(self): u64 { 0 } 72 | final display(self, ...args) { 73 | let mut f = Formatter(UTF8Text(capacity: self.format_hint())); 74 | self.format(f) 75 | f.buffer 76 | } 77 | } 78 | ``` 79 | 80 | - The `format` method is responsible for formatting the value and writing it to the `formatter` object. 81 | - It takes the value itself (`self`) and additional formatting arguments (`args`). 82 | - It returns a `Result` indicating whether the formatting was successful or not. 83 | - The `format_hint` method provides a hint about the expected capacity of the formatter. 84 | - This can be useful for optimizing memory allocation when formatting large strings. 85 | - The `display` method is a convenience function that initializes a `Formatter` object, calls `format` on the value, and returns the formatted string as a buffer. 86 | 87 | By implementing the `Display` trait and providing custom formatting logic, you can customize the display of objects in Valkyrie. 88 | 89 | ## Formatter Derive 90 | 91 | The `Formatter Derive` feature in Valkyrie allows you to automatically generate the implementation of the `Display` trait for a custom class or structure. This eliminates the need to manually implement the `format` method for formatting the object's display. 92 | 93 | To use the `Formatter Derive`, you need to annotate your class or structure with `#derive(Display)`. This tells the Valkyrie compiler to generate the necessary code for formatting the object. 94 | 95 | Here's an example of using the `Formatter Derive` for a `Point` class: 96 | 97 | ```valkyrie 98 | #derive(Display) 99 | class Point { 100 | x: f64, 101 | y: f64, 102 | } 103 | ``` 104 | 105 | With the `#derive(Display)` annotation, the Valkyrie compiler will automatically generate the implementation of the `Display` trait for the `Point` class. This implementation will include the `format` method, which formats the object's display based on the provided formatting options. 106 | 107 | After applying the `Formatter Derive` to the `Point` class, you can use the `display` function or the `{}` placeholder to format and display `Point` objects. 108 | 109 | ```valkyrie 110 | let p = new Point { x: 2.5, y: 3.7 }; 111 | f"{p}" // Output: "Point { x: 2.5, y: 3.7 }" 112 | ``` 113 | 114 | The generated implementation of the `Display` trait will use the default formatting options for the class members. If you want to customize the formatting, you can override the `format` method in your class and provide your own logic. 115 | 116 | By using the `Formatter Derive`, you can simplify the implementation of the `Display` trait for your custom classes and structures, making it easier to format and display objects in a desired way. 117 | -------------------------------------------------------------------------------- /docs/en/collection/dict.md: -------------------------------------------------------------------------------- 1 | ## Dictionary 2 | 3 | The `Dictionary` trait defines a generic dictionary data structure in Valkyrie. 4 | 5 | It provides several associated types and predicates for working with dictionaries. 6 | 7 | ### Associated Types 8 | 9 | - `Key`: Represents the type of keys in the dictionary. 10 | - `Value`: Represents the type of values in the dictionary. 11 | - `Entry`: Represents the type of entries in the dictionary. 12 | 13 | ### Alias Types 14 | 15 | - `Pair`: Represents the type of key-value pairs in the dictionary. 16 | - It is an alias for a tuple `(key: Key, value: Value)`. 17 | 18 | ### Predicates 19 | 20 | - `mutable`: Specifies whether the value of the dictionary can be changed. 21 | - `scalable`: Specifies whether the keys of the dictionary can be added, deleted, or replaced. 22 | - `default`: Specifies whether the dictionary has a default value when a key is not found. 23 | 24 | These associated types and predicates provide flexibility and control over the behavior of dictionaries in Valkyrie. 25 | 26 | ### Methods 27 | 28 | - `get(key: Self::Key): Self::Value?` 29 | - Returns the value associated with the specified key, or `null` if the key is not found. 30 | - `get_or(key: Self::Key, default: Self::Value): Self::Value` 31 | - Returns the value associated with the specified key, or the default value if the key is not found. 32 | - `get_or_default(key: Self::Key): Self::Value` 33 | - Returns the value associated with the specified key, or the default value if the key is not found. 34 | - `insert(key: Self::Key, value: Self::Value): Self::Value?` 35 | - Inserts a new key-value pair into the dictionary. 36 | - `remove(key: Self::Key): Self::Value?` 37 | - Removes the key-value pair associated with the specified key from the dictionary. 38 | 39 | ## Instance Classes 40 | 41 | ### IndexMap 42 | 43 | `IndexMap` is a specific implementation of the dictionary data structure in Valkyrie that preserves the order of inserts. It behaves like a map where elements are stored in the order in which they were added. 44 | 45 | The code snippet demonstrates the usage of `IndexMap`. It creates a new `IndexMap` called `map` with keys of type `UTF8Text` and values of type `Int`. The map is initialized with four key-value pairs: `first: 1`, `second: 2`, `third: 3`, and `forth: 4`. 46 | 47 | A `for` loop is then used to iterate over the key-value pairs in the `map`, printing each key and value. The output shows the keys and values in the order they were inserted. 48 | 49 | ```valkyrie 50 | let map = new IndexMap { 51 | first: 1, 52 | second: 2, 53 | third: 3, 54 | forth: 4, 55 | } 56 | for (key, value) in map { 57 | print("{key}: {value}") 58 | // first: 1 59 | // second: 2 60 | // third: 3 61 | // forth: 4 62 | } 63 | ``` 64 | 65 | The example demonstrates the preservation of insertion order in the `IndexMap`. 66 | 67 | ### OrderMap 68 | 69 | `OrderMap` is another implementation of the dictionary data structure in Valkyrie that preserves the order of the keys. It maintains the keys in the order they were added or last updated. 70 | 71 | The code snippet showcases the usage of `OrderMap`. It creates a new `OrderMap` called `map` with keys of type `UTF8Text` and values of type `Int`. The map is initialized with four key-value pairs: `first: 1`, `second: 2`, `third: 3`, and `forth: 4`. 72 | 73 | Similar to the previous example, a `for` loop is used to iterate over the key-value pairs in the `map`, printing each key and value. The output displays the keys and values in the order of the keys. 74 | 75 | ```valkyrie 76 | let map = new OrderMap { 77 | first: 1, 78 | second: 2, 79 | third: 3, 80 | forth: 4, 81 | } 82 | for (key, value) in map { 83 | print("{key}: {value}") 84 | // first: 1 85 | // forth: 4 86 | // second: 2 87 | // third: 3 88 | } 89 | ``` 90 | 91 | The example demonstrates how `OrderMap` preserves the order of the keys, even if updates are made to the values. 92 | 93 | ### HashMap 94 | 95 | `HashMap` is yet another implementation of the dictionary data structure in Valkyrie, but it does not preserve the order of the keys. It provides efficient key-value storage and retrieval, but the order of the keys is not guaranteed. 96 | 97 | The code snippet illustrates the usage of `HashMap`. It creates a new `HashMap` called `map` with keys of type `UTF8Text`, values of type `Int`, and a `SipHasher24` as the hash function. The map is initialized with four key-value pairs: `first: 1`, `second: 2`, `third: 3`, and `forth: 4`. 98 | 99 | Similarly, a `for` loop is used to iterate over the key-value pairs in the `map`, but unlike the previous examples, the output does not follow a specific order. The keys and values are printed in a random order. 100 | 101 | ```valkyrie 102 | let map = new HashMap { 103 | first: 1, 104 | second: 2, 105 | third: 3, 106 | forth: 4, 107 | } 108 | for (key, value) in map { 109 | print("{key}: {value}") 110 | // random 111 | } 112 | ``` 113 | 114 | This example highlights that `HashMap` does not guarantee the preservation of key order. 115 | 116 | ### Record 117 | 118 | The `Record` type is an alias for `IndexMap`, providing a convenient shorthand for creating dictionaries with string keys. 119 | 120 | ```valkyrie 121 | type Record = IndexMap 122 | ``` 123 | 124 | The `Record` type definition creates a specialized dictionary where the keys are of type `UTF8Text` and the values can be of any type `T`. It essentially represents a `IndexMap` with string keys. 125 | 126 | The `Record` type can be used to define dictionaries with string keys in Valkyrie, providing a more expressive and readable way to work with such dictionaries. 127 | -------------------------------------------------------------------------------- /docs/cn/advance/algebraic-data-type.md: -------------------------------------------------------------------------------- 1 | ## Algebraic Data Type 2 | 3 | 代数数据类型简称 ADT, Curry-Howard 同构指出了逻辑系统和程序语言之间的相似性 4 | 5 | 6 | $$\begin{array}{|c|c|c|} 7 | \hline\text { Algebra } & \text { Logic } & \text { Types } \\ 8 | \hline a+b & a \vee b & \text { a | b } \\ 9 | \hline a \times b & a \wedge b & (a, b) \\ 10 | \hline a=b & a \Longleftrightarrow b & \text { isomorphism }\\ 11 | \hline b^{a} & a \Longrightarrow b & a \rightarrow b \\ 12 | \hline \text { 0 } & \perp & ! \\ 13 | \hline 1 & ⊤ & \text { () } \\ 14 | \hline 15 | \end{array}$$ 16 | 17 | 于是类型可以有如代数一般运算. 18 | 19 | ## Primitive Type 20 | 21 | 代数最基本的是数字, 某种程度上我们可以把类型视为一个集合 22 | 23 | 接着计算所有可能元素的数量以此来定义类型的大小 24 | 25 | 相同大小的类型可以相互包容相互表示, 也就是说他们同构 26 | 27 | ### Void 28 | 29 | ```hs 30 | type Void {}; 31 | ``` 32 | 33 | 我们声明了一个类型, 但是他没有任何实例, 也就是说他有零个值. 34 | 35 | 类型 0 用于代表无法正常返回, 程序可能会永远运行下去 36 | 37 | ```py 38 | def never_return() -> Void { 39 | while true { } 40 | } 41 | ``` 42 | 43 | ### Unit 44 | 45 | 46 | 只有一个值的类型是单例, 有时候也用 `( )` 表示, 函数返回 Unit 时无需分配内存 47 | 48 | ```hs 49 | type Unit { 50 | Unit 51 | } 52 | ``` 53 | 54 | ### Boolean 55 | 56 | ```hs 57 | type Boolean { 58 | True, 59 | False, 60 | } 61 | ``` 62 | 63 | bool 类型的对象有两个值: False 和 True 64 | 65 | ### More 66 | 67 | 以此类推我们还能定义类型 3, 类型 4, 比如 68 | 69 | ```hs 70 | type Sign { 71 | Positive, 72 | Negative, 73 | Zero, 74 | }; 75 | type Direction { 76 | East, 77 | South, 78 | West, 79 | North, 80 | }; 81 | ``` 82 | 83 | 对于 `u8`, 它有 $0\sim255$ 一共 $256$ 个取值, 所以对应类型 $256$ 84 | 85 | 同时 `i8` 也有 $256$ 个取值, 也是类型 $256$, 因此这两者能相互转换 86 | 87 | ## Sum Type 88 | 89 | 可以看到, 每列出一项, 类型的值就会加上那一项对应的类型值 90 | 91 | 考虑集合的并, 如果集合不相交, 取并集后的大小等于之前两者的大小加和 92 | 93 | 在集合论中, 这被称作**不交并**(disjoint union), 如图 94 | 95 |
96 | 97 | 在类型论中, 这被称为**和类型** (Sum Type) 98 | 99 | 在编程语言中很多不同的名称, 比如 enum/variant/alternation/tagged union 等等 100 | 101 | ## Union Type 102 | 103 | 并类型, 有时候会和和类型搞混, 不过这两者是不一样的 104 | 105 | ```ts 106 | //? Sum Type 107 | type Option { 108 | Some(T), 109 | None 110 | } 111 | 112 | Option> =/> Option 113 | ``` 114 | 115 | 当两者类型有交集时, 取并集的时候那部分只会计算一次 116 | 117 | ```ts 118 | //? Union Type 119 | type Optional = T | null; 120 | 121 | T | null | null ==> T | null 122 | Optional> ==> Optional 123 | ``` 124 | 125 | 这种约化有时候会有麻烦, 比如你取到一个 undefined 的时候就容易搞不清到底这个值天生就是 undefined 还是取不到所以返回 undefined? 126 | 127 | 所以 typescript 和 python 虽然有类型标注但这个问题还是很头大 128 | 129 | ## Product Type 130 | 131 | 如果我要表示 $256 \times 256$ 方格上的一个整数点, 我可能会使用元组 `(x, y)` 表示. 132 | 133 | 其中 $x$ 取遍 $0\sim255$, $y$ 同样取遍 $0\sim255$, 于是一共有 $256 \times 256 = 65536$ 个取值. 134 | 135 | 因此, 类型 `(u8, u8)` 对应类型 $65536$, 当然我还可以用 `class`. 136 | 137 | ```ts 138 | class Point { 139 | x: u8 = 0 140 | y: u8 = 0 141 | } 142 | ``` 143 | 144 | 于是 `Point` 和 `(u8, u8)` 也能相互转换, `tuple` 其实就是一个 `untagged class` 145 | 146 | 数学上集合的积叫做**笛卡尔积** (Cartesian Product), 大小相当于所有子集合大小的乘积. 147 | 148 | 所以类型论里管这种结构叫**积类型** (Product Type) 149 | 150 | ### Function type 151 | 152 | 作为 first class function 的语言, 函数也是一种类型, 那么函数类型对应的数如何计算呢? 153 | 154 | 155 | 是的, 我们需要做的就是记下每个可能的实现并计算它们. 156 | 157 | 简单! 例如, 假设我们有一个函数SuitColor映射卡Suit到Color, 红色或黑色. type Suit = Heart | Spade | Diamond | Club 158 | type Color = Red | Black 159 | 160 | type SuitColor = Suit -> Color无论提供什么样的诉讼, 一种方法是返回红色: (Heart -> Red); (Spade -> Red); (Diamond -> Red); (Club -> Red) 161 | 162 | swap :: (a, b) -> (b, a) 163 | 164 | 165 | ## Generics Type 166 | 167 | 泛型或者说参化多态能让你能根据自定义的需求, 编写出适用于任意类型的、灵活可复用的函数及类型 168 | 169 | 让你避免编写重复的代码, 用一种清晰抽象的方式来表达代码的意图 170 | 171 | ```ts 172 | type Option { 173 | Some(T), 174 | None 175 | } 176 | ``` 177 | 178 | 当填入任意类型时, `Optional` 是一种类型, `Optional` 是一种类型 179 | 180 | 但 `Optional` 不是, 它是一个类型构造函数 181 | 182 | 我们可以认为泛型是作用在类型上的一种特殊的函数, 它接受一种或者多种类型, 返回一种新的类型 183 | 184 | 因此 `Option` 对应多项式函数 $F(t) = t + 1$ 185 | 186 | 同样的我们还有 `Result` 类型, 对应多元多项式函数 $F(t, e) = t + e$ 187 | 188 | ```ts 189 | type Result { 190 | Ok(T), 191 | Fail(E), 192 | } 193 | ``` 194 | 195 | ### Recursive Type 196 | 197 | 有时候在类型的定义中会引用自身, 这就是递归类型. 198 | 199 | 最常见的递归类型是 List, 这里的 List 说的是 lisp 中的 List, 200 | 201 | 虽然 valkyrie 中内置了一个 List类型, 不过那个实际是 `Deque` 202 | 203 | 204 | ```ts 205 | type List { 206 | Nil, 207 | Cons { 208 | car: T, 209 | cdr: List, 210 | } 211 | } 212 | ``` 213 | 214 | 这些标识符就像是来自异世界的一样, 让人摸不着头脑 215 | 216 | - **cons**: 对象构造器 (constructs memory objects) 217 | - **car**: 暂存器位址内容 (content of address register) 218 | - **cdr**: 递减暂存器内容 (content of decrement register) 219 | - **nil**: 拉丁语, 等价于英语中的 nothing 220 | 221 | 任何非空的列表, 都可以被视为一对由列表第一个元素及列表其余元素所组成的列表. 222 | 223 | 我们使用 Cons 的一半来指向列表的第一个元素, 然后用另一半指向列表其余的元素, 可能是别的 Cons 或 nil 224 | 225 | List 是和类型, Nil 是单位类型, Cons 是积类型, 所以可以列出表达式 226 | 227 | $$L(t) = 1 + t * L(t)$$ 228 | 229 | 看起来就像是个方程, 我们可以解出 230 | 231 | $$L(t) = \frac{1}{1-t}$$ 232 | 233 | 看起来有点诡异, 这是什么意思? 234 | 235 | 236 | 再来看二叉树, 237 | 238 | ```ts 239 | type BinaryTree { 240 | Leaf(T), 241 | Node(BinaryTree, BinaryTree), 242 | } 243 | ``` 244 | 245 | Leaf 是泛型, Node 是积类型, 所以可以列出表达式 246 | 247 | $$B(t) = t + B(t)\times B(t)$$ 248 | 249 | $$B(t) = \frac{1-\sqrt{1-4a}}{2}$$ 250 | 251 | 更加诡异了, 怎么开根号都出来了 252 | 253 | 254 | 255 | 256 | ### Differential Type 257 | 258 | 259 | 260 | ### Usage 261 | 262 | 这就是你可以称之为"无损"的转换. 如果您对转换进行往返, 则可以恢复原始值. 数学家会称之为同构 (来自希腊语"相同的形状") . 263 | 264 | 另一个例子怎么样?这是一个包含三种情况的类型, 是, 否, 也许. type YesNoMaybe = 265 | | Yes 266 | | No 267 | | Maybe我们可以无损地将其转换为此类型吗?type YesNoOption = { maybeIsYes: bool option } 268 | 269 | 270 | https://codewords.recurse.com/issues/three/algebra-and-calculus-of-algebraic-data-types 271 | 272 | https://zhuanlan.zhihu.com/p/20570808 -------------------------------------------------------------------------------- /docs/cn/basic/class.md: -------------------------------------------------------------------------------- 1 | ## Declaration 2 | 3 | ```ts 4 | class { 5 | 6 | } 7 | ``` 8 | 9 | 10 | 11 | 构造体和我们在第三章讨论过的元组类似. 和元组一样, 构造体的每一部分可以是不同类型. 但不同于元组, 构造体需要命名各部分数据以便能清楚的表明其值的意义. 由于有了这些名字, 构造体比元组更灵活: 不需要依赖顺序来指定或访问实例中的值. 12 | 13 | 定义构造体, 需要使用 `struct` 关键字并为整个构造体提供一个名字. 构造体的名字需要描述它所组合的数据的意义. 14 | 15 | 接着, 在大括号中, 定义每一部分数据的名字和类型, 我们称为 **字段** (*field*) . 例如, 示例 5-1 展示了一个存储用户账号信息的构造体: 16 | 17 | ```ts 18 | class User { 19 | username: str, 20 | email: str, 21 | vip: bool, 22 | score: u64 23 | } 24 | ``` 25 | 26 | ## New Object 27 | 28 | 为了使用构造体, 通过为每个字段指定具体值来创建这个构造体的 **实例**. 29 | 30 | 创建一个实例需要以构造体的名字开头, 接着在大括号中使用 `key: value` 键-值对的形式提供字段, 其中 key 是字段的名字, value 是需要存储在字段中的数据值. 实例中字段的顺序不需要和它们在构造体中声明的顺序一致. 换句话说, 构造体的定义就像一个类型的通用模板, 而实例则会在这个模板中放入特定数据来创建这个类型的值. 例如, 可以像示例 5-2 这样来声明一个特定的用户: 31 | 32 | ```ts 33 | new User { 34 | username= "valkyrie", 35 | email= "valkyrie@nyar.org", 36 | score= 0, 37 | vip= false, 38 | }; 39 | ``` 40 | 41 | 42 | ## Constructor 43 | 44 | 为了从构造体中获取某个特定的值, 可以使用点号. 如果我们只想要用户的邮箱地址, 可以用 `user1.email`. 要更改构造体中的值, 如果构造体的实例是可变的, 我们可以使用点号并为对应的字段赋值. 示例 5-3 展示了如何改变一个可变的 `User` 实例 `email` 字段的值: 45 | 46 | ```valkyrie 47 | # struct User { 48 | # username: String, 49 | # email: String, 50 | # sign_in_count: u64, 51 | # active: bool, 52 | # } 53 | # 54 | let mut user1 = User { 55 | email: String::from("someone@example.com"), 56 | username: String::from("someusername123"), 57 | active: true, 58 | sign_in_count: 1, 59 | }; 60 | 61 | user1.email = String::from("anotheremail@example.com"); 62 | ``` 63 | 64 | 示例 5-3: 改变 `User` 实例 `email` 字段的值 65 | 66 | 注意整个实例必须是可变的;Rust 并不允许只将某个字段标记为可变. 另外需要注意同其他任何表达式一样, 我们可以在函数体的最后一个表达式中构造一个构造体的新实例, 来隐式地返回这个实例. 67 | 68 | 示例 5-4 显示了一个 `build_user` 函数, 它返回一个带有给定的 email 和用户名的 `User` 构造体实例. `active` 字段的值为 `true`, 并且 `sign_in_count` 的值为 `1`. 69 | 70 | ```valkyrie 71 | # struct User { 72 | # username: String, 73 | # email: String, 74 | # sign_in_count: u64, 75 | # active: bool, 76 | # } 77 | # 78 | fn build_user(email: String, username: String) -> User { 79 | User { 80 | email: email, 81 | username: username, 82 | active: true, 83 | sign_in_count: 1, 84 | } 85 | } 86 | ``` 87 | 88 | 示例 5-4: `build_user` 函数获取 email 和用户名并返回 `User` 实例 89 | 90 | 为函数参数起与构造体字段相同的名字是可以理解的, 但是不得不重复 `email` 和 `username` 字段名称与变量有些啰嗦. 如果构造体有更多字段, 重复每个名称就更加烦人了. 幸运的是, 有一个方便的简写语法! 91 | 92 | ### Shorthand Constructor 93 | 94 | 因为示例 5-4 中的参数名与字段名都完全相同, 我们可以使用 **字段初始化简写语法** (*field init shorthand*) 来重写 `build_user`, 这样其行为与之前完全相同, 不过无需重复 `email` 和 `username` 了, 如示例 5-5 所示. 95 | 96 | ```valkyrie 97 | class User { 98 | username: str, 99 | email: str, 100 | vip: bool, 101 | score: u64 102 | } 103 | 104 | fn new_user(email: String, username: String) -> User { 105 | User { 106 | email, 107 | username, 108 | active: true, 109 | sign_in_count: 1, 110 | } 111 | } 112 | ``` 113 | 114 | 示例 5-5: `build_user` 函数使用了字段初始化简写语法, 因为 `email` 和 `username` 参数与构造体字段同名 115 | 116 | 这里我们创建了一个新的 `User` 构造体实例, 它有一个叫做 `email` 的字段. 我们想要将 `email` 字段的值设置为 `build_user` 函数 `email` 参数的值. 因为 `email` 字段与 `email` 参数有着相同的名称, 则只需编写 `email` 而不是 `email: email`. 117 | 118 | ### Update Constructor 119 | 120 | 使用旧实例的大部分值但改变其部分值来创建一个新的构造体实例通常是很有帮助的. 这可以通过 **构造体更新语法** (*struct update syntax*) 实现. 121 | 122 | 首先, 示例 5-6 展示了不使用更新语法时, 如何在 `user2` 中创建一个新 `User` 实例. 我们为 `email` 和 `username` 设置了新的值, 其他值则使用了实例 5-2 中创建的 `user1` 中的同名值: 123 | 124 | ```valkyrie 125 | # struct User { 126 | # username: String, 127 | # email: String, 128 | # sign_in_count: u64, 129 | # active: bool, 130 | # } 131 | # 132 | # let user1 = User { 133 | # email: String::from("someone@example.com"), 134 | # username: String::from("someusername123"), 135 | # active: true, 136 | # sign_in_count: 1, 137 | # }; 138 | # 139 | let user2 = User { 140 | email: String::from("another@example.com"), 141 | username: String::from("anotherusername567"), 142 | active: user1.active, 143 | sign_in_count: user1.sign_in_count, 144 | }; 145 | ``` 146 | 147 | 示例 5-6: 创建 `User` 新实例, 其使用了一些来自 `user1` 的值 148 | 149 | 使用构造体更新语法, 我们可以通过更少的代码来达到相同的效果, 如示例 5-7 所示. `..` 语法指定了剩余未显式设置值的字段应有与给定实例对应字段相同的值. 150 | 151 | ```ts 152 | # struct User { 153 | # username: String, 154 | # email: String, 155 | # sign_in_count: u64, 156 | # active: bool, 157 | # } 158 | # 159 | # let user1 = User { 160 | # email: String::from("someone@example.com"), 161 | # username: String::from("someusername123"), 162 | # active: true, 163 | # sign_in_count: 1, 164 | # }; 165 | # 166 | let user2 = new User { 167 | email: String::from("another@example.com"), 168 | username: String::from("anotherusername567"), 169 | ..user1 170 | }; 171 | ``` 172 | 173 | 示例 5-7: 使用构造体更新语法为一个 `User` 实例设置新的 `email` 和 `username` 值, 不过其余值来自 `user1` 变量中实例的字段 174 | 175 | 示例 5-7 中的代码也在 `user2` 中创建了一个新实例, 其有不同的 `email` 和 `username` 值不过 `active` 和 `sign_in_count` 字段的值与 `user1` 相同. 176 | 177 | ## Tuple Class 178 | 179 | 有时候你只想把几个值简单的套在一起, 那么可以使用**元组构造体** 180 | 181 | 元组构造体有类型名, 但不用标注字段名以及初始化, 只需要标注字段的类型 182 | 183 | 元组构造体的字段没有访问权限控制也不用默认初始化, 更不用定义构造函数, 随时定义随时使用. 184 | 185 | ```ts 186 | class public ColorRGB(u8, u8, u8); 187 | class public Point3D(u8, u8, u8); 188 | 189 | let black = ColorRGB(0, 0, 0); 190 | let origin = Point3D(0, 0, 0); 191 | ``` 192 | 193 | 每一个构造体有其自己的类型, 即使构造体中的字段有着相同的类型. 194 | 195 | 一个获取 `Color` 类型的函数不能接受 `Point` 作为参数, 即便这两个类型都由三个 `u8` 值组成 196 | 197 | ## Empty Class 198 | 199 | ```ts 200 | class Empty; 201 | class EmptyTuple(); 202 | class EmptyClass{}; 203 | class EmptyEmpty { 204 | empty: Empty, 205 | unit: () 206 | } 207 | ``` 208 | 209 | 这类构造体如果放在内存上并不占据任何字节, 因此也被称为零宽类型 (Zero-Sized Type) 210 | 211 | 这会导致 Valkyrie 无法正常管理内存, 因此无法初始化. 212 | -------------------------------------------------------------------------------- /docs/cn/basic/pattern-match.md: -------------------------------------------------------------------------------- 1 | ## `match` 控制流运算符 2 | 3 | > [ch06-02-match.md](https://github.com/rust-lang/book/blob/master/src/ch06-02-match.md) 4 | >
5 | > commit b374e75f1d7b743c84a6bb1ef72579a6588bcb8a 6 | 7 | Rust 有一个叫做 `match` 的极为强大的控制流运算符, 它允许我们将一个值与一系列的模式相比较, 并根据相匹配的模式执行相应代码. 模式可由字面值、变量、通配符和许多其他内容构成;第十八章会涉及到所有不同种类的模式以及它们的作用. `match` 的力量来源于模式的表现力以及编译器检查, 它确保了所有可能的情况都得到处理. 8 | 9 | 可以把 `match` 表达式想象成某种硬币分类器: 硬币滑入有着不同大小孔洞的轨道, 每一个硬币都会掉入符合它大小的孔洞. 同样地, 值也会通过 `match` 的每一个模式, 并且在遇到第一个 "符合" 的模式时, 值会进入相关联的代码块并在执行中被使用. 10 | 11 | 因为刚刚提到了硬币, 让我们用它们来作为一个使用 `match` 的例子! 我们可以编写一个函数来获取一个未知的 (美帝) 硬币, 并以一种类似验钞机的方式, 确定它是何种硬币并返回它的美分值, 如示例 6-3 中所示. 12 | 13 | ```valkyrie 14 | enum Coin { 15 | Penny, 16 | Nickel, 17 | Dime, 18 | Quarter, 19 | } 20 | 21 | fn value_in_cents(coin: Coin) -> u8 { 22 | match coin { 23 | Coin::Penny => 1, 24 | Coin::Nickel => 5, 25 | Coin::Dime => 10, 26 | Coin::Quarter => 25, 27 | } 28 | } 29 | ``` 30 | 31 | 示例 6-3: 一个枚举和一个以枚举成员作为模式的 `match` 表达式 32 | 33 | 拆开 `value_in_cents` 函数中的 `match` 来看. 首先, 我们列出 `match` 关键字后跟一个表达式, 在这个例子中是 `coin` 的值. 这看起来非常像 `if` 使用的表达式, 不过这里有一个非常大的区别: 对于 `if`, 表达式必须返回一个布尔值, 而这里它可以是任何类型的. 例子中的 `coin` 的类型是示例 6-3 中定义的 `Coin` 枚举. 34 | 35 | 接下来是 `match` 的分支. 一个分支有两个部分: 一个模式和一些代码. 第一个分支的模式是值 `Coin::Penny` 而之后的 `=>` 运算符将模式和将要运行的代码分开. 这里的代码就仅仅是值 `1`. 每一个分支之间使用逗号分隔. 36 | 37 | 当 `match` 表达式执行时, 它将结果值按顺序与每一个分支的模式相比较. 如果模式匹配了这个值, 这个模式相关联的代码将被执行. 如果模式并不匹配这个值, 将继续执行下一个分支, 非常类似一个硬币分类器. 可以拥有任意多的分支: 示例 6-3 中的 `match` 有四个分支. 38 | 39 | 每个分支相关联的代码是一个表达式, 而表达式的结果值将作为整个 `match` 表达式的返回值. 40 | 41 | 如果分支代码较短的话通常不使用大括号, 正如示例 6-3 中的每个分支都只是返回一个值. 如果想要在分支中运行多行代码, 可以使用大括号. 例如, 如下代码在每次使用`Coin::Penny` 调用时都会打印出 "Lucky penny!", 同时仍然返回代码块最后的值, `1`: 42 | 43 | ```valkyrie 44 | # enum Coin { 45 | # Penny, 46 | # Nickel, 47 | # Dime, 48 | # Quarter, 49 | # } 50 | # 51 | fn value_in_cents(coin: Coin) -> u8 { 52 | match coin { 53 | Coin::Penny => { 54 | println!("Lucky penny!"); 55 | 1 56 | }, 57 | Coin::Nickel => 5, 58 | Coin::Dime => 10, 59 | Coin::Quarter => 25, 60 | } 61 | } 62 | ``` 63 | 64 | ### 绑定值的模式 65 | 66 | 匹配分支的另一个有用的功能是可以绑定匹配的模式的部分值. 这也就是如何从枚举成员中提取值的. 67 | 68 | 作为一个例子, 让我们修改枚举的一个成员来存放数据. 1999 年到 2008 年间, 美帝在 25 美分的硬币的一侧为 50 个州的每一个都印刷了不同的设计. 其他的硬币都没有这种区分州的设计, 所以只有这些 25 美分硬币有特殊的价值. 可以将这些信息加入我们的 `enum`, 通过改变 `Quarter` 成员来包含一个 `State` 值, 示例 6-4 中完成了这些修改: 69 | 70 | ```valkyrie 71 | #[derive(Debug)] # 这样可以可以立刻看到州的名称 72 | enum UsState { 73 | Alabama, 74 | Alaska, 75 | # --snip-- 76 | } 77 | 78 | enum Coin { 79 | Penny, 80 | Nickel, 81 | Dime, 82 | Quarter(UsState), 83 | } 84 | ``` 85 | 86 | 示例 6-4: `Quarter` 成员也存放了一个 `UsState` 值的 `Coin` 枚举 87 | 88 | 想象一下我们的一个朋友尝试收集所有 50 个州的 25 美分硬币. 在根据硬币类型分类零钱的同时, 也可以报告出每个 25 美分硬币所对应的州名称, 这样如果我们的朋友没有的话, 他可以将其加入收藏. 89 | 90 | 在这些代码的匹配表达式中, 我们在匹配 `Coin::Quarter` 成员的分支的模式中增加了一个叫做 `state` 的变量. 当匹配到 `Coin::Quarter` 时, 变量 `state` 将会绑定 25 美分硬币所对应州的值. 接着在那个分支的代码中使用 `state`, 如下: 91 | 92 | ```valkyrie 93 | # #[derive(Debug)] 94 | # enum UsState { 95 | # Alabama, 96 | # Alaska, 97 | # } 98 | # 99 | # enum Coin { 100 | # Penny, 101 | # Nickel, 102 | # Dime, 103 | # Quarter(UsState), 104 | # } 105 | # 106 | fn value_in_cents(coin: Coin) -> u8 { 107 | match coin { 108 | Coin::Penny => 1, 109 | Coin::Nickel => 5, 110 | Coin::Dime => 10, 111 | Coin::Quarter(state) => { 112 | println!("State quarter from {:?}!", state); 113 | 25 114 | }, 115 | } 116 | } 117 | ``` 118 | 119 | 如果调用 `value_in_cents(Coin::Quarter(UsState::Alaska))`, `coin` 将是 `Coin::Quarter(UsState::Alaska)`. 当将值与每个分支相比较时, 没有分支会匹配, 直到遇到 `Coin::Quarter(state)`. 这时, `state` 绑定的将会是值 `UsState::Alaska`. 接着就可以在 `println!` 表达式中使用这个绑定了, 像这样就可以获取 `Coin` 枚举的 `Quarter` 成员中内部的州的值. 120 | 121 | ### 匹配 `Option` 122 | 123 | 我们在之前的部分中使用 `Option` 时, 是为了从 `Some` 中取出其内部的 `T` 值;我们还可以像处理 `Coin` 枚举那样使用 `match` 处理 `Option`! 与其直接比较硬币, 我们将比较 `Option` 的成员, 不过 `match` 表达式的工作方式保持不变. 124 | 125 | 比如我们想要编写一个函数, 它获取一个 `Option` 并且如果其中有一个值, 将其加一. 如果其中没有值, 函数应该返回 `None` 值并不尝试执行任何操作. 126 | 127 | 得益于 `match`, 编写这个函数非常简单, 它将看起来像示例 6-5 中这样: 128 | 129 | ```valkyrie 130 | fn plus_one(x: Option) -> Option { 131 | match x { 132 | None => None, 133 | Some(i) => Some(i + 1), 134 | } 135 | } 136 | 137 | let five = Some(5); 138 | let six = plus_one(five); 139 | let none = plus_one(None); 140 | ``` 141 | 142 | 示例 6-5: 一个在 `Option` 上使用 `match` 表达式的函数 143 | 144 | #### 匹配 `Some(T)` 145 | 146 | 让我们更仔细地检查 `plus_one` 的第一行操作. 当调用 `plus_one(five)` 时, `plus_one` 函数体中的 `x` 将会是值 `Some(5)`. 接着将其与每个分支比较. 147 | 148 | ```valkyrie 149 | None => None, 150 | ``` 151 | 152 | 值 `Some(5)` 并不匹配模式 `None`, 所以继续进行下一个分支. 153 | 154 | ```valkyrie 155 | Some(i) => Some(i + 1), 156 | ``` 157 | 158 | `Some(5)` 与 `Some(i)` 匹配吗?当然匹配! 它们是相同的成员. `i` 绑定了 `Some` 中包含的值, 所以 `i` 的值是 `5`. 接着匹配分支的代码被执行, 所以我们将 `i` 的值加一并返回一个含有值 `6` 的新 `Some`. 159 | 160 | 接着考虑下示例 6-5 中 `plus_one` 的第二个调用, 这里 `x` 是 `None`. 我们进入 `match` 并与第一个分支相比较. 161 | 162 | ```valkyrie 163 | None => None, 164 | ``` 165 | 166 | 匹配上了! 这里没有值来加一, 所以程序结束并返回 `=>` 右侧的值 `None`, 因为第一个分支就匹配到了, 其他的分支将不再比较. 167 | 168 | 将 `match` 与枚举相结合在很多场景中都是有用的. 你会在 Rust 代码中看到很多这样的模式: `match` 一个枚举, 绑定其中的值到一个变量, 接着根据其值执行代码. 这在一开始有点复杂, 不过一旦习惯了, 你会希望所有语言都拥有它! 这一直是用户的最爱. 169 | 170 | ### 匹配是穷尽的 171 | 172 | `match` 还有另一方面需要讨论. 考虑一下 `plus_one` 函数的这个版本, 它有一个 bug 并不能编译: 173 | 174 | ```valkyrie 175 | fn plus_one(x: Option) -> Option { 176 | match x { 177 | Some(i) => Some(i + 1), 178 | } 179 | } 180 | ``` 181 | 182 | 我们没有处理 `None` 的情况, 所以这些代码会造成一个 bug. 幸运的是, 这是一个 Rust 知道如何处理的 bug. 如果尝试编译这段代码, 会得到这个错误: 183 | 184 | ```text 185 | error[E0004]: non-exhaustive patterns: `None` not covered 186 | --> 187 | | 188 | 6 | match x { 189 | | ^ pattern `None` not covered 190 | ``` 191 | 192 | Rust 知道我们没有覆盖所有可能的情况甚至知道哪些模式被忘记了! Rust 中的匹配是 **穷尽的** (*exhaustive*) : 必须穷举到最后的可能性来使代码有效. 特别的在这个 `Option` 的例子中, Rust 防止我们忘记明确的处理 `None` 的情况, 这使我们免于假设拥有一个实际上为空的值, 这造成了之前提到过的价值亿万的错误. 193 | 194 | ### `_` 通配符 195 | 196 | Rust 也提供了一个模式用于不想列举出所有可能值的场景. 例如, `u8` 可以拥有 0 到 255 的有效的值, 如果我们只关心 1、3、5 和 7 这几个值, 就并不想必须列出 0、2、4、6、8、9 一直到 255 的值. 所幸我们不必这么做: 可以使用特殊的模式 `_` 替代: 197 | 198 | ```valkyrie 199 | let some_u8_value = 0u8; 200 | match some_u8_value { 201 | 1 => println!("one"), 202 | 3 => println!("three"), 203 | 5 => println!("five"), 204 | 7 => println!("seven"), 205 | _ => (), 206 | } 207 | ``` 208 | 209 | `_` 模式会匹配所有的值. 通过将其放置于其他分支之后, `_` 将会匹配所有之前没有指定的可能的值. `()` 就是 unit 值, 所以 `_` 的情况什么也不会发生. 因此, 可以说我们想要对 `_` 通配符之前没有列出的所有可能的值不做任何处理. 210 | 211 | 然而, `match` 在只关心 **一个** 情况的场景中可能就有点啰嗦了. 为此 Rust 提供了`if let`. 212 | -------------------------------------------------------------------------------- /License.md: -------------------------------------------------------------------------------- 1 | ## creative commons 2 | 3 | # CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. 6 | 7 | ### Statement of Purpose 8 | 9 | The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). 10 | 11 | Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. 12 | 13 | For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 14 | 15 | 1. __Copyright and Related Rights.__ A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: 16 | 17 | i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; 18 | 19 | ii. moral rights retained by the original author(s) and/or performer(s); 20 | 21 | iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; 22 | 23 | iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; 24 | 25 | v. rights protecting the extraction, dissemination, use and reuse of data in a Work; 26 | 27 | vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and 28 | 29 | vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 30 | 31 | 2. __Waiver.__ To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 32 | 33 | 3. __Public License Fallback.__ Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 34 | 35 | 4. __Limitations and Disclaimers.__ 36 | 37 | a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. 38 | 39 | b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. 40 | 41 | c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. 42 | 43 | d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. 44 | -------------------------------------------------------------------------------- /docs/en/definition/structure.md: -------------------------------------------------------------------------------- 1 | ## Class 2 | 3 | The `class` keyword is used to define a class in Valkyrie. 4 | 5 | It allows you to encapsulate data and methods into a single entity. 6 | 7 | Here's an example of a class definition: 8 | 9 | ```valkyrie 10 | class A { 11 | field: Type = default_value; 12 | method(): Return { 13 | method_body 14 | } 15 | } 16 | ``` 17 | 18 | In the above code snippet, we define a class named `A`. 19 | 20 | It has a single field named `field` of type `Type` with a default value. 21 | 22 | The `field` can be accessed and modified by the methods defined within the class. 23 | 24 | ## Private Fields 25 | 26 | In Valkyrie, private fields in a class can be denoted by using an underscore (`_`) prefix. These fields are intended to be accessed and modified only within the class itself. Here's an example: 27 | 28 | ```valkyrie 29 | class A { 30 | _field: Type = default_value; 31 | } 32 | ``` 33 | 34 | In the above code snippet, we define a class `A` with a private field `_field` of type `Type` and a default value. The underscore prefix convention is commonly used to indicate that the field is private and should not be accessed directly from outside the class. 35 | 36 | ## Property 37 | 38 | Properties in Valkyrie allow you to define custom getter and setter methods for accessing and modifying class fields. Here's an example: 39 | 40 | ```valkyrie 41 | class A { 42 | _field: Type = default_value; 43 | get field(self): Type { 44 | self._field 45 | } 46 | set field(self, value: Type) { 47 | self._field = value 48 | } 49 | } 50 | ``` 51 | 52 | In the above code snippet, we define a class `A` with a private field `_field`. We also define a property `field` with a custom getter and setter. The getter method (`get field()`) returns the value of `_field`, and the setter method (`set field(value)`) sets the value of `_field` to the provided value. By using properties, we can encapsulate the access and modification of the field and add any additional logic if needed. 53 | 54 | ## Property Derive 55 | 56 | In Valkyrie, properties can be derived automatically using attributes. The `#[get, set]` attribute is used to generate the default getter and setter methods for a field. Here's an example: 57 | 58 | ```valkyrie 59 | class A { 60 | #[get, set] 61 | _field: Type = default_value; 62 | } 63 | ``` 64 | 65 | In the above code snippet, we define a class `A` with a private field `_field_name`. 66 | 67 | By applying the `#[get, set]` attribute to `_field_name`, Valkyrie automatically generates the default getter and setter methods for the field. 68 | 69 | This provides a convenient way to define properties without explicitly writing the getter and setter methods. 70 | 71 | ### Static Method 72 | 73 | If the first argument of a method in a class is not `self`, then it is considered a static method. Static methods can be called directly on the class itself using the `::` syntax. Here's an example: 74 | 75 | ```valkyrie 76 | extends A { 77 | // static method 78 | method1(): String { 79 | "call method1" 80 | } 81 | } 82 | ``` 83 | 84 | In the above code snippet, we extend the class `A` and define a static method named `method1()`. This method can be called as `A::method1()`. 85 | 86 | ## Instance Method 87 | 88 | In Valkyrie, instance methods are associated with specific instances of a class and can be called on those instances. Here are two examples of instance methods: 89 | 90 | ```valkyrie 91 | extends A { 92 | /// instance method 93 | method2(self): String { 94 | self.field.to_string() 95 | } 96 | } 97 | ``` 98 | 99 | In the above code snippet, we extend the class `A` and define an instance method named `method2()`. 100 | 101 | This method takes `self` as the first argument, which represents the instance on which the method is called. 102 | 103 | Within the method, we access the `field` of the instance using `self.field` and convert it to a string using the `to_string()` method. 104 | 105 | The method returns the string representation of the `field`. 106 | 107 | ```valkyrie 108 | extends A { 109 | /// instance method 110 | method3(mut self, value): String { 111 | self.set_value(value) 112 | } 113 | } 114 | ``` 115 | 116 | In the above code snippet, we extend the class `A` and define another instance method named `method3()`. 117 | 118 | These instance methods can be called on instances of the class `A` by using the dot notation. 119 | 120 | For example, if we have an instance named `a` of class `A`, we can call `a.method2()` and `a.method3(value)` or `A::method3(a, value)` to execute the respective methods. 121 | 122 | ## Structure 123 | 124 | The `structure` keyword is used to define a structure (similar to a struct) in Valkyrie. Structures allow you to group related data together. Here's an example of a structure definition: 125 | 126 | ```valkyrie 127 | structure B { 128 | field: Type = default_value; 129 | } 130 | ``` 131 | 132 | In the above code snippet, we define a structure named `B`. It has a single field named `field` of type `Type` with a default value. The fields of a structure can be accessed and modified directly. 133 | 134 | ### Parameter Passing Comparison 135 | 136 | When passing parameters to functions or methods, there are different ways to handle the ownership and mutability of the value being passed. The table below compares the parameter passing behavior between classes (`class A`) and structures (`structure B`): 137 | 138 | 139 | | Parameter | `class A` | `structure B` | 140 | |-------------|---------------|------------------| 141 | | `x: X` | pass `&x` | pass `x.clone()` | 142 | | `ref x: X` | pass `&x` | pass `&x` | 143 | | `mut x: X` | pass `&mut x` | pass `move x` | 144 | | `move x: X` | pass `move x` | pass `move x` | 145 | 146 | In the case of `class A`, when passing a parameter `x` of type `X` without any additional keywords, the ownership of `x` is not transferred. 147 | 148 | Instead, a reference to `x` (`&X`) is passed. If the parameter is declared as `ref x: X`, a reference to `x` (`&X`) is passed explicitly. 149 | 150 | Lastly, for `mut x: X`, a mutable reference to `x` (`&mut X`) is passed, allowing the function or method to modify `x`. 151 | 152 | For `structure B`, the behavior is slightly different. 153 | 154 | When passing a parameter `x` of type `X` without any additional keywords, a clone of `x` (`x.clone()`) is passed, transferring the ownership. 155 | 156 | If the parameter is declared as `ref x: X`, a reference to `x` (`&x`) is passed, without transferring ownership. 157 | 158 | Lastly, for `mut x: X`, the ownership of `x` is moved to the function or method using the `move` keyword. 159 | 160 | These differences in parameter passing can be useful in different scenarios depending on the desired ownership and mutability semantics. 161 | 162 | ## Implementation Details 163 | 164 | The structure will be compiled into a `Record`, and all fields are regarded as row type `field: (self) -> T`. 165 | 166 | ```valkyrie 167 | type A = [ 168 | field: (self) -> Type, 169 | delay: (self) -> Lazy, 170 | method1: () -> String, 171 | method2: (self) -> String, 172 | ] 173 | ``` 174 | 175 | This means that the two calls `a.b` and `a.b()` are indistinguishable. 176 | 177 | For convenience, leave out the parentheses if `b` is like a noun, and add parentheses if `b` is like a verb. 178 | -------------------------------------------------------------------------------- /docs/cn/Readme.vk: -------------------------------------------------------------------------------- 1 | # Valkyrie Language 2 | 3 | 4 | ## 设计目标 5 | 6 | Valkyrie 是一门具有渐进类型的动态语言, 7 | 8 | Valkyrie 的设计目标是灵活, 简洁以及高度的一致性 9 | 10 | 其血缘来自 Rust, Scala 以及 Swift. 11 | 12 | 接下来介绍 Valkyrie 的主要特性 13 | 14 | ## 扩展 15 | 16 | 如果没有扩展, 那么 V 是一门很死板很严格的语言. 17 | 18 | 严格的令人难受, `1 + 1.0`, `"a" + 'b'`, `[1] ++ 2`, 统统算不了. 19 | 20 | 扩展赋予了 V 高度的灵活性与可能性. 21 | 22 | 默认会载入名为 Prelude 的扩展, Prelude 中定义了一些常用的合理的运算规则. 23 | 24 | extension 可以继承和重写, 你可以也定义自己的 Prelude, 然后形成自己的代码风格. 25 | 26 | V 中所有的语法糖都由对应的 trait 来静态决策, 以此来免除不必要运行时开销 27 | 28 | ### 孤儿规则 29 | 30 | 如果你想扩展一个方法, 要么类是在你的库中定义的, 要么特质是在你的库中定义的 31 | 32 | 否则只能在 extension package namespace 中修改 33 | 34 | 这一规则是为了防止在不知情的情况下引入未知的语义 35 | 36 | ````valkyrie 37 | /// Namespace is generally lowercase, but extension is uppercase 38 | /// Because the performance is similar to the introduce a trait 39 | pkg extension Features; 40 | ```` 41 | 42 | ### 符号重载 43 | 44 | 最常见的应用是符号重载, 当你要整合两个第三方库类型转换时, 经常会被孤儿规则禁止. 45 | 46 | 因此需要声明 extension 来实现兼容. 47 | 48 | 还有一种情况是某个运算是方便的, 但可能不是很严格. 49 | 50 | 比如我们一般默认加法服从交换律, 但是 `"a" + "b" != "b" + "a"` 破坏了交换律. 51 | 52 | 于是可以将选择权交给库用户. 53 | 54 | ````valkyrie 55 | /// Otherwise, all it will inherit all extensions in the current namespace 56 | @no_prelude 57 | pkg extension FreeAdd { 58 | /// Symbols are defined in the standard library 59 | /// Types are also defined in the standard library 60 | /// Overloading is prohibited by the orphan rule 61 | /// Ordinary overload does not need extension 62 | def +(i: Integer, f: Decimal) { 63 | /// The function will return automatically 64 | /// The return type is automatically inferred 65 | i as Decimal + f 66 | } 67 | } 68 | ```` 69 | 70 | ### 数字模式 71 | 72 | 有时候我们想输入一些特殊的数值, 比如带单位的运算, 如果用构造器很可能让代码失去可读性. 73 | 74 | 这种情况下可以用数字模式, 数字模式是后缀形式的. 75 | 76 | ````valkyrie 77 | tagged Quality { 78 | Kilogram(auto Decimal), 79 | Gram(auto Decimal), 80 | } 81 | def +(lhs: Quality, rhg: Quality): Quality { 82 | /// Some conversion rules, that's it, anyone can write 83 | @unimplemented() 84 | } 85 | 86 | pkg extension SIUnit { 87 | /// number_suffix means integer_suffix + decimal_suffix 88 | def number_suffix kg(n): Quality = Quality::Kilogram(n); 89 | def number_suffix g(n): Quality = Quality::Gram(n); 90 | } 91 | ```` 92 | 93 | 然后你就可以写如下写法了! 94 | 95 | ````valkyrie 96 | use SIUnit; 97 | let weight = 1kg + 1g; 98 | ```` 99 | 100 | 以此类推, 你应该能很轻易的实现下列功能, 这就是 Valkyrie 一致性的体现. 101 | 102 | ````valkyrie 103 | use Complex; 104 | let z = 1 + 2i; 105 | /// Note that you cannot bind `i` twice in one namespace 106 | use Quaternion; 107 | let q = 1 + 2i + 3j + 4k; 108 | ```` 109 | 110 | ### 字符串模式 111 | 112 | 字符串也是个很复杂的东西, 众口难调, 不同的人喜欢不同的写法 113 | 114 | 好在我们有字符串模式, 字符串模式是前缀表达. 115 | 116 | 如果什么前缀也没有, 默认的字符串叫 s-string(slot). 117 | 118 | ````valkyrie 119 | r"raw-string: ${x + 1}\n" 120 | s"slot-string: ${x + 1}\n" 121 | f"format-string: %s %f\s \n" 122 | ```` 123 | 124 | 同样的, 你也可以用 `extension` 扩展你自己的字符串模式. 125 | 126 | 你已经猜到了, 使用 `string_prefix`, 这也是一致性的体现. 127 | 128 | 你能想到什么应用? 嵌入 json 对象? 嵌入 css 样式? 或者更炫酷的用法? 129 | 130 | 131 | ## 模式匹配 132 | 133 | 越来越多的语言采用模式匹配或者类似的语法, 模式匹配快成每个语言的标配了. 134 | 135 | V 当然也有个高度一致, 可 desugar 的灵活模式匹配语法 136 | 137 | ### 字面量匹配 138 | 139 | 字面量匹配是标配 140 | 141 | ````valkyrie 142 | match x { 143 | case 1 => "integer" 144 | case 1.0 => "decimal" 145 | case '1' => "character" 146 | case "1" => "string" 147 | case 1...4 => "range" 148 | case (1, 2) => "tuple" 149 | case [1, 2] => "list" 150 | _ => "something else" 151 | } 152 | ```` 153 | 154 | ### case 守卫 155 | 156 | 有时候模式匹配量需要一点条件判定, 这种情况下可以用 case 守卫 157 | 158 | ````valkyrie 159 | match x { 160 | case x is Integer => "x is an instance of type `Integer`" 161 | case x is Callablte => "x satisfies the trait bound `Callable`" 162 | case x in [1, 2, 3] => "x is one of [1, 2, 3]" 163 | case x if x < 0 => "x satisfies the condition x < 0" 164 | _ => "none of the above conditions are met" 165 | } 166 | ```` 167 | 168 | ### case 解构 169 | 170 | 某些时候想要匹配某一段数据, 那么可以用 case 解构 171 | 172 | ````valkyrie 173 | if case Point {x: a, y, ...p} = Point {x: 1, y: 2, z: 3, w: 4,} { 174 | print(a) /// 1 175 | print(y) /// 2 176 | print(p) /// {z: 3, w: 4} 177 | } 178 | 179 | if case Point(a, ..p, y) = Point(1, 2, 3, 4) { 180 | print(a) /// 1 181 | print(p) /// [2, 3] 182 | print(y) /// 4 183 | } 184 | ```` 185 | 186 | ### 自定义提取器 187 | 188 | 对于自定义的类, 可以定义 unapply 方法来自定义提取的数据. 189 | 190 | 191 | ````valkyrie 192 | match input { 193 | case Regex(group0) => Integer::parse(group0), 194 | } 195 | /// desugar as unapply 196 | if case Some(group0) = Regex::unapply(input) { 197 | Integer::parse(group0) 198 | } 199 | ```` 200 | 201 | 202 | ## 闭包和匿名函数 203 | 204 | 闭包也是一个现代化的语言必备的特性 205 | 206 | 考虑如下函数 207 | 208 | ````valkyrie 209 | def doulbe_even(x) { 210 | match x { 211 | x if x % 2 => 2 * x, 212 | x => x 213 | } 214 | } 215 | [1, 2, 3, 4].map(doulbe_even) 216 | ```` 217 | 218 | 如果一个函数接受的最后一个参数是闭包, 那么可以省略 `()` 219 | 220 | 现在考虑把它写成匿名函数 221 | 222 | ````valkyrie 223 | [1, 2, 3, 4].map { 224 | /// `(lambda (x) expr)` even longer than python 🤣 225 | lambda (x) match x { 226 | x if x % 2 => 2 * x, 227 | x => x 228 | } 229 | } 230 | ```` 231 | 232 | 这个看起来也太复杂了, 这是完全形式的闭包, 只是作为 desugar 的结果而存在, 实际用不到 233 | 234 | 一般常用的是下面的 case 闭包: 235 | 236 | ````valkyrie 237 | [1, 2, 3, 4].map { 238 | case x if x % 2 => 2 * x, 239 | case x => x 240 | } 241 | ```` 242 | 243 | 还有一种简写方式叫 slot 闭包: 244 | 245 | ````valkyrie 246 | [1, 2, 3, 4].map { 247 | if $0 % 2 {2 * $0} else {$0} 248 | } 249 | /// Looks like perl, maybe the ternary operator should be banned 250 | [1, 2, 3, 4].map {$0 % 2 ? 2 * $0 : $0} 251 | ```` 252 | 253 | 254 | ## 多态 255 | 256 | 有时候我们需要一些多态接口, 比如同时接受字符串和整数的函数 257 | 258 | 侵入性最强但是用起来最方便的是隐式类型转换 259 | 260 | ````valkyrie 261 | /// First define ordinary type conversion 262 | extends Integer: From { 263 | def from(s) { Self::parse(i) } 264 | } 265 | /// `ImplicitFrom` needs to meet trait bound `From` 266 | extends Integer: ImplicitFrom; 267 | 268 | def add_one(input: Integer): Integer { 269 | input + 1 270 | } 271 | /// Found type mismatch, try implicit type conversion 272 | add_one("1") /// 2 273 | ```` 274 | 275 | 其次是显式类型转换, 自动规约输入 276 | 277 | ````valkyrie 278 | def add_one(auto input: Integer): Integer { 279 | input + 1 280 | } 281 | /// Found that the type does not match, call the `from` method 282 | add_one("1") /// 2 283 | ```` 284 | 285 | 比较规范的是使用基于 trait 的泛型静态派发 286 | 287 | ````valkyrie 288 | def add_one(input: T): Integer 289 | for T: Into 290 | { 291 | input.into() + 1 292 | } 293 | ```` 294 | 295 | 以上这些方法都是非限定性的多态, 输入不需要预先确定. 296 | 297 | 但是有时候输入是固定的几类, 这就需要限定性多态. 298 | 299 | 限定性多态可以用并类型和和类型实现, 很多语言里不区分这两个, 甚至混淆了两者. 300 | 301 | 但是仔细想想 `Optional>` 和 `Nullable>` 能一样吗? 302 | 303 | ````valkyrie 304 | /// This is the sum type, tagged union 305 | tagged Optional { 306 | Some, 307 | None, 308 | } 309 | 310 | /// This is the union type, untagged union 311 | class Null {}; 312 | type Nullable = T | Null; 313 | 314 | Optional> ==> Optional> 315 | Nullable> ==> Nullable 316 | ```` 317 | 318 | ````valkyrie 319 | /// sum type matching 320 | def add_one(input: Integer|String): Integer { 321 | let y = match input { 322 | x is Integer => x, 323 | x is String => Integer::parse(x) 324 | } 325 | y + 1 326 | } 327 | 328 | /// union type matching 329 | tagged Canbe { 330 | Integer(Integer) 331 | String(String) 332 | } 333 | 334 | def add_one(input: Canbe): Integer { 335 | let y = match input { 336 | Canbe::Integer(x) => x, 337 | Canbe::String(x) => Integer::parse(x) 338 | } 339 | y + 1 340 | } 341 | ```` 342 | 343 | ## 专门设计的 REPL 模式 344 | 345 | REPL 模式是指类似于 Mathematica, Observable 或者 Jupyter 的工作模式 346 | 347 | ### 时序与非时序模式 348 | 349 | 考虑如下代码: 350 | 351 | ````valkyrie 352 | let second = 1 + first; 353 | let first = 1; 354 | ```` 355 | 356 | 在带时序的语言中, 这应该直接报错, 第一行先于第二行发生, 不能使用一个未声明未初始化的变量 `first`. 357 | 358 | 但是在无时序的语言中, 这是可以实现的, 因为所有声明都在同一时刻发生, 没有谁先谁后. 359 | 360 | 再考虑如下代码: 361 | 362 | ````valkyrie 363 | let a = 1; 364 | let a = 2; 365 | ```` 366 | 367 | 在带时序的语言中, 显然第二次行应该覆盖第一行关于 `a` 的声明. 368 | 369 | 但是在无时序的语言中会报错, 因为所有声明都在同一时刻发生, 没有谁先谁后. 370 | 371 | Valkyrie 中的作用域和闭包是有时序的, 但是也不能重复声明函数 372 | 373 | 而其他的 package, class, trait, tagged 都是无时序的, 因此禁止重复声明 374 | 375 | ### REPL 模式 376 | 377 | 但是在 REPL 模式下, package namespace 是时序的, 可以反复修改一个函数并覆盖掉之前这样很方便调试. 378 | 379 | REPL 模式下所有未声明的变量默认是首次声明的可变变量, 这种设定借鉴了 Mathematica, 也很方便调试 380 | 381 | REPL 模式下可以使用 `use?` 导入一个外部模块并允许反复导入, 以此实现热重载 382 | 383 | 这里 `?` 表示不要加入全局方法表, 因为一旦加入就会污染所有的定义, 这种污染是不可逆的. 384 | 385 | REPL 模式可以使用 `¶` 和 `⁋` 获取历史输入输出, 相当于 Mathematica 的 `In`, `Out` 以及 `%` 386 | 387 | ## 基于 AST 的宏 388 | 389 | 基于 AST 的宏可能没有基于 Token 的宏那么强大, 但是你真的想在一个语言里写完全不同的另一个语言吗? 390 | 391 | 特别是 Token 宏的 IDE 支持性一般特别差, 带 TokenKind 的 AST 宏却能获得非常好的支持. 392 | 393 | ## IR 兼容性 394 | 395 | Valkyrie 的语法并不是绝对稳定的, Valkyrie 会把源文件编译成单个的 IR 文件来屏蔽这种改变, 由此来获得前向兼容性. 396 | 397 | Valkyrie 使用类似 Rust 的 Edition 发布模式. 398 | -------------------------------------------------------------------------------- /docs/.vuepress/config.ts: -------------------------------------------------------------------------------- 1 | import {SidebarConfig, SiteLocaleConfig} from "vuepress"; 2 | import {SidebarConfigArray} from "@vuepress/theme-default/lib/shared/nav"; 3 | 4 | import {defineUserConfig, defaultTheme} from 'vuepress' 5 | import {shikiPlugin} from "@vuepress/plugin-shiki"; 6 | import {IGrammar} from "vscode-textmate"; 7 | import * as path from "path"; 8 | 9 | 10 | const locales: SiteLocaleConfig = { 11 | '/cn/': { 12 | lang: 'zh-CN', 13 | title: 'Valkyrie 语言教程', 14 | }, 15 | '/en/': { 16 | lang: 'en-US', 17 | title: 'Valkyrie Tutorial', 18 | } 19 | }; 20 | 21 | const sidebar_en: SidebarConfigArray = [ 22 | { 23 | text: 'Literals', 24 | collapsible: false, 25 | children: [ 26 | { 27 | text: "Overview", 28 | link: "/en/literal/readme.md", 29 | }, 30 | { 31 | text: "Number Literal", 32 | link: "/en/literal/number.md", 33 | }, 34 | { 35 | text: "String Formatted", 36 | link: "/en/literal/string.md", 37 | }, 38 | { 39 | text: "String Templated", 40 | link: "/en/literal/template.md", 41 | }, 42 | ] 43 | }, 44 | { 45 | text: 'Collections', 46 | collapsible: false, 47 | children: [ 48 | { 49 | text: "Overview", 50 | link: "/en/collection/readme.md", 51 | }, 52 | { 53 | text: "List", 54 | link: "/en/collection/list.md", 55 | }, 56 | { 57 | text: "Dict", 58 | link: "/en/collection/dict.md", 59 | }, 60 | { 61 | text: "Map", 62 | link: "/en/collection/map.md", 63 | }, 64 | { 65 | text: "Set", 66 | link: "/en/collection/set.md", 67 | }, 68 | ] 69 | }, 70 | { 71 | text: 'Definitions', 72 | collapsible: false, 73 | children: [ 74 | { 75 | text: "Overview", 76 | link: "/en/definition/readme.md", 77 | }, 78 | { 79 | text: "Let Bind", 80 | link: "/en/definition/let-binding.md", 81 | }, 82 | { 83 | text: "Micro Process", 84 | link: "/en/definition/def-micro.md", 85 | }, 86 | { 87 | text: "Macro Process", 88 | link: "/en/definition/def-macro.md", 89 | }, 90 | { 91 | text: "Types", 92 | link: "/en/definition/typing.md", 93 | }, 94 | { 95 | text: "Structures", 96 | link: "/en/definition/structure.md", 97 | }, 98 | { 99 | text: "Inheritance", 100 | link: "/en/definition/subtyping.md", 101 | }, 102 | { 103 | text: "Interfaces", 104 | link: "/en/definition/interface.md", 105 | }, 106 | { 107 | text: "Association Type", 108 | link: "/en/definition/associated.md", 109 | }, 110 | { 111 | text: "Enumerations", 112 | link: "/en/definition/enumerate.md", 113 | }, 114 | { 115 | text: "Flags", 116 | link: "/en/definition/flags.md", 117 | }, 118 | { 119 | text: "Unions", 120 | link: "/en/definition/union.md", 121 | }, 122 | { 123 | text: "Unites", 124 | link: "/en/definition/disjoint-union.md", 125 | }, 126 | ] 127 | }, 128 | { 129 | text: 'Invokes', 130 | collapsible: false, 131 | children: [ 132 | { 133 | text: "Overview", 134 | link: "/en/invoke/readme.md", 135 | }, 136 | { 137 | text: "Function Call", 138 | link: "/en/invoke/apply.md", 139 | }, 140 | { 141 | text: "Function Dot Call", 142 | link: "/en/invoke/apply-dot.md", 143 | }, 144 | { 145 | text: "Index Call", 146 | link: "/en/invoke/subscript.md", 147 | }, 148 | { 149 | text: "Slice Call", 150 | link: "/en/invoke/slice.md", 151 | }, 152 | { 153 | text: "Lambda Call", 154 | link: "/en/invoke/lambda.md", 155 | }, 156 | { 157 | text: "Lambda Dot Call", 158 | link: "/en/invoke/lambda-dot.md", 159 | } 160 | ] 161 | }, 162 | { 163 | text: 'Control Flow', 164 | collapsible: false, 165 | target: "/en/control/readme.md", 166 | children: [ 167 | { 168 | text: "Overview", 169 | link: "/en/control/readme.md" 170 | }, 171 | { 172 | text: "If Statement", 173 | link: "/en/control/jump-if.md" 174 | }, 175 | { 176 | text: "Switch Statement", 177 | link: "/en/control/jump-switch.md" 178 | }, 179 | { 180 | text: "Non Locally Return", 181 | link: "/en/control/jump-control.md" 182 | }, 183 | { 184 | text: "While Loop", 185 | link: "/en/control/loop-while.md" 186 | }, 187 | { 188 | text: "For Loop", 189 | link: "/en/control/loop-for.md" 190 | }, 191 | { 192 | text: "Auto Handler", 193 | link: "/en/control/handle-try.md" 194 | }, 195 | { 196 | text: "Effect Handler", 197 | link: "/en/control/handle-catch.md" 198 | }, 199 | { 200 | text: "Typing Handler", 201 | link: "/en/control/handle-match.md" 202 | }, 203 | ] 204 | }, 205 | { 206 | text: 'Advance', 207 | collapsible: false, 208 | children: [ 209 | { 210 | text: "Overview", 211 | link: "/en/advance/readme.md", 212 | }, 213 | { 214 | text: "Pattern Match", 215 | link: "/en/advance/pattern-match.md", 216 | }, 217 | { 218 | text: "Extractor", 219 | link: "/en/advance/extractor.md", 220 | }, 221 | { 222 | text: "Builder", 223 | link: "/en/advance/builder.md", 224 | }, 225 | { 226 | text: "Logger", 227 | link: "/en/advance/logger.md", 228 | }, 229 | { 230 | text: "Effect System", 231 | link: "/en/advance/effect-system.md", 232 | }, 233 | ] 234 | }, 235 | { 236 | text: 'Appendix', 237 | collapsible: false, 238 | children: [ 239 | { 240 | text: "Keywords Summary", 241 | link: "/en/appendix/readme.md", 242 | }, 243 | { 244 | text: "Rust User Guide", 245 | link: "/en/appendix/for-rust.md", 246 | }, 247 | ] 248 | } 249 | ] 250 | 251 | const theme = defaultTheme( 252 | { 253 | repo: 'nyar-lang/valkyrie-language', 254 | colorModeSwitch: true, 255 | docsBranch: "dev", 256 | editLink: true, 257 | docsDir: 'docs', 258 | lastUpdated: true, 259 | locales: { 260 | '/cn/': { 261 | selectLanguageText: '选择语言', 262 | selectLanguageName: "简体中文", 263 | editLinkText: '在 GitHub 上编辑此页', 264 | sidebar: { 265 | "/cn/": sidebar_en, 266 | }, 267 | }, 268 | '/en/': { 269 | selectLanguageText: 'Languages', 270 | selectLanguageName: 'English', 271 | editLinkText: 'Edit this page on GitHub', 272 | sidebar: { 273 | "/en/": sidebar_en 274 | }, 275 | navbar: [] 276 | }, 277 | }, 278 | } 279 | ) 280 | 281 | 282 | export default defineUserConfig({ 283 | dest: 'docs/.build', 284 | lang: 'en-US', 285 | title: 'Valkyrie Tutorial', 286 | description: 'A multi-stage gradual typed language', 287 | locales: locales, 288 | head: [ 289 | ['link', {rel: 'shortcut icon', type: "image/x-icon", href: './favicon.png'}] 290 | ], 291 | theme: theme, 292 | // serviceWorker: true, 293 | markdown: { 294 | toc: {} 295 | 296 | // config: md => { 297 | // 298 | // } 299 | }, 300 | plugins: [ 301 | shikiPlugin({ 302 | theme: "one-dark-pro", 303 | langs: [ 304 | "hs", 305 | "typescript", 306 | "python", 307 | "rust", 308 | "yaml", 309 | { 310 | id: "Valkyrie", 311 | scopeName: "source.vk", 312 | aliases: ["vk", "valkyrie"], 313 | // samplePath? : string; 314 | // embeddedLangs? : Lang[]; 315 | // balancedBracketSelectors? : string[]; 316 | // unbalancedBracketSelectors? : string[]; 317 | path: path.resolve("./docs/.vuepress/styles/valkyrie.tmLanguage.json"), 318 | // grammar? : IGrammar; 319 | } 320 | ] 321 | }), 322 | ], 323 | }) 324 | -------------------------------------------------------------------------------- /docs/cn/advance/higher-kinded-type.md: -------------------------------------------------------------------------------- 1 | ### Higher Kinded Type 2 | 3 | ``` 4 | 什么是高阶 5 | 我们知道, 在强类型编程语言中, 每一个表达式都有类型. 有些类型, 在这门语言中是内置的、基本的, 我们把它们叫做"基础"类型. 比如Rust里面的 i32 bool char 等等. 还有一些类型, 是通过其它类型一起组合出来的, 比如函数, 6 | 7 | fn double(arg: i32) -> i32 { 8 | arg * 2 9 | } 10 | 这个函数类型指的是, 接受一个 i32 类型参数, 生成一个 i32 类型返回值. 它们的类型都是可以列出来的: 11 | 12 | expression | type 13 | ------------------------ 14 | true | bool 15 | 1 | i32 16 | double | i32 -> i32 17 | double(2) | i32 18 | 19 | 如果一个函数, 可以接受函数作为参数或者返回值, 那么它就是所谓的高阶函数(higher ordered function). 20 | 21 | fn twice(f: fn(i32)->i32) -> Boxi32> 22 | { 23 | Box::new(move |a| f(f(a))) 24 | } 25 | 26 | fn double(arg: i32) -> i32 { 27 | arg * 2 28 | } 29 | 30 | fn main() { 31 | let g = twice(double); 32 | println!("{}", g(2)); 33 | } 34 | 上例中, 这个 twice 函数, 就是高阶函数, 它的类型为 (i32->i32) -> (i32->i32). 35 | 36 | 37 | Vector 他不是类型, 他是泛型, 具体的 Vector 才是类型 38 | 39 | 40 | 还有一些更复杂的类型, 比如 Vec 比上面的这些类型更复杂. 因为 Vec 本身不是一个合法类型, 它必须接受一个类型参数 i32 才能成为一个真正的类型 Vec. 我们可以把 Vec 视为针对类型的"函数", 称作"类型构造器", 给它输入一个类型, 它会返回一个类型出来. 我们定义一个概念"阶(kind)" 来描述这种现象. 比如最基本的类型 i32 bool , 它们的kind就是 type. 而 Vec 的kind就是 type -> type. 41 | 42 | expression | kind 43 | ------------------- 44 | i32 | type 45 | i32 -> i32 | type 46 | Vec | type 47 | Vec | type -> type 48 | 49 | 类似的, 假如我们有一个类型构造器, 它本身可以接受类型构造器作为参数, 那它就是"高阶类型". 比如, 我们要设计一个容器类型, 它里面装的一组 i32 整数, 但是我们把内部具体实现方式 (可以用 Vec 或者 List) 开放出来作为参数由用户指定, 伪代码类似这样: 50 | 51 | # 类似 scala 的伪代码 52 | struct Collection> { 53 | data: C 54 | } 55 | 使用的时候, 我们可以这样指定泛型参数 Collection Collection, 那么这个 Collection 就是所谓的"高阶类型". 56 | 57 | expression | kind 58 | ------------------------------------------- 59 | Vec | type 60 | Vec | type -> type 61 | Collection | (type->type) -> type 62 | Collection> | type -> type 63 | 64 | 65 | 66 | 67 | 其它语言中的hkt 68 | 这一部分来自于 niko 的博客, 本人完全没有把握是否完整理解了 niko 的文章, 欢迎各位提意见. 69 | 70 | 一个直观的 hkt 设计方案, 就是直接为这个功能设计一种高阶类型的语法. 举个例子, 假如我们想设计一个通用函数, 它能把一个容器中的整数类型转换为浮点数类型, 容器本身是泛型的, 可以是 Vec 或者 LinkedList, 可以这么设计: 71 | 72 | # 假设一种表示高阶类型的语法, 类似 scala 73 | fn floatify_hkt>(ints: &I) -> I 74 | # ^^^^ the notation `I<_>` signals that `I` is 75 | # not a complete type 76 | 这个例子以及解释是从 Nicholas D. Matsakis 的博客搬运过来的. 在这个例子中, I 是一个泛型参数, 它是一个类型构造器. 假如 I 被实例化为 Vec, 那么这个函数的类型就被实例化成了 Vec -> Vec, 假如 I 被实例化为 LinkedList, 那么这个函数的类型就被实例化成了 LinkedList -> LinkedList. 但是这个设计有许多问题, 下面逐一分析. 77 | 78 | 高阶trait约束 79 | Rust泛型的类型检查, 是在泛型函数、类型定义的时候当场检查的, 而不是等到实例化的时候才检查. 所以, 对于上面这个泛型函数, 我们一定需要对它指定合理的约束条件, 否则类型 I 什么都干不了. 80 | 81 | 假设我们定义下面这样的一个 trait, 来对 Vec 和 LinkedList 做一个统一抽象: 82 | 83 | trait Collection { 84 | fn empty() -> Self; 85 | fn add(&mut self, value: Item); 86 | fn iterate<'iter>(&'iter self) -> Self::Iter<'iter>; 87 | type Iter: Iterable; 88 | } 89 | 然后, 对上面那个泛型函数添加高阶类型约束条件: 90 | 91 | fn floatify_hkt>(ints: &I) -> I 92 | where for I: Collection 93 | 看起来这个 higher kinded trait bounds 的设计很合理. 但是, 我们注意到 Haskell 中是不存在 higher kinded trait bounds 这种东西的, 它一定是因为某种原因而没有这样设计. 所以 Haskell 里面只能这么写, 把各种可能性枚举出来: 94 | 95 | fn floatify_hkt>(ints: &I) -> I 96 | where I: Collection, 97 | I: Collection 98 | 如果只能用这样的写法呢, 是会有问题的. 某些情况下, 会造成"抽象泄漏". 比如说, 像下面这样一个函数: 99 | 100 | fn process>(inputs: &I) -> I 101 | where I: Collection, I: Collection, 102 | { 103 | struct MyType { ... } 104 | 105 | ... 106 | } 107 | 如果这个函数内部定义了一个类型, 这个函数的输出类型是 I. 那我们的这个泛型约束就很难写出来, 除非把 MyType 定义到这个函数外边. 本来我们希望封装到内部的类型, 被迫暴露出去了, 这就是抽象泄漏. 108 | 109 | 对 Haskell 来说, 这好像是一个比较常见的问题. 随手搜索了一下, 看起来 Haskell 的解决方案是使用 TypeFamily. 这也是Rust设计组准备采用的方案. 110 | 111 | 在 Scala 中, 如何表达任意的高阶类型约束不清楚, 希望有网友能告知. 112 | 113 | 高阶Self类型 114 | 在前面, 我们定义了一个trait Collection, 它的成员类型是 T. 因此我们可以对不同的容器写这样的 impl: 115 | 116 | impl Collection for Vec {} 117 | # ^^ this is a type 118 | 在这个impl块中, 我们针对的是 Vec 来实现的, 它的kind是 type. 如果我们要支持高阶类型, 那么我们应该支持针对kind为 type->type 的类型impl. 我们现在更进一步, 如果我们想定义一个高阶trait, 怎么办?意思是, impl 的时候 for 后面的那个目标如果是"类型构造器"的话, 是什么情况. 假设我们这么设计: 119 | 120 | trait HkCollection for Self<_> { 121 | # ^^ ^^^^^^^ declare that `Self` is a type constructor 122 | # stands for "higher-kinded" 123 | 124 | fn empty() -> Self; 125 | fn add(self: &mut Self, value: T); 126 | # ^^^ the `T` effectively moved from the trait to the methods 127 | ... 128 | } 129 | 130 | impl HkCollection for Vec<_> { 131 | # ^^ this is a type constructor 132 | fn empty() -> Vec { 133 | Vec::new() 134 | } 135 | ... 136 | } 137 | 如果有了这样的东西, 那么我们可以针对任何一个类型构造器 I, 写这样的约束条件 where I: HkCollection, 而不需要写 where for I: Collection, 因为我们的 HkCollection 是作用到类型构造器上的. 138 | 139 | 这个设计看起来很不错, 但它有非常巨大的缺陷. 比如, 它无法针对 BitSet 这样的容器 impl. 140 | 141 | impl HkCollection for BitSet {} 142 | # ^^ not a type constructor 143 | 另外一个问题是, 有些容器, 它对内部元素的类型是有约束条件的. 比如 HashSet, 它要求 T: Hash 条件. 在上面这个设计中, 像 Self<_> 这样的设计, 实际上没有办法约束 _ 应该满足什么条件: 144 | 145 | trait HkCollection for HashSet<_> 146 | # ^^^^^^^ how can we restrict `_` to `Hash` types? 147 | Haskell 的 HKT 似乎是可以应用到任意类型上的, 无法指定约束条件. 但是在Rust里面, 约束条件无处不在, 我们如果选择 Foo<_> 这样的设计, 那么一定要想个办法来写清楚 _ 的约束条件. 148 | 149 | 类型推断 150 | 注意到前面我们说过的, Rust的一个特殊之处在于, 它的泛型参数有两种 kind, 分别是 type 和 lifetime. 假如我们有这样的一个类型: 151 | 152 | struct ListIter<'iter, T> { ... } 153 | 那么, 一个完整的 ListIter<'a, T> 它的 kind 是 type, 而 ListIter 它的 kind 可以看作 (lifetime, type)->type. 如果我们定义一个类型 type I<'_> = ListIter<'_, i32>, 那么 I 的 kind 就是 lifetime->type. 高阶类型内部还可以嵌套, 比如 G<_<_>>, 那么这个 G 的 kind 就是 (type->type)->type. 这种设计可以让你把"类型"当成"函数"来看待, 有些类型本身接受类型参数, 然后生成新的类型. 但是, 它还有个缺陷是, 影响了类型推断 type inference. 我们来看这个例子: 154 | 155 | fn floatify_hkt>(ints: &I) -> I 156 | 这里的类型参数 I 的 kind 是 type->type. 这个函数的目的是, 把一个容器内部的元素从 i32 转成 f32 (Vec -> Vec 或者 LinkedList -> LinkedList, 而不是 Vec -> LinkedList) . 那么, 现在这个类型推断就会出问题了, 即便我们已经知道了具体的实参类型和返回值类型, 我们还是解不出来这个 I 是什么. 157 | 158 | 这个问题实际上是这样的, I 实际上是一个"函数", 它输入一个 type, 输出一个 type. 现在我们可以从实参类型, 得到它的其中一个输入以及对应的输出 I == Vec. 我们还可以从返回类型, 得到它的另外一个输入以及对应的输出 I == Vec. 请问现在这个 I 是什么. 这个问题类似于: 假设 f(1) == 2 且 f(2) == 3, 求函数 f 是什么. 这个问题没有"唯一"解. 当然, 如果我们假设 f 一定是一阶函数, 那么我们可以求出来 f(x) = x + 1, 但是没有这个条件的情况下就难说了. 159 | 160 | Haskell 是没有这个问题的, 这是因为 Haskell 限制了泛型参数的应用顺序, 即所谓的 Currying. 在Rust中没有 Currying, 这意味着 Vec<_> 的kind 是 type->type, Result<_, i32> 的kind是 type->type, Result 的kind也是 type->type. 所以, 假设我们有下面这样一个方程求解: 161 | 162 | ?1 = Result 163 | 那么 ?1 = Result, ?2 = u32 或者 ?1 = Result<_, u32>, ?2 = i32, 都是这个方程的解. 164 | 165 | Rust是不能采用Currying的设计的. 第一个原因是它有两种基本的 kind. 比如说我们有个类型 Iterable<'a, T>, 按Haskell的观点, 它的 kind 可以写成 lifetime -> type -> type (实际上它还要求 T: 'a, 这个就更复杂了先忽略) . 我们希望给它一个指定的 T, 那么它的kind就会变成 lifetime -> type. 但是Currying是不允许这样的, 参数是按顺序的, 你必须先指定一个固定的lifetime, 让它的kind变成 type->type. 166 | 167 | Rust不采用Currying的第二个原因是, 在实际中有些类型不是按顺序应用泛型参数的. 比如我们的 HashMap 类型, 它有三个泛型参数: pub struct HashMap, 我们可能希望有一个类型构造器是这样的 M = HashMap<_, _, S>, 但是用 Currying 是得不到的. 168 | 169 | ATC 170 | 前面讲了很多为什么不在Rust中直接支持HKT的理由. 接下来我们回到正题, 说一说这个RFC究竟是一个什么样的提案. 从这个提案的名字上, 我们就可以看出一点端倪, 它叫做 generic associated types (泛型关联类型), 或者叫做 associated type constructors (关联类型构造器). 它的实质就是, 允许trait里面的"关联类型"成为"类型构造器". 比如说: 171 | 172 | trait Iterable { 173 | type Item<'a>; # 注意它携带泛型参数 174 | # Item 的 kind 是 lifetime -> type 175 | } 176 | 177 | trait Foo { 178 | type Bar; # 泛型参数可以是生命周期, 可以是类型 179 | # Bar 的 kind 是 type -> type 180 | } 181 | 语法上非常简单, 就是这么一个小小的扩展. 我们来看看在这个功能基础上, 怎么设计本文前面的那个 floatify 函数. 首先我们当然需要一个 trait, 来统一抽象我们的容器类型: 182 | 183 | trait Collection { 184 | fn empty() -> Self; 185 | fn add(&mut self, value: T); 186 | fn iterate(&self) -> Self::Iter; 187 | type Iter: Iterator; 188 | } 189 | 假设我们有一个具体容器, 名字叫做 List, 我们针对这个类型, 实现这个 trait: 190 | 191 | impl Collection for List { 192 | fn empty() -> List { 193 | List::new() 194 | } 195 | 196 | fn add(&mut self, value: T) { 197 | self.prepend(value); 198 | } 199 | 200 | fn iterate<'iter>(&'iter self) -> ListIter<'iter, T> { 201 | self.iter() 202 | } 203 | 204 | type Iter = ListIter<'iter, T>; 205 | # ^^^^^ oh, wait, this is not in scope! 206 | } 207 | 从这里我们看到了问题所在, 在写最后的关联类型的时候, 我们发现, 这个生命周期参数没法写. 这时候, 就该ATC这个功能出场了. 我们可以让这个关联类型不是一个具体类型, 而是一个"类型构造器", 它的 kind 为 lifetime->type, 这样我们可以让它在各种场合下, 针对不同的生命周期, 生成合适的类型. 修改 trait 的设计如下: 208 | 209 | trait Collection { 210 | # as before 211 | fn empty() -> Self; 212 | fn add(&mut self, value: T); 213 | 214 | # Here, we use associated type constructors: 215 | fn iterate<'iter>(&'iter self) -> Self::Iter<'iter>; 216 | type Iter<'iter>: Iterator; 217 | } 218 | 注意这个语法, 有点容易混淆, 这个关联类型 Iter 在声明的时候有个参数, 就代表它是"类型构造器", 在 iterator 方法返回的时候, 我们要给 Iter 一个合适的参数, 它才能生成具体的类型. 我们对具体类型来impl这个trait: 219 | 220 | use std::slice; 221 | impl Collection for Vec { 222 | fn empty() -> Self { vec![] } 223 | fn add(&mut self, value: T) { self.push(value); } 224 | fn iterate<'iter>(&'iter self) -> slice::Iter<'self, T> { self.iter() } 225 | type Iter<'iter> = slice::Iter<'iter, T>; 226 | } 227 | 那么, 用这样的一个 trait, 我们就可以实现最开始的那个函数了: 228 | 229 | fn floatify(ints: &I) -> F 230 | where I: Collection, F: Collection 231 | { 232 | let mut floats = F::empty(); 233 | for &f in c.iterate() { 234 | floats.add(f as f32); 235 | } 236 | floats 237 | } 238 | 这个函数是可以工作的. 但是, 它有一个缺点, 那就是, 不仅可以支持 Vec -> Vec 还可以支持 Vec -> LinkedList. 239 | 240 | fn test(x: &Vec) { 241 | let y: Vec = floatify(x); # 可以通过 242 | let z: LinkedList = floatify(x); # 可以通过 243 | let p = floatify(x); # 编译错误, p 的类型无法推断 244 | } 245 | 所以, 这个实现依然不是我们想要的东西, 我们想要的是类似这样的函数: 246 | 247 | # 输入和输出是同样的容器类型 248 | fn floatify_hkt(ints: &I) -> I 249 | # ^^^^^^ wait up, what is `I` here? 250 | 接下来, 我们要用一种"设计模式", 叫做 type family (也是从 Haskell 学的) . 我们另外定义一个trait: 251 | 252 | trait CollectionFamily { 253 | # Member 也是一个 type constructor 254 | type Member: Collection; 255 | } 256 | 针对具体类型 Vec 和 List 实现这个trait: 257 | 258 | struct VecFamily; 259 | 260 | impl CollectionFamily for VecFamily { 261 | type Member = Vec; 262 | } 263 | 264 | struct ListFamily; 265 | 266 | impl CollectionFamily for ListFamily { 267 | type Member = List; 268 | } 269 | 并且在原先的 Collection 中, 再添加一个关联类型, 指向 family: 270 | 271 | trait Collection { 272 | # Backlink to `Family`. 273 | type Family: CollectionFamily; 274 | ... 275 | 接下来, 我们用这个 CollectionFamily 来重新实现 floatify: 276 | 277 | fn floatify_family(ints: &C) -> C::Family::Member 278 | where C: Collection 279 | { 280 | let mut floats = C::Family::Member::empty(); 281 | for &f in c.iterate() { 282 | floats.add(f as f32); 283 | } 284 | floats 285 | } 286 | 上面这个设计圆满了, 它满足了我们所有的需求. 注意上面我们用到了针对 ATC 的约束: type Member: Collection;, 它代表的真实含义实际上是: 287 | 288 | for Self::Member: Collection; 289 | 所以, 我们也同时做到了针对高阶类型的约束. ATC 这个设计, 语法改动小, 基本是现有语法的一个自然延伸, 而且可以完成高阶类型的功能, 非常契合Rust当前的状态, 相比而言, 直接支持高阶类型语法不是一个好的选择. 290 | 291 | 目前, Rust编译器里面关于trait的逻辑很乱, 还有一些难以修复的bug. 因此, niko开了一个新项目 https://github.com/nikomatsakis/chalk, 重写整个trait系统. 在这个新的架构下, ATC功能基本是水到渠成顺手就做了的事情. 所以, 只要等 chalk 什么时候合并到正式编译器里面, 用户就可以使用这个功能了, 目测2017年有点悬, 2018年肯定能实现. 292 | 293 | 高阶类型的出现, 很可能可以解锁一些以前难以实现的问题, 这还需要社区做更多的探索. 比如, 最近就有人提到, Generator 生成器最好是基于ATC来实现, 否则有些场景下会出现编译错误, 限制用户的发挥, 参考 https://internals.rust-lang.org/t/streaming-generators/5850. 294 | ``` 295 | -------------------------------------------------------------------------------- /docs/.vuepress/readme.md: -------------------------------------------------------------------------------- 1 | ## Extension 2 | 3 | If there is no extension, then Valkyrie is a very rigid and strict language. 4 | 5 | Strictly uncomfortable, `1 + 1.0`, `"a" +'b'`, `[1] ++ 2`, none of them works. 6 | 7 | Extensions gives Valkyrie a high degree of flexibility and possibility. 8 | 9 | The extension named `Prelude` will be loaded by default. `Prelude` defines some common and reasonable arithmetic rules. 10 | 11 | Extension can be inherited and rewritten, you can also define your own `Prelude`, and then form your own code style. 12 | 13 | All the syntactic sugars in Valkyrie are statically determined by the corresponding traits to avoid unnecessary runtime overhead. 14 | 15 | ### Orphan Rules 16 | 17 | If you want to extend a method, either the `class` is defined in your library, or the `trait` is defined in your library 18 | 19 | Otherwise, it can only be modified in the `extension package namespace` 20 | 21 | This rule is to prevent the introduction of unknown semantics without knowing it 22 | 23 | 24 | ![code1.png](https://i.loli.net/2021/01/01/i6E3qSJXrbxRYwa.png) 25 | 26 |
27 | Copy code 28 | ````valkyrie 29 | #? Namespace is generally lowercase, but extension is uppercase 30 | #? Because the performance is similar to the introduce a trait 31 | pkg extension Features; 32 | ```` 33 |
34 | 35 | ### Operator overload 36 | 37 | The most common application is operator overloading. 38 | 39 | When you want to integrate two or more third-party library type conversions, it is often prohibited by the orphan rule. 40 | 41 | (really hate rust's orphan rules 🤣 :) 42 | 43 | So you need to declare extension to achieve compatibility. 44 | 45 | There is also a situation where a certain operation is convenient, but it may not be very strict. 46 | 47 | For example, we generally default that addition obeys the commutative law, but `"a" + "b" != "b" + "a"` breaks the commutative law. 48 | 49 | So you can give the choice to the library user. 50 | 51 | ![code2.png](https://i.loli.net/2021/01/01/ViJAGnKHjQLzTg5.png) 52 | 53 |
54 | Copy code 55 | ````valkyrie 56 | #? Otherwise, all it will inherit all extensions in the current namespace 57 | @no_prelude 58 | pkg extension FreeAdd { 59 | #? Symbols are defined in the standard library 60 | #? Types are also defined in the standard library 61 | #? Overloading is prohibited by the orphan rule 62 | #? Ordinary overload does not need extension 63 | def +(i: Integer, f: Decimal) { 64 | #? The function will return automatically 65 | #? The return type is automatically inferred 66 | i as Decimal + f 67 | } 68 | } 69 | ```` 70 |
71 | 72 | ### Digit mode 73 | 74 | Sometimes we want to enter some special values, such as calculations with units. If you use the constructor, it may make the code unreadable. 75 | 76 | In this case, you can use digital mode. 77 | 78 | Digital mode is in suffix form. 79 | 80 | ![code3.png](https://i.loli.net/2021/01/01/vjMSVIYtCPH9Erd.png) 81 | 82 |
83 | Copy code 84 | ````valkyrie 85 | tagged Quality { 86 | Kilogram(auto Decimal), 87 | Gram(auto Decimal), 88 | } 89 | def +(lhs: Quality, rhg: Quality): Quality { 90 | #? Some conversion rules, that's it, anyone can write 91 | @unimplemented() 92 | } 93 | pkg extension SIUnit { 94 | #? number_suffix means integer_suffix + decimal_suffix 95 | def number_suffix kg(n): Quality = Quality::Kilogram(n); 96 | def number_suffix g(n): Quality = Quality::Gram(n); 97 | } 98 | ```` 99 |
100 | 101 | Then you can write as follows! 102 | 103 | ![code4.png](https://i.loli.net/2021/01/01/zdTSYPK9M8xlDpA.png) 104 |
105 | Copy code 106 | ````valkyrie 107 | use SIUnit; 108 | let weight = 1kg + 1g; 109 | ```` 110 |
111 | 112 | By analogy, you should be able to easily implement the following functions, which is the embodiment of Valkyrie's consistency. 113 | 114 | ![code5.png](https://i.loli.net/2021/01/01/8pWx96FjmoMv3nq.png) 115 |
116 | Copy code 117 | ````valkyrie 118 | use Complex; 119 | let z = 1 + 2i; 120 | #? Note that you cannot bind `i` twice in one namespace 121 | use Quaternion; 122 | let q = 1 + 2i + 3j + 4k; 123 | ```` 124 |
125 | 126 | ### String mode 127 | 128 | String is also a very complicated thing, it is difficult to adjust, different people like different writing. 129 | 130 | Fortunately, we have string patterns, which are prefix expressions. 131 | 132 | If there is no prefix, the default string is called `s-string(slot)`. 133 | 134 | ![code6.png](https://i.loli.net/2021/01/01/PMTWRo1qVgb4EUc.png) 135 |
136 | Copy code 137 | ````valkyrie 138 | r"raw-string: ${x + 1}\n" 139 | s"slot-string: ${x + 1}\n" 140 | f"format-string: %s %f\s \n" 141 | ```` 142 |
143 | 144 | Similarly, you can also use extension to extend your own string pattern 145 | 146 | As you have guessed, use `string_prefix`, which is also a manifestation of consistency 147 | 148 | What application can you think of? Embed json object? Embed re object? Or more cool usage? 149 | 150 | ## Pattern matching 151 | 152 | More and more languages use pattern matching or similar grammars, and pattern matching is fast becoming the standard for every language. 153 | 154 | Valkyrie of course also has a highly consistent, but desugar flexible pattern matching syntax 155 | 156 | ### Literal match 157 | 158 | Literal matching is basic. 159 | 160 | ![code7.png](https://i.loli.net/2021/01/01/p97Ix54MoB2sTbE.png) 161 |
162 | Copy code 163 | ````valkyrie 164 | match x { 165 | case 1 => "integer" 166 | case 1.0 => "decimal" 167 | case '1' => "character" 168 | case "1" => "string" 169 | case 1...4 => "range" 170 | case (1, 2) => "tuple" 171 | case [1, 2] => "list" 172 | case _ => "something else" 173 | } 174 | ```` 175 |
176 | 177 | ### case guard 178 | 179 | Sometimes the amount of pattern matching requires a little conditional judgment, in this case you can use the `case guard` 180 | 181 | ![code8.png](https://i.loli.net/2021/01/01/X8fbnA57BcPHpuL.png) 182 |
183 | Copy code 184 | ````valkyrie 185 | match x { 186 | case x is Integer => "x is an instance of type `Integer`" 187 | case x is Callablte => "x satisfies the trait bound `Callable`" 188 | case x in [1, 2, 3] => "x is one of [1, 2, 3]" 189 | case x if x < 0 => "x satisfies the condition x < 0" 190 | case _ => "none of the above conditions are met" 191 | } 192 | ```` 193 |
194 | 195 | ### case deconstruction 196 | 197 | Sometimes you want to match a certain piece of data, then you can use case deconstruction 198 | 199 | ![code9.png](https://i.loli.net/2021/01/01/MR139UAC5s8WKmI.png) 200 |
201 | Copy code 202 | ````valkyrie 203 | if case Point {x: a, y, ...p} = Point {x: 1, y: 2, z: 3, w: 4, } { 204 | print(a) #? 1 205 | print(y) #? 2 206 | print(p) #? {z: 3, w: 4} 207 | } 208 | if case Point(a, ..p, y) = Point(1, 2, 3, 4) { 209 | print(a) #? 1 210 | print(p) #? [2, 3] 211 | print(y) #? 4 212 | } 213 | ```` 214 |
215 | 216 | 217 | ### Custom extractor 218 | 219 | For custom classes, you can define the `unapply` method to customize the extracted data. 220 | 221 | ![code10.png](https://i.loli.net/2021/01/01/i3lVkAIwRB9CyL6.png) 222 |
223 | Copy code 224 | ````valkyrie 225 | match input { 226 | case Regex(group0) => Integer::parse(group0), 227 | } 228 | #? desugar as unapply 229 | if case Some(group0) = Regex::unapply(input) { 230 | Integer::parse(group0) 231 | } 232 | ```` 233 |
234 | 235 | 236 | ## Closures and Lambda Functions 237 | 238 | Closures are also a necessary feature of a modern language. 239 | 240 | Consider the following function: 241 | 242 | ![](https://ftp.bmp.ovh/imgs/2021/01/e223f1897413b3a2.png) 243 | 244 |
245 | Copy code 246 | ````valkyrie 247 | def doulbe_even(x) { 248 | match x { 249 | x if x % 2 => 2 * x, 250 | x => x, 251 | } 252 | } 253 | [1, 2, 3, 4].map(doulbe_even) 254 | ```` 255 |
256 | If the last parameter accepted by a function is a closure, then `()` can be omitted. 257 | 258 | Now consider writing it as an anonymous function: 259 | 260 | ![](https://ftp.bmp.ovh/imgs/2021/01/db0295c02d008e44.png) 261 | 262 | 263 |
264 | Copy code 265 | ````valkyrie 266 | [1, 2, 3, 4].map { 267 | #? `{lambda (x) expr}` even longer than python 🤣 268 | lambda (x) match x { 269 | x if x % 2 => 2 * x, 270 | x => x, 271 | } 272 | } 273 | ```` 274 |
275 | 276 | (This looks so complicated 🤣 :) 277 | 278 | This is a full form of closure, it only exists as a result of desugar, and is not actually used. 279 | 280 | The following `case closure` is commonly used: 281 | 282 | ![](https://ftp.bmp.ovh/imgs/2021/01/21d6ec20f40daafa.png) 283 |
284 | Copy code 285 | ````valkyrie 286 | [1, 2, 3, 4].map { 287 | case x if x % 2 => 2 * x, 288 | case x => x, 289 | } 290 | ```` 291 |
292 | There is also a shorthand method called `slot closure`: 293 | 294 | ![](https://ftp.bmp.ovh/imgs/2021/01/480ebcb19262ab36.png) 295 | 296 |
297 | Copy code 298 | ````valkyrie 299 | [1, 2, 3, 4].map { 300 | if $0 % 2 {2 * $0} else {$0} 301 | } 302 | #? Looks like perl, maybe the ternary operator should be banned 303 | [1, 2, 3, 4].map {$0 % 2 ? 2 * $0 : $0} 304 | ```` 305 |
306 | 307 | ## Polymorphism 308 | 309 | Sometimes we need some polymorphic interfaces, such as functions that accept strings and integers at the same time 310 | 311 | The most intrusive but the most convenient to use is implicit type conversion 312 | 313 | ![](https://ftp.bmp.ovh/imgs/2021/01/e4a5c772985379c7.png) 314 | 315 | 316 |
317 | Copy code 318 | ````valkyrie 319 | #? First define ordinary type conversion 320 | extends Integer: From { 321 | def from(s) { Self::parse(i) } 322 | } 323 | #? `ImplicitFrom` needs to meet trait bound `From` 324 | extends Integer: ImplicitFrom; 325 | def add_one(input: Integer): Integer { 326 | input + 1 327 | } 328 | #? Found type mismatch, try implicit type conversion 329 | add_one("1") #? 2 330 | ```` 331 |
332 | 333 | Followed by explicit type conversion, automatic convert input. 334 | 335 | ![](https://ftp.bmp.ovh/imgs/2021/01/870edc42a53ad0aa.png) 336 | 337 |
338 | Copy code 339 | ````valkyrie 340 | def add_one(auto input: Integer): Integer { 341 | input + 1 342 | } 343 | #? Found that the type does not match, call the `from` method 344 | add_one("1") #? 2 345 | ```` 346 |
347 | 348 | The more standard is to use trait-based generic static dispatch 349 | 350 | This constraint method is also called parametric polymorphism, or generic 351 | 352 | ![](https://ftp.bmp.ovh/imgs/2021/01/46b144d7ae808b29.png) 353 | 354 |
355 | Copy code 356 | ````valkyrie 357 | def add_one(input: T): Integer 358 | for T: Into 359 | { 360 | input.into() + 1 361 | } 362 | ```` 363 |
364 | 365 | The above methods are all non-limiting polymorphism, and the input does not need to be predetermined. 366 | 367 | But sometimes the input is fixed in several categories, which requires definite polymorphism. 368 | 369 | Restrictive polymorphism can be realized by combining types and sum types. Many languages do not distinguish between the two, or even confuse the two. 370 | 371 | But think about it carefully, can `Optional>` and `Nullable>` be the same? 372 | 373 | ![](https://ftp.bmp.ovh/imgs/2021/01/e94e0c8f84e5a6bc.png) 374 | 375 |
376 | Copy code 377 | ````valkyrie 378 | #? This is the sum type, tagged union 379 | tagged Optional { 380 | Some, 381 | None, 382 | } 383 | #? This is the union type, untagged union 384 | class Null {}; 385 | type Nullable = T | Null; 386 | Optional> ==> Optional> 387 | Nullable> ==> Nullable 388 | ```` 389 |
390 | 391 | So the treatment of the two is also different. 392 | 393 | But in fact the compiler should be able to optimize to the same. 394 | 395 | ![](https://ftp.bmp.ovh/imgs/2021/01/f99d25a5a4124a39.png) 396 | 397 |
398 | Copy code 399 | ````valkyrie 400 | #? sum type matching 401 | def add_one(input: Integer|String): Integer { 402 | let y = match input { 403 | x is Integer => x, 404 | x is String => Integer::parse(x) 405 | } 406 | y + 1 407 | } 408 | #? union type matching 409 | tagged Canbe { 410 | Integer(Integer) 411 | String(String) 412 | } 413 | def add_one(input: Canbe): Integer { 414 | let y = match input { 415 | Canbe::Integer(x) => x, 416 | Canbe::String(x) => Integer::parse(x) 417 | } 418 | y + 1 419 | } 420 | ```` 421 |
422 | 423 | ## Specially designed REPL mode 424 | 425 | REPL mode refers to a working mode similar to Mathematica, Observable or Jupyter 426 | 427 | ### Sequential and non-sequential mode 428 | 429 | Consider the following code: 430 | 431 | ````ts 432 | let second = 1 + first; 433 | let first = 1; 434 | ```` 435 | 436 | In a sequential language, this should report an error directly, the first line occurs before the second line, and an undeclared and uninitialized variable `first` cannot be used. 437 | 438 | But in a non-chronological language, this is achievable, because all declarations happen at the same time, and no one comes first. 439 | 440 | Then consider the following code: 441 | 442 | ````ts 443 | let a = 1; 444 | let a = 2; 445 | ```` 446 | 447 | In a non-sequential language, it is obvious that the second line should cover the declaration of `a` in the first line. 448 | 449 | But in an unscheduled language, an error will be reported, because all declarations occur at the same time, and no one comes first. 450 | 451 | The scope and closure in Valkyrie are sequential, but the function still cannot be declared repeatedly. 452 | 453 | Other packages, classes, traits, and tagged are all non-sequential, so repeated declarations are prohibited. 454 | 455 | ### REPL mode 456 | 457 | But in REPL mode, the package namespace is time-sequential, and you can modify a function repeatedly and overwrite the previous one, which is very convenient for debugging. 458 | 459 | All undeclared variables in REPL mode default to the variable variables declared for the first time. This setting borrows from Mathematica and is also very convenient for debugging. 460 | 461 | In REPL mode, you can use `use?` to import an external module and allow repeated imports to achieve hot reloading 462 | 463 | Here `?` means do not add to the global method table, because once added, it will pollute all definitions, and this pollution is irreversible. 464 | 465 | REPL mode can use `¶` and `⁋` to get historical input and output, which is equivalent to Mathematica's `In`, `Out` and `%` 466 | 467 | ## AST-based macro 468 | 469 | AST-based macros may not be as powerful as token-based macros, but do you really want to write a completely different language in another language? 470 | 471 | In particular, the IDE support of Token macros is generally very poor, but AST macros with TokenKind can get very good support. 472 | 473 | ## IR compatibility 474 | 475 | Valkyrie's syntax is not absolutely stable. Valkyrie will compile the source file into a single IR file to shield this change and obtain forward compatibility. 476 | 477 | Valkyrie uses a Rust-like Edition release model. 478 | 479 | --------------------------------------------------------------------------------