├── src ├── miji │ ├── misc │ │ ├── misc.md │ │ ├── author.md │ │ ├── resources.md │ │ ├── wishes.md │ │ ├── tips.md │ │ ├── layout.md │ │ └── glossary.md │ ├── basic │ │ ├── py2mojo.md │ │ ├── basic.md │ │ ├── operators.md │ │ └── literal.md │ ├── start │ │ ├── start.md │ │ ├── pixi.md │ │ ├── magic.md │ │ ├── hello.md │ │ └── project.md │ ├── advanced │ │ ├── advanced.md │ │ ├── unsafe.md │ │ └── parameterization.md │ ├── move │ │ ├── move.md │ │ └── different.md │ ├── extend │ │ ├── extend.md │ │ └── numojo.md │ ├── apply │ │ ├── apply.md │ │ ├── design.md │ │ └── work.md │ ├── index.md │ └── intro.md ├── .vitepress │ ├── hooks │ │ ├── index.ts │ │ └── useMediumZoom.ts │ ├── theme │ │ ├── index.ts │ │ ├── style.css │ │ ├── custom.css │ │ └── fonts.css │ ├── config.mts │ └── vars.css ├── scripts │ ├── variables.mojo │ └── types.mojo ├── public │ ├── icon.png │ ├── graphs │ │ ├── pointed_status.jpg │ │ ├── safe_memory_view.jpg │ │ ├── variable_as_vault.jpg │ │ ├── result_as_blind_box.jpg │ │ ├── variable_initialized.jpg │ │ ├── symbol_table_as_vaults.jpg │ │ ├── variable_uninitialized.jpg │ │ └── variable_vault_a_int_123456789.jpg │ └── fonts │ │ └── Inconsolata-Variable.ttf ├── shims-vue.d.ts ├── numojo │ ├── intro.md │ ├── ndarray.md │ └── view.md └── index.md ├── .gitignore ├── postcss.config.js ├── tools ├── two_columns.txt └── tc2sc.py ├── tailwind.config.cjs ├── tsconfig.json ├── package.json └── README.md /src/miji/misc/misc.md: -------------------------------------------------------------------------------- 1 | # Miscellaneous topics 2 | -------------------------------------------------------------------------------- /src/.vitepress/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useMediumZoom' -------------------------------------------------------------------------------- /src/miji/basic/py2mojo.md: -------------------------------------------------------------------------------- 1 | # Use Python objects in Mojo 2 | 3 | -------------------------------------------------------------------------------- /src/scripts/variables.mojo: -------------------------------------------------------------------------------- 1 | fn main(): 2 | var a = 1 3 | -------------------------------------------------------------------------------- /src/public/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forfudan/MojoMiji/HEAD/src/public/icon.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | ___* 4 | src/.vitepress/cache 5 | .DS_Store 6 | 7 | local 8 | .vscode 9 | .mypy_cache -------------------------------------------------------------------------------- /src/public/graphs/pointed_status.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forfudan/MojoMiji/HEAD/src/public/graphs/pointed_status.jpg -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; -------------------------------------------------------------------------------- /src/public/graphs/safe_memory_view.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forfudan/MojoMiji/HEAD/src/public/graphs/safe_memory_view.jpg -------------------------------------------------------------------------------- /src/public/graphs/variable_as_vault.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forfudan/MojoMiji/HEAD/src/public/graphs/variable_as_vault.jpg -------------------------------------------------------------------------------- /src/public/fonts/Inconsolata-Variable.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forfudan/MojoMiji/HEAD/src/public/fonts/Inconsolata-Variable.ttf -------------------------------------------------------------------------------- /src/public/graphs/result_as_blind_box.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forfudan/MojoMiji/HEAD/src/public/graphs/result_as_blind_box.jpg -------------------------------------------------------------------------------- /src/public/graphs/variable_initialized.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forfudan/MojoMiji/HEAD/src/public/graphs/variable_initialized.jpg -------------------------------------------------------------------------------- /src/public/graphs/symbol_table_as_vaults.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forfudan/MojoMiji/HEAD/src/public/graphs/symbol_table_as_vaults.jpg -------------------------------------------------------------------------------- /src/public/graphs/variable_uninitialized.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forfudan/MojoMiji/HEAD/src/public/graphs/variable_uninitialized.jpg -------------------------------------------------------------------------------- /src/scripts/types.mojo: -------------------------------------------------------------------------------- 1 | fn main(): 2 | var a: Float64 = 120 3 | var b: Int = 24 4 | var c: String = "Hello, world!" 5 | print(a, b, c) 6 | -------------------------------------------------------------------------------- /src/public/graphs/variable_vault_a_int_123456789.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forfudan/MojoMiji/HEAD/src/public/graphs/variable_vault_a_int_123456789.jpg -------------------------------------------------------------------------------- /tools/two_columns.txt: -------------------------------------------------------------------------------- 1 |
MojoPython
2 | 3 | ```mojo 4 | 5 | ``` 6 | 7 | 8 | 9 | ```python 10 | 11 | ``` 12 | 13 |
-------------------------------------------------------------------------------- /src/miji/start/start.md: -------------------------------------------------------------------------------- 1 | # Start 2 | 3 | This part of the Miji helps you get started with Mojo. It covers the installation of a package manager, initiation of a Mojo project, and installation of the Mojo compiler. At the end, you will learn how to write your first Mojo program. 4 | -------------------------------------------------------------------------------- /src/miji/basic/basic.md: -------------------------------------------------------------------------------- 1 | # Basic features of Mojo 2 | 3 | This part of the Miji focuses on the basic features of Mojo, e.g., variables, data types, functions, control flows, etc. Most of the topics covered in this part are so similar to Python that you will feel very comfortable with them. 4 | -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | // Mocks all files ending in `.vue` showing them as plain Vue instances 4 | declare module '*.vue' { 5 | import type { DefineComponent } from 'vue'; 6 | const component: DefineComponent<{}, {}, any>; 7 | export default component; 8 | } 9 | -------------------------------------------------------------------------------- /src/miji/advanced/advanced.md: -------------------------------------------------------------------------------- 1 | # Advanced 2 | 3 | This part of the Miji focuses on the advanced features of Mojo, e.g., SIMD, parameterization, generics, traits, ownership, references, lifetime, etc. These topics are so different from Python that you need to pay some time understanding them. Knowing another compiled language like C++ or Rust will help you a lot in this part. 4 | -------------------------------------------------------------------------------- /src/miji/move/move.md: -------------------------------------------------------------------------------- 1 | # Move from Python to Mojo 2 | 3 | This part of the Miji helps you move from Python to Mojo. We first look at some examples and then discuss the similarities and differences between the two languages. 4 | 5 | If you are already a Python user, you can quickly get yourself familiar with Mojo with the following chapters. If you are not yet a Python user, you can skip this part and go to the next part, which discusses the basic concepts of Mojo. 6 | -------------------------------------------------------------------------------- /src/numojo/intro.md: -------------------------------------------------------------------------------- 1 | # NuMojo 2 | 3 | NuMojo is a library for numerical computing in Mojo, similar to NumPy and SciPy in Python. [Link to the repo](https://github.com/Mojo-Numerics-and-Algorithms-group/NuMojo). 4 | 5 | I will discuss the conceptual models of the NDArray and how the functionalities are implemented internally. 6 | 7 | Some of the functionalities are not yet fulfilled due to Mojo, e.g., parameterized traits. We will incorporate them once they are ready. -------------------------------------------------------------------------------- /tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | darkMode: ["class"], 4 | content: [ 5 | './components/**/*.{ts,tsx,vue}', 6 | './src/**/*.{ts,tsx,vue}', 7 | ], 8 | theme: { 9 | extend: { 10 | flex: { 11 | '5': '0.5 0.5 0%', 12 | '2': '2 2 0%' 13 | } 14 | } 15 | }, 16 | plugins: [require("daisyui")], 17 | } -------------------------------------------------------------------------------- /src/miji/extend/extend.md: -------------------------------------------------------------------------------- 1 | # Extending Mojo 2 | 3 | Mojo comes with its own standard library, but it is not aimed to cover all the functionalities that users may need. The designers must find a balance between the size of the standard library and the coverage of functionalities. If some use cases are not common enough, they should be left for users to implement themselves. This gives room for third-party libraries to flourish. 4 | 5 | In this part, I will discuss some of the libraries in Mojo's ecosystem and their functionalities. 6 | -------------------------------------------------------------------------------- /src/.vitepress/theme/index.ts: -------------------------------------------------------------------------------- 1 | // https://vitepress.dev/guide/custom-theme 2 | import type { Theme } from 'vitepress' 3 | import DefaultTheme from 'vitepress/theme-without-fonts' 4 | import { useMediumZoomProvider } from '../hooks' 5 | import './style.css' 6 | import './fonts.css' 7 | import './custom.css' 8 | // Group icons plugin 9 | import 'virtual:group-icons.css' 10 | 11 | export default { 12 | extends: DefaultTheme, 13 | 14 | enhanceApp({ app, router, siteData }) { 15 | // 图片放大 16 | useMediumZoomProvider(app, router) 17 | } 18 | } satisfies Theme -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ 3 | "./src/**/*", 4 | "./components/**/*" 5 | ], 6 | "exclude": [ 7 | "dist", 8 | "src/.vitepress/cache", 9 | "node_modules" 10 | ], 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "skipLibCheck": true, 14 | "allowJs": true, 15 | "noEmit": true, 16 | "esModuleInterop": true, 17 | "compilerOptions": { 18 | "target": "ESNext", 19 | "moduleResolution": "node", 20 | "module": "ESNext", 21 | "baseUrl": ".", 22 | "paths": { 23 | "@/*": [ 24 | "./components/*" 25 | ] 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/miji/apply/apply.md: -------------------------------------------------------------------------------- 1 | # APPLIED MOJO - Case study 2 | 3 | Now we come to a case study - to implement a matrix type in Mojo by applying the knowledge we have learned in the previous chapters. 4 | 5 | The matrix type is a fundamental data structure in numerical computing. It is not as complicated and flexible as and N-dimensional array (`NDArray`), but it is more efficient if we are only dealing with two-dimensional data. It is also a good data type to start with. 6 | 7 | This case study is similar to a pull request to the NuMojo library ([PR #138](https://github.com/Mojo-Numerics-and-Algorithms-group/NuMojo/pull/138)) that introduces the matrix type. However, the case study is more concise and focused on basic functionalities. 8 | -------------------------------------------------------------------------------- /src/.vitepress/theme/style.css: -------------------------------------------------------------------------------- 1 | /* Override VitePress code block styles with exact selectors */ 2 | .vp-doc [class*='language-'] code, 3 | .vp-doc [class*='language-'] pre, 4 | .vp-doc :not(pre)>code, 5 | .line-numbers-wrapper, 6 | pre, 7 | code, 8 | kbd, 9 | samp { 10 | font-family: "Inconsolata", monospace !important; 11 | font-weight: 400 !important; 12 | font-variant-ligatures: none !important; 13 | font-feature-settings: "liga" 0, "clig" 0, "kern" 0, "calt" 0 !important; 14 | } 15 | 16 | /* Set line height to 1.4 for code blocks */ 17 | .vp-doc [class*='language-'] code { 18 | line-height: 1.4 !important; 19 | } 20 | 21 | /* Make code font size same as normal text (16px instead of default 14px) */ 22 | .vp-doc [class*='language-'] code, 23 | .vp-doc :not(pre)>code { 24 | font-size: 1em !important; 25 | /* Same as normal text instead of 0.875em */ 26 | } 27 | 28 | @layer utilities {} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mojomiji-website", 3 | "version": "0.0.1", 4 | "private": true, 5 | "description": "", 6 | "devDependencies": { 7 | "@types/node": "^20.11.24", 8 | "autoprefixer": "^10.4.18", 9 | "daisyui": "^4.7.2", 10 | "markdown-it-footnote": "^4.0.0", 11 | "markdown-it-mathjax3": "^4.3.2", 12 | "medium-zoom": "^1.1.0", 13 | "postcss": "^8.4.35", 14 | "tailwindcss": "^3.4.1", 15 | "vitepress": "1.6.3", 16 | "vue": "^3.4.21" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/forFudan/MojoMiji" 21 | }, 22 | "type": "module", 23 | "scripts": { 24 | "dev": "vitepress dev src", 25 | "build": "vitepress build src", 26 | "preview": "vitepress preview src" 27 | }, 28 | "dependencies": { 29 | "@vueuse/core": "^10.9.0", 30 | "tailwind": "^4.0.0", 31 | "vitepress-plugin-group-icons": "^1.6.0" 32 | } 33 | } -------------------------------------------------------------------------------- /src/.vitepress/hooks/useMediumZoom.ts: -------------------------------------------------------------------------------- 1 | import mediumZoom from 'medium-zoom' 2 | import { inject, nextTick, onMounted, watch } from 'vue' 3 | import type { Zoom } from 'medium-zoom' 4 | import type { App, InjectionKey } from 'vue' 5 | import type { Router } from 'vitepress' 6 | 7 | declare module 'medium-zoom' { 8 | interface Zoom { 9 | refresh: (selector?: string) => void 10 | } 11 | } 12 | 13 | export const mediumZoomSymbol: InjectionKey = Symbol('mediumZoom') 14 | 15 | export function useMediumZoom() { 16 | onMounted(() => inject(mediumZoomSymbol)?.refresh()) 17 | } 18 | 19 | export function useMediumZoomProvider(app: App, router: Router) { 20 | //@ts-ignore 21 | if (import.meta.env.SSR) return 22 | const zoom = mediumZoom() 23 | zoom.refresh = () => { 24 | zoom.detach() 25 | zoom.attach(':not(a) > img:not(.image-src)') 26 | } 27 | app.provide(mediumZoomSymbol, zoom) 28 | watch( 29 | () => router.route.path, 30 | () => nextTick(() => zoom.refresh()) 31 | ) 32 | } -------------------------------------------------------------------------------- /src/miji/misc/author.md: -------------------------------------------------------------------------------- 1 | # About the author 2 | 3 | Yuhao Zhu (朱宇浩) is a quantitative credit risk model validator at ABN AMRO Bank. He previously obtained his PhD in Finance from the Erasmus University Rotterdam. He is a Pythonista and has been programming in Python for over 10 years for his research, work, and hobbies. 4 | 5 | Yuhao became interested in Mojo in late 2023 when he was looking for a programming language that could make use of the type hints in Python to improve performance, after which he started to migrate some of his Python code to Mojo program in Mojo for the [Yuhao Chinese Input Method](https://shurufa.app/). He is also a contributor to [NuMojo](https://github.com/Mojo-Numerics-and-Algorithms-group/NuMojo) and [DeciMojo](https://github.com/forfudan/decimojo). 6 | 7 | Beside programming, Yuhao enjoys reading and writing, especially in the fields of literature, philosophy, and history. He is currently writing a novel called *"Gate of Heaven"*. 8 | 9 | Yuhao is currently living in Rotterdam, the Netherlands, with his wife and a cat. 10 | -------------------------------------------------------------------------------- /src/miji/misc/resources.md: -------------------------------------------------------------------------------- 1 | # Useful resources and links for Mojo 2 | 3 | 1. [Mojo official manual](https://docs.modular.com/mojo/manual/get-started/): The official documentation for Mojo, which provides comprehensive information about the language, its features, and how to use it. 4 | 1. [Mojo changelogs](https://docs.modular.com/mojo/changelog): A record of changes made to the Mojo language, including new features, bug fixes, and improvements. 5 | 1. [Mojo standard library Github repository](https://github.com/modular/modular): The official repository for the Mojo standard library, which contains the source code and documentation for the built-in functions and types in Mojo. 6 | 1. [Mojo community forum](https://forum.modular.com/): A place where Mojo users can ask questions, share knowledge, and discuss various topics related to Mojo programming. 7 | 1. [Mojo Discord server](https://discord.gg/m4uBMDNv): A community of Mojo users where you can discuss Mojo programming, ask questions, and get help from other users in real-time. 8 | 1. [awesome-mojo](https://github.com/mojicians/awesome-mojo): A curated list of awesome Mojo frameworks, libraries, software and resources. 9 | -------------------------------------------------------------------------------- /src/.vitepress/theme/custom.css: -------------------------------------------------------------------------------- 1 | th { 2 | white-space: nowrap; 3 | } 4 | 5 | /* Theme of quotation */ 6 | 7 | .vp-doc blockquote { 8 | border-left: 4px solid #ff00ae; 9 | border-radius: 4px; 10 | background-color: transparent !important; 11 | } 12 | 13 | /* Theme of inline code */ 14 | 15 | li>code, 16 | /* table code, */ 17 | p code { 18 | /* word-break: break-word; */ 19 | color: var(--vp-c-purple-1) !important; 20 | font-weight: bold; 21 | /* border: #79ccff 1px dotted; */ 22 | background-color: transparent !important; 23 | } 24 | 25 | /* Theme of hyperlinks */ 26 | .vp-doc a { 27 | color: var(--vp-c-indigo-1) !important; 28 | /* font-weight: bold; */ 29 | /* border: var(--vp-c-indigo-1) 1px dotted; */ 30 | background-color: transparent !important; 31 | /* text-decoration: underline; */ 32 | } 33 | 34 | 35 | /* Theme of headings */ 36 | .vp-doc h1, 37 | .vp-doc h2, 38 | .vp-doc h3, 39 | .vp-doc h4, 40 | .vp-doc h5, 41 | .vp-doc h6, 42 | .vp-doc .header-anchor:before { 43 | color: #e15300 !important; 44 | /* 45 | background: linear-gradient(to right, #db0000, #e4b600); 46 | background-clip: text; 47 | -webkit-background-clip: text; 48 | -webkit-text-fill-color: transparent; 49 | width: max-content; 50 | */ 51 | } -------------------------------------------------------------------------------- /tools/tc2sc.py: -------------------------------------------------------------------------------- 1 | # %% 2 | import opencc 3 | import re 4 | 5 | t2s = opencc.OpenCC("t2s") 6 | 7 | paths_of_docs = [ 8 | "/index.md", 9 | "/miji/introduction.md", 10 | "/miji/variables.md", 11 | "/miji/types.md", 12 | "/miji/ownership.md", 13 | ] 14 | 15 | for path_of_doc in paths_of_docs: 16 | with open("../src/zht" + path_of_doc, mode="r", encoding="utf8") as temp: 17 | doc = temp.read() 18 | if not doc.startswith(""): 19 | pat = re.compile( 20 | r"((?:[\S\s]+?)|(?:`.+?`)|(?:<.+?>)|(?:[^`<>]+)|(?:[\r\n]+)|(?:[<>]))+?" 21 | ) 22 | res = re.findall(pat, doc) 23 | res_zht = [] 24 | for i in res: 25 | if i.startswith("`") or i.startswith("<"): 26 | res_zht.append(i) 27 | else: 28 | res_zht.append(t2s.convert(i)) 29 | output = "".join(res_zht) 30 | print(path_of_doc, "translated.") 31 | else: 32 | output = doc 33 | print(path_of_doc, "not translated.") 34 | output = output.replace("/zht/", "/zhs/") 35 | output = output.replace("link: /zhs/index", "link: /zht/index") 36 | with open("../src/zhs" + path_of_doc, mode="w", encoding="utf8") as temp: 37 | temp.write(output) 38 | 39 | # %% 40 | -------------------------------------------------------------------------------- /src/.vitepress/theme/fonts.css: -------------------------------------------------------------------------------- 1 | /* Keep Noto Serif Khitan Small Script from Google Fonts */ 2 | @import url('https://fonts.googleapis.com/css2?family=Noto+Serif+Khitan+Small+Script&display=swap'); 3 | 4 | /* Self-hosted Inconsolata font - same version as local system */ 5 | @font-face { 6 | font-family: 'Inconsolata'; 7 | src: url('/fonts/Inconsolata-Variable.ttf') format('truetype-variations'); 8 | font-weight: 200 900; 9 | font-stretch: 50% 200%; 10 | font-display: swap; 11 | } 12 | 13 | :root { 14 | 15 | --vp-font-family-base: ui-sans-serif, system-ui, -apple-system, 16 | BlinkMacSystemFont, 'Segoe UI', Roboto, 17 | 'Helvetica Neue', Helvetica, Arial, 'Noto Sans', sans-serif, 18 | 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; 19 | --vp-font-family-mono: "Inconsolata", "SF Mono", "Menlo", "Monaco", "Cascadia Code", "Roboto Mono", ui-monospace, SFMono-Regular, Consolas, 'Liberation Mono', 'Courier New', monospace; 20 | 21 | /* --vp-home-hero-name-color: transparent; 22 | --vp-home-hero-name-background: -webkit-linear-gradient(120deg, 23 | var(--vp-c-brand-1), 24 | var(--vp-c-brand-3)); 25 | ; 26 | 27 | --vp-home-hero-image-background-image: none; 28 | --vp-home-hero-image-filter: none; */ 29 | 30 | --vp-home-hero-name-color: transparent; 31 | --vp-home-hero-name-background: -webkit-linear-gradient(-30deg, #bd34fe 30%, #2970fc); 32 | --vp-home-hero-image-background-image: linear-gradient(45deg, #bd34fe 50%, #2970fc 50%); 33 | --vp-home-hero-image-filter: blur(64px); 34 | } 35 | 36 | .medium-zoom-overlay { 37 | background-color: var(--vp-c-bg) !important; 38 | z-index: 100; 39 | } 40 | 41 | .medium-zoom-overlay~img { 42 | z-index: 101; 43 | } 44 | 45 | .medium-zoom--opened .medium-zoom-overlay { 46 | opacity: 0.9 !important; 47 | } -------------------------------------------------------------------------------- /src/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | 4 | hero: 5 | name: "MOJO MIJI" 6 | text: "A Guide to Mojo Programming Language" 7 | tagline: from A Pythonista's Perspective 8 | image: 9 | dark: /icon.png 10 | light: /icon.png 11 | alt: icon 12 | actions: 13 | - theme: brand 14 | text: Open Miji 15 | link: miji/ 16 | # - theme: alt 17 | # text: NuMojo 18 | # link: numojo/intro 19 | 20 | features: 21 | - title: Fast 22 | details: Mojo supports static typing, SIMD, and meta-programming. The speed of Mojo is much, much faster than that of Python. 23 | - title: Safe 24 | details: Mojo supports ownership semantic and increase the safety of memory. 25 | - title: Pythonic 26 | details: Mojo's coding style and syntax is close to Python, which is very friendly for Python users. 27 | - title: Concrete examples 28 | details: This Miji Book contains many concrete examples of Mojo code, which can help you learn Mojo quickly and avoid pitfalls that I encountered. 29 | - title: Abundant illustrations 30 | details: This Miji Book provides a lot of graphs on memory layout of Mojo objects, which can help you to understand how Mojo works. 31 | - title: Pythonista's perspective 32 | details: This Miji Book stands from a Pythonista's perspective and always compare Mojo with Python, making it easier for you to convert your Python knowledge into Mojo. 33 | --- 34 | 35 | 36 | 37 | ::: info About this Miji 38 | 39 | "Miji" is the latinization of the Chinese word , which literally means a "secret book" or "book of tips". You can call this book "Mojo Miji", "Miji Book", or just "the Miji". 40 | 41 | [GitHub repository of this Miji](https://github.com/forFudan/MojoMiji) 42 | [GitHub repository of Mojo's standard library](https://github.com/modularml/mojo) 43 | 44 | ::: 45 | 46 | ::: info About author 47 | 48 | I am Yuhao Zhu (朱宇浩), a senior credit risk model validator at ABN AMRO Bank and visiting fellow at Erasmus University Rotterdam. 49 | 50 | [Personal website](https://zhuyuhao.com) 51 | [GitHub page](https://github.com/forFudan) 52 | 53 | ::: 54 | -------------------------------------------------------------------------------- /src/numojo/ndarray.md: -------------------------------------------------------------------------------- 1 | # NDArray type 2 | 3 | ## Array initializing 4 | 5 | Array initializing method, i.e., `__init__` constructs a pure container 6 | by reading in the shape and the stride information. 7 | Although the memory is allocated, it is not initialized. 8 | Users should always construct arrays using functions in creation routines. 9 | For example, `zeros`, `ones`, `randn`, `array`, etc. 10 | 11 | The following ways of array initializing are supported: 12 | 13 | 1. `NDArrayShape` and `order`. 14 | 2. `List` and `order`. 15 | 3. `VariadicList` and `order`. 16 | 17 | ## Array indexing and slicing 18 | 19 | ### Array indexing 20 | 21 | Array indexing always returns a scalar or an SIMD. 22 | 23 | "safe" means whether boundary checks are made. 24 | "order" means whether it evaluate the order or directly gets value from the underlying buffer. 25 | If yes, the strides information will be considered. 26 | If no, the indexing will based on the continuous allocation of the data buffer. 27 | 28 | | method (overload) | args | Ret | safe? | order? | notes | 29 | | ----------------- | ---- | ------ | ----- | ------ | --------------- | 30 | | `_getitem` | *Int | Scalar | no | yes | | 31 | | `__getitem__` | Idx | Scalar | yes | yes | | 32 | | `item` | Int | Scalar | yes | yes | `[0, size)` | 33 | | `item` | *Int | Scalar | yes | yes | `[0, shape[i])` | 34 | | `load` | Int | Scalar | yes | no | | 35 | | `load[width]` | Int | SIMD | yes | no | | 36 | | `load[width]` | *Int | SIMD | yes | no | | 37 | 38 | ### Array slicing 39 | 40 | Array slicing always returns an NDArray of the same number of dimensions or less. 41 | Currently, the returns are copies of the data. 42 | In future, when Mojo's trait are enhanced, slicing will returns a view of the data of the array. 43 | 44 | | method (overload) | args | safe? | order? | notes | 45 | | ----------------- | -------------------- | ----- | ------ | -------------------- | 46 | | `__getitem__` | Int | yes | yes | i-th row of input | 47 | | `__getitem__` | List[Int] | yes | yes | Rows of input | 48 | | `__getitem__` | List[Slice] | yes | yes | Same ndim as input | 49 | | `__getitem__` | *args[Slice] | yes | yes | Same ndim as input | 50 | | `__getitem__` | *Slice | yes | yes | Same ndim as input | 51 | | `__getitem__` | *Variant[Slice, Int] | yes | yes | Same or smaller ndim | 52 | | `__getitem__` | NDArray[DType.bool] | no | no | Items of buffer | 53 | | `__getitem__` | NDArray[DType.index] | no | no | Items of buffer | -------------------------------------------------------------------------------- /src/miji/advanced/unsafe.md: -------------------------------------------------------------------------------- 1 | # Unsafe Mojo 2 | 3 | > "I am the owner." 4 | > "But I am the God." 5 | > -- Yuhao Zhu, *Gate of Heaven* 6 | 7 | ::: danger 8 | 9 | **Unsafe** does not mean "death", but it increases the probability of "death". Do not touch unsafe Mojo unless you know what you are doing and are willing to take the risk. 10 | 11 | ::: 12 | 13 | [[toc]] 14 | 15 | ## Unsafe realm 16 | 17 | Mojo is designed to be safe, by means of the ownership system and compile-time checks. However, there are some cases where "safe Mojo" is not able to (elegantly) handle. Thus, we have the other side of Mojo: **unsafe Mojo**. In this world, we can directly manipulate the memory and bypass the ownership system by means of unsafe pointers. Finally, it is you, but not the variable, who is the true owner of the values. 18 | 19 | Many language expose pointers directly to users, while others try to hide them. Mojo is in the middle: it has a safe pointer type `Pointer` which is designed to be checked against the ownership rule, and an unsafe pointer type `UnsafePointer` which is designed to bypass the ownership system. You are then responsible for ensuring the safety of your code. 20 | 21 | ::: info is "unsafe" a bad word? 22 | 23 | Some people do not like the term "unsafe" because it implies that the code is inherently dangerous or flawed. They prefer to use other, more neutral words like "raw pointers" or "trusted pointers" to describe the same concept. 24 | 25 | However, the term "unsafe" does not necessarily mean that memory issues will certainly happen, but it means that the probability of memory issues happening is higher than in safe Mojo. It servers more like a "remainder" than a "description". When you see this term, you will be alerted that you are entering a world where the compiler will not help you, and you need to be careful about what you are doing. 26 | 27 | ::: 28 | 29 | --- 30 | 31 | Recall that we have four ownership statuses in Mojo: "Isolated", "Referenced", "Pointed", and "Unsafely pointed". The first three statuses are safe, either by duplicating the value or by tracking the information of the owner. 32 | 33 | The last, however, is unsafe. It neither get a copy of the value, nor tracks the information of the owner. Instead, it directly points to an address, a space, in the memory. It likes a ghost that enters the house through the wall, reading the newspaper, changing the furniture, or even destroying everything. Nobody knows that it has come and gone, except for one person, you, the programmer. 34 | 35 | You have to ensure, on your own, that the address is valid (not uninitialized, not freed, not out of bounds), that the value is what you expect, that the type is correct, and that the value is not unintentionally modified by your access. You will manually validate whether the rules of ownership are followed. 36 | 37 | ## Unsafe pointers 38 | 39 | Unsafe pointers are the core of unsafe Mojo. Firstly, it is a type (defined as a struct in the Mojo standard library). 40 | -------------------------------------------------------------------------------- /src/miji/start/pixi.md: -------------------------------------------------------------------------------- 1 | # Install package manager for Mojo 2 | 3 | Mojo can be installed and run on MacOS and Linux. Currently, the support of Windows is not available. If you are using Windows, please use Windows Subsystem for Linux (WSL) to run Mojo. 4 | 5 | Before programming in Mojo, you need to first set up two things: 6 | 7 | 1. Install `pixi`, a command line (CLI) tool and package manager. 8 | 1. Install `mojo` extension in your IDE (I would say VS Code is the best choice for now). 9 | 10 | [[toc]] 11 | 12 | ## Install Pixi 13 | 14 | [Pixi](https://pixi.sh/latest/) is a package manager and virtual environment manager for Mojo (as well as other languages). You can install Pixi on MacOS, Linus, or Windows Subsystem for Linux (WSL) by running the following command in your terminal: 15 | 16 | ```bash 17 | curl -fsSL https://pixi.sh/install.sh | sh 18 | ``` 19 | 20 | Notes: Some instruction will be printed in your terminal. Read them carefully and run the `source` command or restart your terminal as instructed. 21 | 22 | Now, the `pixi` binary is installed in the directory `~/.pixi/bin`. 23 | 24 | ::: tip Modular family 25 | 26 | You may be confused by the names of Modular's products. Here is a brief introduction to the Modular family: 27 | 28 | | | Description | Official link | 29 | | ------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------ | 30 | | magic | Package and virtual environment manager (being deprecated) | [https://docs.modular.com/magic/](https://docs.modular.com/magic/) | 31 | | pixi | Package and virtual environment manager (to replace magic) | [https://pixi.sh/latest/](https://pixi.sh/latest/) | 32 | | modular | Company, platform, suite of AI libraries and tools | [https://www.modular.com/](https://www.modular.com/) | 33 | | max | GenAI graph compiler | [https://docs.modular.com/max/intro](https://docs.modular.com/max/intro) | 34 | | mojo | Programming language, mojo compiler | [https://docs.modular.com/mojo/manual/](https://docs.modular.com/mojo/manual/) | 35 | 36 | ::: 37 | 38 | ### Update pixi 39 | 40 | To update pixi, you can run the following command in your terminal: 41 | 42 | ```bash 43 | pixi self-update 44 | ``` 45 | 46 | ### Remove pixi 47 | 48 | To remove Pixi, you can run the following command in your terminal: 49 | 50 | ```bash 51 | rm ~/.pixi/bin 52 | ``` 53 | 54 | ## Install Mojo extension in VS Code 55 | 56 | To install Mojo extension in VS Code, you can search for "Mojo" in the "Extensions: Marketplace" Tab. You can also assess it via the link [https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo). 57 | 58 | There are two versions of the extension: `Mojo` and `Mojo (nightly)`. The first one is the stable version. For now, I recommend you to use the stable version, unless you want to try the latest features of Mojo. 59 | -------------------------------------------------------------------------------- /src/miji/extend/numojo.md: -------------------------------------------------------------------------------- 1 | # NuMojo 2 | 3 | NuMojo is a library for numerical computing in Mojo, similar to NumPy and SciPy in Python. [Link to the repo](https://github.com/Mojo-Numerics-and-Algorithms-group/NuMojo). 4 | 5 | I will discuss the conceptual models of the NDArray and how the functionalities are implemented internally. 6 | 7 | Some of the functionalities are not yet fulfilled due to Mojo, e.g., parameterized traits. We will incorporate them once they are ready. 8 | 9 | [[toc]] 10 | 11 | ## Array initializing 12 | 13 | Array initializing method, i.e., `__init__` constructs a pure container 14 | by reading in the shape and the stride information. 15 | Although the memory is allocated, it is not initialized. 16 | Users should always construct arrays using functions in creation routines. 17 | For example, `zeros`, `ones`, `randn`, `array`, etc. 18 | 19 | The following ways of array initializing are supported: 20 | 21 | 1. `NDArrayShape` and `order`. 22 | 2. `List` and `order`. 23 | 3. `VariadicList` and `order`. 24 | 25 | ## Array indexing and slicing 26 | 27 | ### Array indexing 28 | 29 | Array indexing always returns a scalar or an SIMD. 30 | 31 | "safe" means whether boundary checks are made. 32 | "order" means whether it evaluate the order or directly gets value from the underlying buffer. 33 | If yes, the strides information will be considered. 34 | If no, the indexing will based on the continuous allocation of the data buffer. 35 | 36 | | method (overload) | args | Ret | safe? | order? | notes | 37 | | ----------------- | ---- | ------ | ----- | ------ | --------------- | 38 | | `_getitem` | *Int | Scalar | no | yes | | 39 | | `__getitem__` | Idx | Scalar | yes | yes | | 40 | | `item` | Int | Scalar | yes | yes | `[0, size)` | 41 | | `item` | *Int | Scalar | yes | yes | `[0, shape[i])` | 42 | | `load` | Int | Scalar | yes | no | | 43 | | `load[width]` | Int | SIMD | yes | no | | 44 | | `load[width]` | *Int | SIMD | yes | no | | 45 | 46 | ### Array slicing 47 | 48 | Array slicing always returns an NDArray of the same number of dimensions or less. 49 | Currently, the returns are copies of the data. 50 | In future, when Mojo's trait are enhanced, slicing will returns a view of the data of the array. 51 | 52 | | method (overload) | args | safe? | order? | notes | 53 | | ----------------- | -------------------- | ----- | ------ | -------------------- | 54 | | `__getitem__` | Int | yes | yes | i-th row of input | 55 | | `__getitem__` | List[Int] | yes | yes | Rows of input | 56 | | `__getitem__` | List[Slice] | yes | yes | Same ndim as input | 57 | | `__getitem__` | *args[Slice] | yes | yes | Same ndim as input | 58 | | `__getitem__` | *Slice | yes | yes | Same ndim as input | 59 | | `__getitem__` | *Variant[Slice, Int] | yes | yes | Same or smaller ndim | 60 | | `__getitem__` | NDArray[DType.bool] | no | no | Items of buffer | 61 | | `__getitem__` | NDArray[DType.index] | no | no | Items of buffer | -------------------------------------------------------------------------------- /src/miji/start/magic.md: -------------------------------------------------------------------------------- 1 | # Install Magic 2 | 3 | ::: warning Deprecation of magic 4 | 5 | The `magic` manager is in the process of being deprecated. The alternative solution is `pixi`. Please consider go to the next chapter and install `pixi` instead. 6 | 7 | See the following post in the Moular forum for more information: [Migrating from Magic to Pixi](https://forum.modular.com/t/migrating-from-magic-to-pixi/1530). 8 | 9 | ::: 10 | 11 | Mojo can be installed and run on MacOS and Linux. Currently, the support of Windows is not available. If you are using Windows, please use Windows Subsystem for Linux (WSL) to run Mojo. 12 | 13 | Before programming in Mojo, you need to first set up two things: 14 | 15 | 1. Install `magic`, a command line (CLI) tool and package manager. 16 | 1. Install `mojo` extension in your IDE (I would say VS Code is the best choice for now). 17 | 18 | [[toc]] 19 | 20 | ## Install Magic CLI 21 | 22 | Magic is a package manager and virtual environment manager for Mojo (as well as other languages). It is based on pixi, so a lot of the commands are similar to pixi. 23 | 24 | You can install Magic CLI on MacOS or Linus by running the following command in your terminal: 25 | 26 | ```bash 27 | curl -ssL https://magic.modular.com/ff414efd-16ac-4bf3-8efc-50b059272ab6 | bash 28 | ``` 29 | 30 | Notes: Some instruction will be printed in your terminal. Read them carefully and run the `source` command as instructed. 31 | 32 | Now, Magic CLI is installed in the directory `~/.modular/`. 33 | 34 | ::: tip Modular family 35 | 36 | You may be confused by the names of Modular's products. Here is a brief introduction to the Modular family: 37 | 38 | | | Description | Official link | 39 | | ------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------ | 40 | | magic | Package and virtual environment manager, command line tool | [https://docs.modular.com/magic/](https://docs.modular.com/magic/) | 41 | | modular | Platform, suite of AI libraries and tools | [https://www.modular.com/](https://www.modular.com/) | 42 | | max | GenAI graph compiler | [https://docs.modular.com/max/intro](https://docs.modular.com/max/intro) | 43 | | mojo | Programming language | [https://docs.modular.com/mojo/manual/](https://docs.modular.com/mojo/manual/) | 44 | 45 | ::: 46 | 47 | ### Update Magic 48 | 49 | To update Magic CLI, you can run the following command in your terminal: 50 | 51 | ```bash 52 | magic self-update 53 | ``` 54 | 55 | ### Remove Magic 56 | 57 | To remove Magic CLI, you can run the following command in your terminal: 58 | 59 | ```bash 60 | rm ~/.modular/bin/magic 61 | ``` 62 | 63 | ## Install Mojo extension in VS Code 64 | 65 | To install Mojo extension in VS Code, you can search for "Mojo" in the "Extensions: Marketplace" Tab. You can also assess it via the link [https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo](https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo). 66 | 67 | There are two versions of the extension: `Mojo` and `Mojo (nightly)`. The first one is the stable version. For now, I recommend you to use the stable version, unless you want to try the latest features of Mojo. 68 | -------------------------------------------------------------------------------- /src/miji/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: doc 3 | nav: false 4 | --- 5 | 6 | # Mojo Miji - A Guide to Mojo Programming Language from A Pythonista's Perspective 7 | 8 | by ***Yuhao Zhu 朱宇浩*** 9 | 10 | ::: warning Notes 11 | 12 | This Miji is compatible with Mojo v0.25.6 (2025-09-22). 13 | 14 | For the example programs featured in this Miji, you can find them in the [GitHub repository](https://github.com/forfudan/my-first-mojo-project). 15 | 16 | ::: 17 | 18 | - [INTRODUCTION](./intro) 19 | - [PART I: START WITH MOJO](./start/start) 20 | 21 | - [Install pixi and extension](./start/pixi) (Package manager and VS Code extension) 22 | - [Initiate Mojo project](./start/project) (Install Mojo compiler) 23 | - [My first Mojo program](./start/hello) (Hello, world!) 24 | - [PART II: MOVE INTO MOJO](./move/move) 25 | - [Convert Python code into Mojo](./move/examples) (Four examples, Python becomes Mojo) 26 | - [Things that are in common](./move/common) (Similarities between Python and Mojo) 27 | - [Things that are different](./move/different) (Differences between Python and Mojo) 28 | - [PART III: BASIC MOJO](./basic/basic) 29 | - [Variables](./basic/variables) 30 | - [Copy and move](./basic/copy) 31 | - [Data types - basic](./basic/types) (Int, float, bool, etc.) 32 | - [Data types - composite](./basic/composite) (List et al.) 33 | - [Data type - string](./basic/string) (similar to `str` in Python) 34 | - [Literals and type inference](./basic/literal) 35 | - [Functions](./basic/functions) 36 | - [Operators and assignment expressions](./basic/operators) 37 | - [Control flows](./basic/control) (Loops and conditionals) 38 | - [Error handling and raises](./basic/errors) (Exceptions) 39 | - [Structs](./basic/structs) (Similar to classes in Python) 40 | - [Modules and packages](./basic/packages) (Modular programming) 41 | - [PART IV: ADVANCED MOJO](./advanced/advanced) 42 | - [Data type - SIMD](./advanced/simd) (The first-class `SIMD` type) 43 | - [Parameterization](./advanced/parameterization) 44 | - [Generic and traits](./advanced/generic) 45 | - [Ownership](./advanced/ownership) (The ownership system) 46 | - [References and pointers](./advanced/references) (The borrowing system) 47 | - [Lifetimes and origins](./advanced/lifetimes) (The lifetime system) 48 | 49 | - [PART V: APPLY MOJO](./apply/apply.md) (Let's use Mojo to program something useful) 50 | - [Design of MatMojo library](./apply/design.md) (Preparation for a matrix library) 51 | - [Make it work](./apply/work.md) (Define and implement the basic functionalities) 52 | - [PART VI: EXTEND MOJO](./extend/extend) 53 | - [Arbitrary-precision numbers](./extend/decimojo) (A library for arbitrary-precision integers and decimals) 54 | - [Multi-dimensional arrays](./extend/numojo) (A library for numerical computing and NDArray) 55 | - [PART VII: MISCELLANEOUS](./misc/misc) 56 | 57 | - [Glossary of Mojo terms](./misc/glossary) (Find explanations of key terms and concepts) 58 | - [Tips and warnings](./misc/tips) (Do not fall into the pits I fell into) 59 | - [Useful resources and links](./misc/resources) (Online resources and links related to Mojo) 60 | 61 | - [About the author](./misc/author) (A brief introduction of the author) 62 | 63 | -------------------------------------------------------------------------------- /src/miji/misc/wishes.md: -------------------------------------------------------------------------------- 1 | # Wishes 2 | 3 | Here are some features that I wish Mojo could have in the future. These are not necessarily bugs or issues, but rather enhancements that could improve the user experience and make Mojo more powerful and flexible in this AI era. 4 | 5 | ## Optional block ending keyword 6 | 7 | I wish Mojo could support an optional block ending keyword, such as `end` or `done`, to make auto-formatting more convenient. Consider that `end` is ready a common argument name in some functions, so `done` may be a better choice. Some Pythonistas use `pass` as a block ending keyword in the Python code, which is useful to detect mis-indentation in some cases, but it is not always working. 8 | 9 | For example, we have the following code: 10 | 11 | ```mojo 12 | fn main(): 13 | var a = 5 14 | var b = 10 15 | for i in range(3): 16 | print(a) 17 | if a < b: 18 | print("a is less than b") 19 | ``` 20 | 21 | If we want to copy the first for-loop block to another place, let's say into the code block of `if a < b:`, we have to manually add the indentation for the copied block by one level. This is because Mojo is indentation-sensitive, but the copied block will have two possible and valid indentations: 22 | 23 | 1. The original indentation of the copied block, which is outside the `if` statement. 24 | 1. One more level of indentation of the copied block, which is inside the `if` statement. 25 | 26 | ```mojo 27 | fn main(): 28 | var a = 5 29 | var b = 10 30 | for i in range(3): 31 | print(a) 32 | if a < b: 33 | print("a is less than b") 34 | for i in range(3): 35 | print(a) 36 | ``` 37 | 38 | or 39 | 40 | ```mojo 41 | fn main(): 42 | var a = 5 43 | var b = 10 44 | for i in range(3): 45 | print(a) 46 | if a < b: 47 | print("a is less than b") 48 | for i in range(3): 49 | print(a) 50 | ``` 51 | 52 | This is a simple case, so manual indentation is not a big deal. However, if we have a more complex code with multiple nested blocks, it will be very inconvenient to copy and paste the blocks and do manual adjustments ourselves. 53 | 54 | Nowadays, we also rely on AI tools to help us with code generation or revision. These generated code may not be indented correctly, and we have to manually adjust the indentation. This is prone to errors and may lead to unexpected behaviors. For example, if the indentation is not desired but the code runs anyway, just like the example above. 55 | 56 | An optional block ending keyword can help us avoid this problem. We can simply copy the block and paste it anywhere we want, without worrying about the indentation. The formatter will infer from the block ending keyword and automatically indent the block correctly. The first example, when added with the `done` keyword, will look like this: 57 | 58 | ```mojo 59 | fn main(): 60 | var a = 5 61 | var b = 10 62 | for i in range(3): 63 | print(a) 64 | done 65 | if a < b: 66 | print("a is less than b") 67 | done 68 | done 69 | ``` 70 | 71 | Let's do the copying of the for-loop again: 72 | 73 | ```mojo 74 | fn main(): 75 | var a = 5 76 | var b = 10 77 | for i in range(3): 78 | print(a) 79 | done 80 | if a < b: 81 | print("a is less than b") 82 | for i in range(3): 83 | print(a) 84 | done 85 | done 86 | done 87 | ``` 88 | 89 | Since there is a `done` keyword at the end of the code block of the if-statement, and we copied the code block of for-statement before the `done` keyword, the formatter will understand that the copied block is a nested one and will be automatically indented by one level. 90 | 91 | This way, we can avoid manual indentation and reduce the risk of errors. 92 | 93 | The ending keyword is not mandatory, but optional. So it is ignored by the compiler, but will be used by the formatter to format the code or by the linter to check whether the indentation is correct. 94 | 95 | Moreover, the Mojo extension in VS Code can also use inlay hints to show the ending keyword at the end of each block, so that we can easily check whether the indentation is correct or not. 96 | -------------------------------------------------------------------------------- /src/miji/start/hello.md: -------------------------------------------------------------------------------- 1 | # My first Mojo program 2 | 3 | Now we have set up everything, finally. In this chapter, let's write our first Mojo program and run it. 4 | 5 | [[toc]] 6 | 7 | ## Hello, world 8 | 9 | Let's create a folder in our project directory called `src`. Then we create a sub-folder `start`. Within this folder, we create a file called `hello.mojo`. Your project directory should look like this: 10 | 11 | ```console 12 | my-first-mojo-project 13 | ├── pixi.toml 14 | └── src 15 | └── start 16 | └── hello.mojo 17 | ``` 18 | 19 | We open the `hello.mojo` file and type the following code: 20 | 21 | ::: code-group 22 | 23 | ```mojo 24 | def main(): 25 | print("Hello, world!") 26 | ``` 27 | 28 | ::: 29 | 30 | You may be surprised (or even a little bit disappointed) to see that the code is 100% identical to Python code. But yes, it is just the design philosophy of Mojo. It is designed to similar to Python, so that a Python user, like you, can learn it easily. 31 | 32 | Now, let's run this code. You can type the following command in your terminal: 33 | 34 | ::: code-group 35 | 36 | ```bash 37 | pixi run mojo src/start/hello.mojo 38 | ``` 39 | 40 | ::: 41 | 42 | And you will see the output: 43 | 44 | ```console 45 | Hello, World! 46 | ``` 47 | 48 | Congratulations! You have successfully run your first Mojo program. 🎉 49 | 50 | ## What has happened? 51 | 52 | You may ask have many questions in your mind: Why I have to define a function called `main()`? What happens when I run `pixi run mojo src/start/hello.mojo`? What is the difference between `pixi run mojo` and `python`? 53 | 54 | Here are the answers to your questions. 55 | 56 | ### The `main()` function 57 | 58 | This might be the first difference you notice between Mojo and Python. In Python, you can run code in a file without defining a main function. However, in Mojo, you have to define a function called `main()` to run your code. By putting the code in the `main()` function, you tell Mojo that this is the **entry point** of your program. The mojo compiler will execute the code from the `main()` function. 59 | 60 | We will come back to this in Section [The main function](../basic/functions.md#the-main-function) in Chapter [Functions](../basic/functions.md). 61 | 62 | ::: tip `main()` function in Python 63 | 64 | Some Python users also define a `main()` function in their Python code. But they have to write `main()` so that Python interpreter will run the code within the `main()` function. For example, 65 | 66 | ```python 67 | def main(): 68 | print("Hello, world!") 69 | main() # Run the main function 70 | ``` 71 | 72 | In Mojo, you do not need to write the last line. 73 | 74 | ::: 75 | 76 | ### Behind `pixi run mojo file.mojo` 77 | 78 | `pixi run mojo file.mojo` is a short form of `pixi run mojo run file.mojo`. There are two steps: 79 | 80 | 1. `pixi run mojo`: You tells pixi to run the Mojo compiler that is installed in your current environment (`.pixi/envs`). 81 | 1. `mojo file.mojo` You asks Mojo compiler to run the `file.mojo` file. 82 | 83 | You may find the second step similar to `python file.py`, where you ask Python interpreter to run the `file.py` file. 84 | The output of running the `hello.mojo` file with Mojo is also the same as the output of running the `hello.py` file with Python. 85 | 86 | However, there are some key differences between `mojo file.mojo` and `python file.py`. In Python, the software "Python" (we call it an "interpreter") reads the `file.py` file and executes the code in it. The python interpreter has to be kept running in the background in order to generate the output "hello, world!". 87 | 88 | On the contrary, in Mojo, the process is a bit different: 89 | 90 | - The Mojo compiler first reads the `file.mojo` file and compiles it into a binary executable file. After compilation, the Mojo compiler is no longer needed. This step can also be done by running `pixi run mojo build file.mojo`. 91 | - The binary executable file is executed alone. It generates the output "hello, world!". This step can also be done by running `./file`. 92 | 93 | ::: tip Two commands 94 | 95 | `pixi run mojo file.mojo` is actually a short cut for the following two commands. You can try yourself: 96 | 97 | ```bash 98 | pixi run mojo build src/hello.mojo # Compile the Mojo file 99 | ./hello # Run the compiled binary file 100 | ``` 101 | 102 | The first command complies the `hello.mojo` file into a binary executable file called `hello` in the root directory. The second command runs the `hello` file to get the output "hello, world!". You can test it in your terminal. 103 | 104 | You can copy this "hello" file to another computer (same OS) and run it without installing Mojo. This is a feature that Python does not have. 105 | 106 | ::: 107 | 108 | ## Next step 109 | 110 | Now you have successfully run your first Mojo program. I believe that you cannot wait to write another mojo script. For example, something like this: 111 | 112 | ::: code-group 113 | 114 | ```mojo 115 | def main(): 116 | a = 1 117 | b = 2.0 118 | print(a + b) 119 | ``` 120 | 121 | ::: 122 | 123 | Then, let's go to the next Part of the Miji, where we try two find out a quick path for you to migrate from the Pythonic world to the Magician world. 124 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mojo Miji 2 | 3 | This is the repository of the [Mojo Miji, a guide to Mojo programming language from a Pythonista's perspective](https://mojo-lang.com), written by Yuhao Zhu. 4 | 5 | The example programs featured in the book can be found in [this repo](https://github.com/forfudan/my-first-mojo-project). 6 | 7 | > [!NOTE] 8 | >Please considering raising an issue or start a discussion if you find any errors or inaccuracies in this Miji. Because the book is constantly updated to keep up with the fast-evolving Mojo language, direct PR is not encouraged, as it may cause conflicts. Thank you for your understanding and support! 9 | 10 | Mojo is a new programming language developed by Modular, a company founded by Chris Lattner, the creator of Swift and LLVM. The language was made known to the public in 2023 and it has been open-sourcing its [standard library](https://github.com/modular/modular) gradually since early 2024. Mojo is initially designed to be a superset of the Python programming language, incorporating modern features such as static typing, ownership, traits, and meta-programming. These enhancements allow Python users to transition easily while benefiting from increased execution speed and enhanced safety. Mojo’s long-term goal is to be fully compatible with Python syntax and ecosystem. 11 | 12 | I started to use Mojo at the early stage of its development in early 2024. Later, I participated in the development of the [NuMojo library](https://github.com/Mojo-Numerics-and-Algorithms-group/NuMojo), which is a library for numerical computing similar to NumPy, SciPy in Python. During this process, Mojo is growing, so do I. I learned a lot about Mojo and found it to be a powerful, promising, attractive, and fun programming language. I also encountered many challenges and pitfalls that I had to overcome. Mojo's syntax and features were quite similar to Python, but there were also significant differences that required careful attention. 13 | 14 | That is why I decided to write this book, the Mojo Miji, to introduce the most important features and functionalities of Mojo from a Pythonista's perspective. It is designed to help Python users quickly adapt to Mojo, highlighting the similarities and differences between the two languages. The guide will cover various aspects of Mojo, including its coding style, syntax, data types, control structures, meta-programming, ownership semantics, and third-party packages. 15 | 16 | The term "Miji" is derived from the Chinese word "", which means "secret book" or "manual". It is often used to refer to a guide or handbook that provides valuable insights, tips, and shortcuts for mastering a particular subject. Since it is called "secret book", it does not necessarily follow the same structure, conceptual models, or definitions as in a conventional tutorial. Instead, it tries to provide **additional** insights, tips, conceptual models that are not present in the official documentation but would be helpful for understanding Mojo from a Pythonista's perspective, e.g., quaternary system of variable, four statuses of ownership, etc. You can read this Miji as a **complementary** resource to the [official Mojo manual](https://docs.modular.com/mojo/manual/). 17 | 18 | In that sense, throughout this Miji, I will focus on providing concrete examples and exercises in both Python and Mojo, allowing readers to easily compare the two languages. I will also draw graphs and diagrams to illustrate memory layouts or internal representations of Mojo objects, since Mojo emphasizes the importance of memory safety. Finally, I will tell you about the pits that I fell into when I used Mojo, so that you can avoid them. 19 | 20 | You do not need to be an expert in Python to read this Miji, but you should either have a basic understanding of Python, such as data types, control structures, functions, classes, and modules. Or, you have experience in programming with another static-typed programming language, such as C, C++, Rust, or Java. If you are a Pythonista, you will be more comfortable with reading this Miji. 21 | 22 | This Miji Book is expected to be updated continuously over a long period (around five years). Many of the examples and explanations in this Miji may become outdated as Mojo evolves. Thank you in advance for your understanding and patience! If you see any errors or inaccuracies in this Miji, please feel free to open an issue. Your feedback is greatly appreciated. 23 | 24 | ## License 25 | 26 | The following license applies to the book Mojo Miji. 27 | 28 | ```txt 29 | Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 30 | International 31 | 32 | Copyright (c) 2024-present, Yuhao Zhu (朱宇浩) 33 | ``` 34 | 35 | The following license applies to the template of the website (Vitepress) 36 | 37 | ```txt 38 | MIT License 39 | 40 | Copyright (c) 2019-present, Yuxi (Evan) You 41 | 42 | Permission is hereby granted, free of charge, to any person obtaining a copy 43 | of this software and associated documentation files (the "Software"), to deal 44 | in the Software without restriction, including without limitation the rights 45 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 46 | copies of the Software, and to permit persons to whom the Software is 47 | furnished to do so, subject to the following conditions: 48 | 49 | The above copyright notice and this permission notice shall be included in all 50 | copies or substantial portions of the Software. 51 | 52 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 53 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 54 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 55 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 56 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 57 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 58 | SOFTWARE. 59 | ``` 60 | -------------------------------------------------------------------------------- /src/numojo/view.md: -------------------------------------------------------------------------------- 1 | # NDArray View 2 | 3 | ## Introduction 4 | 5 | Array view is a special kind of array who does not own the underlying data but use a reference to the data owned by other arrays. 6 | 7 | The array view is particularly helpful if you want to obtain the sliced array without copying the underlying data. The time consumed in creating slices is constant. 8 | 9 | (Note: This feature is not yet implement but a proposal.) 10 | 11 | ## Data structure 12 | 13 | Recall that `NDArray` is defined as 14 | 15 | ```mojo 16 | struct NDArray[dtype: DType = DType.float64, Buffer: Bufferable = OwnedData](Stringable, Writable): 17 | ``` 18 | 19 | The array view is actually: 20 | 21 | ```mojo 22 | NDArray[dtype, Buffer = RefData[__origin_of(existing_array)] 23 | ``` 24 | 25 | The underlying data buffer of `NDArray` is `OwnedData`, which is a wrapper of owned `UnsafePointer`. The underlying data buffer of view of data is `RefData`, which is a wrapper of referenced `UnsafePointer` of another array. 26 | 27 | Both `OwnedData` and `RefData` are of the `Bufferable` trait. You can always obtain the pointer to the underying buffer of array `a`, no matter it is a view or not, by: 28 | 29 | ```mojo 30 | a._buf.get_ptr() # UnsafePointer 31 | ``` 32 | 33 | ## Constructing view 34 | 35 | Constructing a view is simple: Passing a reference to the underlying data buffer, and specify the shape and strides information of the view. No copying of data happens in this process. 36 | 37 | ```mojo 38 | fn __init__( 39 | out self, 40 | shape: NDArrayShape, 41 | strides: NDArrayStrides, 42 | ptr: UnsafePointer[Scalar[dtype], 43 | offset: Int, 44 | ): 45 | self.shape = shape 46 | self.strides = strides 47 | self.size = self.shape.size 48 | self._buf = Buffer(ptr=ptr+offset) 49 | ``` 50 | 51 | See, the array view will simply re-use the data buffer of an existing array (mutable reference), but with some offset in pointer, new shape, and new strides information. 52 | 53 | ## Offset, shape, and strides of view 54 | 55 | The shape and strides of the view can be determined based on: 56 | 57 | - The shape and strides of the original array. 58 | - The start, end, and step of the slices. 59 | 60 | Here is an example, an 6x8 array with 48 items. The values in the box can be seen as both the addresses of vitual memory and the values stored there. 61 | 62 | ```console 63 | a 64 | ┌──┬──┬──┬──┬──┬──┬──┬──┐ 65 | │ 0│ 1│ 2│ 3│ 4│ 5│ 6│ 7│ 66 | ├──┼──┼──┼──┼──┼──┼──┼──┤ 67 | │ 8│ 9│10│11│12│13│14│15│ 68 | ├──┼──┼──┼──┼──┼──┼──┼──┤ 69 | │16│17│18│19│20│21│22│23│ 70 | ├──┼──┼──┼──┼──┼──┼──┼──┤ 71 | │24│25│26│27│28│29│30│31│ 72 | ├──┼──┼──┼──┼──┼──┼──┼──┤ 73 | │32│33│34│35│36│37│38│39│ 74 | ├──┼──┼──┼──┼──┼──┼──┼──┤ 75 | │40│41│42│43│44│45│46│47│ 76 | └──┴──┴──┴──┴──┴──┴──┴──┘ 77 | ``` 78 | 79 | This array `a` has `shape=(6,8)` and `strides=(8,1)`. 80 | 81 | Now we want to obtain a slice of the array `a` as a view. Let's say `b = a[1:6:2, 2:8:2]`. By taking out all the values needed, the array view `b` should look like as follows. 82 | 83 | ```console 84 | b = a[1:6:2, 2:8:2] 85 | ┌──┬──┬──┐ 86 | │10│12│14│ 87 | ├──┼──┼──┤ 88 | │26│28│30│ 89 | ├──┼──┼──┤ 90 | │42│44│46│ 91 | └──┴──┴──┘ 92 | ``` 93 | 94 | This array view `b` has `offset=10`, `shape=(3,3)` and `strides=(16,2)`. 95 | 96 | A set of more general formulae to get information of array view goes as follows. 97 | 98 | $$ 99 | \begin{aligned} 100 | offset_b &= \overrightarrow{start} \cdot \overrightarrow{strides_a}\\ 101 | shape_b &= \lceil \frac{\overrightarrow{end} - \overrightarrow{start}}{\overrightarrow{step}} \rceil \text{(item-wise)}\\ 102 | stride_b &= \overrightarrow{step} \cdot \overrightarrow{strides_a} 103 | \end{aligned} 104 | $$ 105 | 106 | Here are shape and strides for other operations: 107 | 108 | - `transpose`: Just swap the corresponding items of shape and strides. 109 | - `flip`: It is equivalent to slice `[len-1: -1: -1]`. 110 | 111 | ## View indexing 112 | 113 | Recall that the buffer location of a value can be calculated by the following formula: 114 | 115 | $$ 116 | \text{Buffer address} = offset + \overrightarrow{index} \cdot \overrightarrow{strides} 117 | $$ 118 | 119 | So, item `(1, 1)` of array view `b` has the adress `10 + 1*16 + 1*2 = 28`. item `(2, 0)` of array view `b` has the adress `10 + 2*16 + 0*2 = 42`. This is exactly as displayed in the figure above. 120 | 121 | ## Slice of whole axis 122 | 123 | Sometimes we want to retrieve a whole axis of an array (row of matrix, plain of ndarray, etc). For example, `a[1]` or `a[1, :]` returns the second row of the matrix `a` above, which would be: 124 | 125 | ```console 126 | a[1]: 127 | ┌──┬──┬──┬──┬──┬──┬──┬──┐ 128 | │ 8│ 9│10│11│12│13│14│15│ 129 | └──┴──┴──┴──┴──┴──┴──┴──┘ 130 | ``` 131 | 132 | In this sense, the original strides information will not be useful (no matter the ndim decreases by 1 or not) because you never travel to the next row. 133 | 134 | In `numpy`, the strides can be arbitary when the `shape[i] == 1`, which means that F-continous being true does not necessarily mean that `strides[0] == itemsize`, and that C-continous being true does not necessarily mean that `strides[-1] == itemsize`. 135 | 136 | ```python 137 | >>> a = np.arange(9).reshape((3,3)) 138 | >>> b = a[::100, :] 139 | >>> print(a.flags) 140 | (24, 8) 141 | >>> print(b.flags) 142 | C_CONTIGUOUS : True 143 | F_CONTIGUOUS : True 144 | OWNDATA : False 145 | WRITEABLE : True 146 | ALIGNED : True 147 | WRITEBACKIFCOPY : False 148 | >>> print(b.strides) 149 | (2400, 8) 150 | ``` 151 | 152 | **In Numojo, the slices shall be first normalized so that the flag will reflect the true memory layout.** 153 | 154 | ## Decrease of ndim 155 | 156 | Sometimes, slice results in decrease of dimensions, e.g., `a[1]`. The new shape and the new strides can be determined by dropping the corresponding axis. The offset can be calcuated using: 157 | 158 | $$ 159 | offset_b = \overrightarrow{start} \cdot \overrightarrow{strides_a} 160 | $$ 161 | 162 | For instance, `a` has shape `(6, 8)` and strides `(8, 1)`. 163 | 164 | Then, `b = a[1]` has shape `(8)`, strides `(1)`, and offset `8`. 165 | $$ 166 | offset_b = (1, 0) \cdot (8, 1) = 8 167 | $$ 168 | 169 | ```console 170 | b = a[1]: 171 | ┌──┬──┬──┬──┬──┬──┬──┬──┐ 172 | │ 8│ 9│10│11│12│13│14│15│ 173 | └──┴──┴──┴──┴──┴──┴──┴──┘ 174 | ``` 175 | 176 | ## SIMD operations 177 | 178 | To perform SIMD operations on view of array, we need to verify whether the items are still continously store in memory. This can be checked by: 179 | 180 | - If `strides[-1] = 1`, then it is continous on the last axis (C-continuous). 181 | - If `strides[0] = 1`, then it is continous on the first axis (F-continuous). 182 | - For 1darray, it can both be C-continous and F-continuous. 183 | 184 | If the checks pass, we can still load and store more than one items via SIMD. Otherwise, we should load the values individually with their indices. 185 | 186 | (Notes: `order=="F"` should be replaced by `strides[0]==1` in future.) 187 | -------------------------------------------------------------------------------- /src/miji/misc/tips.md: -------------------------------------------------------------------------------- 1 | # Information, tips, and warnings 2 | 3 | This chapter summarizes the information, tips, and warnings that are scattered throughout the Miji. It is intended to help you quickly find the information you need without having to search through the entire Miji. 4 | 5 | [Speed comparison with C and Rust](../move/examples#fibonacci-sequence): Do you know that Mojo is faster than C and Rust in some cases? I am also surprised by the results of the speed comparison. 6 | 7 | [Docstring style guide](../move/common#documentation-string): There are many styles of docstring in Python. Some people use Google style, some people use NumPy style, and some people use reStructuredText (reST) style. Do you know that Mojo has its own [docstring style guide](https://github.com/modular/modular/blob/main/mojo/stdlib/docs/docstring-style-guide.md)? 8 | 9 | [Graph: Memory layout of a variable](../basic/variables): This graph illustrates how a variable of `Int` type is laid out in memory. 10 | 11 | [Why you should always use `var` to define variables](../basic/variables.md): It is good to cultivate a habit of using `var` to define variables. Otherwise, it may lead to confusion and unintended behavior, especially when you are working with nested scopes. 12 | 13 | [Type is important](../basic/variables.md#conceptual-model-of-mojo-variables): Type is important to a variable. It determines how the value is stored in memory, how much space it occupies, how it can be interpreted, and how it can be manipulated. 14 | 15 | [Do not abuse function overloading](../basic/functions.md#function-overloading): Functional overloading is powerful, but it can lead to confusion if you use it as a general container for anything. You should always make the function names self-explanatory. 16 | 17 | [Graph: `read` and `var` arguments in memory](../basic/functions#mutability-of-arguments): These graphs illustrate how `read` and `var` arguments are laid out in memory and how they are interacted with the variables you pass to the function. 18 | 19 | [Arguments and reference - Mojo vs Rust](../basic/functions.md): Do you know that the term "reference" means different things in Mojo and Rust? 20 | 21 | [Why we should use constructors explicitly](../basic/types#integers): Let's do an exercise to understand why we should always use constructors explicitly. 22 | 23 | [R-value and L-value](../basic/types#literals-and-type-inference): Do you know that a value can be either an r-value or an l-value? 24 | 25 | [Floating-point values are inexact](../basic/types#floating-point-numbers): Are you aware that floating-point values are inexact and can lead to unexpected results? This is because floating-point values are represented in binary format, which cannot always be exactly represented in decimal format. 26 | 27 | [Is character a grapheme cluster or a code point?](../basic/string#grapheme-clusters): Do you know that a "character" can mean different things? It can be a single byte, a code point, or a grapheme cluster. So you should be more explicit about what you mean by "character" in your code. 28 | 29 | [Visual checks of valid UTF-8 code points](../basic/string#utf-8-encoding): Do you know that, in the first byte of a valid UTF-8 code point, the number of leading ones is equal to the number of bytes for non-single-byte code point. 30 | 31 | [Boundary checks on lists are not on by default](../basic/composite#index-or-slice-a-list): Do you know that Mojo does not perform boundary checks on lists by default? This means that if you try to access an index that is out of bounds, the program will not raise an error. This might be very dangerous. Check this section for more details. 32 | 33 | [Chained comparison: Syntax sugar or poison?](../basic/operators.md): Are you aware that chained comparison can lead to confusion and unintended behavior? 34 | 35 | [Non-exhaustive conditionals - Mojo vs Python](../basic/control#non-exhaustive-conditionals): Do you know that non-exhaustive conditionals would lead to compile-time errors in Mojo if you do not handle all possible cases for functions that return a value? This is different from Python, where non-exhaustive conditionals would not lead to errors. 36 | 37 | [Graph: Memory layout of a struct](../basic/structs#memory-layout-of-struct): This graph illustrate how a struct is laid out in memory. 38 | 39 | [Fetch value of a field in a struct](../basic/structs#memory-layout-of-struct): Are you curious about how Mojo fetches the value of a field in a struct, e.g., `human.age`? It calculates the offset of the field from the start of the struct in memory, and then reads the value with the address. 40 | 41 | [Verify the memory layout of a struct](../basic/structs#memory-layout-of-struct): Do you know that you can verify the memory layout of a struct by using `bitcast()` method of `UnsafePointer`? But use it with caution. 42 | 43 | [`Float64` or `SIMD[DType.float64, 1]`](../advanced/simd.md#type-of-simd): You can use either `Float64` or `SIMD[DType.float64, 1]` to represent a single-precision floating-point value, because the former one is just an alias of the latter. In most cases, using the former one is more concise and readable. 44 | 45 | [`main()` function in a package](../basic/packages#write-packages): Do you know that you cannot run a file with a `main()` function when the file is in a package? Check this section for more details. 46 | 47 | [`Int` and `Bool` are not SIMD types](../advanced/simd.md#type-of-simd): Do you know that the built-in type `Int` and `Bool` are not aliases of SIMD? The corresponding SIMD types are actually `SIMD[DType.index, 1]` or `SIMD[DType.bool, 1]`, respectively. So, don't be surprised if the compiler complains about the type mismatch when you try to use `Int` or `Bool` in a SIMD context. 48 | 49 | [Implicit and explicit trait declaration](../advanced/generic#traits): Do you know that Mojo allows you to use traits either implicitly or explicitly? You do not need to put the name a trait in the struct declaration. As long as you define all the methods that the trait requires, the compiler will automatically treat the struct as conforming to the trait. It is useful when you want to apply some self-defined traits on a built-in type. 50 | 51 | [Use multiple traits in functions](../advanced/generic#multiple-traits): At the moment, Mojo does not support using multiple traits for a type parameter in functions. 52 | 53 | [Syntax shapes your behavior - Rust vs Mojo in transfer of ownership](../advanced/ownership.md): Are you aware that your coding style is influenced by the syntax of the programming language you use? 54 | 55 | [You can be still safe without knowing the ownership model](../advanced/ownership.md): Do you know that you can still be safe without understanding the ownership model? But this does not apply if you begin to dive into the ocean of unsafe Mojo. 56 | 57 | [Non-transferable values](../advanced/ownership.md): Do you know that some values cannot be transferred to another owner? This is because they are small enough to be copied efficiently. If you use `^` to transfer ownership of such values, the compiler will ignore this and treat them as if they were copied. 58 | 59 | [ASAP Destruction Policy](../advanced/ownership.md): Do you know that Mojo compiler has a policy that destroys a variable as soon as they are used for the last time? However, any safe pointers to the variable can extend the lifetime of the variable to as long as the pointer is alive. 60 | 61 | [Inconsistent behaviors of copy and move](../advanced/ownership.md): If you define different behaviors for `__copyinit__()` and `__moveinit__` in a struct, you may encounter unexpected behaviors when you use the assignment operator `=`. This is because the Mojo compiler will call the `__moveinit__()` method if the variable is not used after the assignment. So, you should always define the same behavior for both methods. 62 | 63 | [Historical keywords of references conventions](../advanced/references#keywords-of-conventions): Do you know that the keywords `read`, `mut`, and `var` were not always the keywords used to define the ownership and mutability of function arguments? You can find the historical keywords and the versions of Mojo when they were introduced and deprecated. 64 | -------------------------------------------------------------------------------- /src/miji/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | > Everything is about programming except for programming itself. Programming is about control. 4 | > -- Yuhao Zhu, *Gate of Heaven* 5 | 6 | Mojo is a new programming language developed by Modular, a company founded by [Chris Lattner](https://www.nondot.org/sabre/), the creator of Swift and LLVM. The language was made known to the public in 2023 and it has been open-sourcing its [standard library](https://github.com/modular/modular) gradually since early 2024. Mojo is initially designed to be a superset of the Python programming language, incorporating modern features such as static typing, ownership, traits, and meta-programming. These enhancements allow Python users to transition easily while benefiting from increased execution speed and enhanced safety. In the long term, it can be fully compatible with Python syntax and ecosystem. In early 2025, this objective shifted toward making Mojo a programming language that uses Python's syntax and interacts seamlessly with Python code. 7 | 8 | I started to use Mojo at the early stage of its development in early 2024. Later, I participated in the development of the [NuMojo library](https://github.com/Mojo-Numerics-and-Algorithms-group/NuMojo), which is a library for numerical computing similar to NumPy, SciPy in Python. During this process, Mojo is growing, so do I. I learned a lot about Mojo and found it to be a powerful, promising, attractive, and fun programming language. I also encountered many challenges and pitfalls that I had to overcome. Mojo's syntax and features were quite similar to Python, but there were also significant differences that required careful attention. 9 | 10 | That is why I decided to write this book, the Mojo Miji, to introduce the most important features and functionalities of Mojo from a Pythonista's perspective. It is designed to help Python users quickly adapt to Mojo, highlighting the similarities and differences between the two languages. The guide will cover various aspects of Mojo, including its coding style, syntax, data types, control structures, meta-programming, ownership semantics, and third-party packages. 11 | 12 | The term "Miji" is derived from the Chinese word "", which means "secret book" or "manual". It is often used to refer to a guide or handbook that provides valuable insights, tips, and shortcuts for mastering a particular subject. Since it is called "secret book", it does not necessarily follow the same structure, conceptual models, or definitions as in a conventional tutorial. Instead, it tries to provide **additional** insights, tips, conceptual models that are not present in the official documentation but would be helpful for understanding Mojo from a Pythonista's perspective, e.g., [quaternary system of variable](./basic/variables#conceptual-model-of-mojo-variable), [four statuses of ownership](./advanced/ownership#four-statuses-of-ownership), etc. You can read this Miji as a **complementary** resource to the [official Mojo manual](https://docs.modular.com/mojo/manual/). 13 | 14 | In that sense, throughout this Miji, I will focus on providing concrete examples and exercises in both Python and Mojo, allowing readers to easily compare the two languages. I will also draw graphs and diagrams to illustrate memory layouts or internal representations of Mojo objects, since Mojo emphasizes the importance of memory safety. Finally, I will tell you about the [pits that I fell into](./misc/tips) when I used Mojo, so that you can avoid them. 15 | 16 | You do not need to be an expert in Python to read this Miji, but you should either have a basic understanding of Python, such as data types, control structures, functions, classes, and modules. Or, you have experience in programming with another static-typed programming language, such as C, C++, Rust, or Java. If you are a Pythonista, you will be more comfortable with reading this Miji. 17 | 18 | This Miji will be structured as follows: 19 | 20 | 1. [START](./start/start): The first part introduces how set up the Mojo environment on your computer so that you are able to write your [first Mojo program](./start/hello) (yes, it is "hello, world!"). 21 | 1. [MOVE](./move/move): The second part shows you how to [convert Python code into Mojo code](./move/examples). It starts with 4 concrete sample programs, then moves to the [similarities](./move/common), and finally presents the [differences](./move/different) between the two languages. If you are familiar with Python, after reading this part, you will be able to write Mojo code with a certain level of confidence. *If you have no experience in Python, you can skip this part.* 22 | 1. [BASIC](./basic/basic): The third part covers the basic features of Mojo, starting with a [conceptual model of variables](./basic/variables), and then introducing [functions](./basic/functions), [data types](./basic/types), [operators](./basic/operators), [structs](./basic/structs), [control flows](./basic/control), [error handling](./basic/errors), [modules](./basic/packages), etc. These features are also present in Python, but Mojo has some differences that you should be aware of. This part will help you to understand the basic syntax and semantics of Mojo. 23 | 1. [ADVANCED](./advanced/advanced): The fourth part introduces some more advanced features of Mojo, such as [SIMD](./advanced/simd), [parameterization](./advanced/parameterization), [generic](./advanced/generic), [ownership](./advanced/ownership), [reference system](./advanced/references), [lifetime system](./advanced/lifetimes) etc. These features are not present in Python, but they are essential for writing efficient and safe Mojo code. This part will help you become a more proficient Mojo programmer, a true Magician. 24 | 1. [APPLY](./apply/apply): The fifth part consists of a small case study, in which we use Mojo to build a Matrix type, similar to NumPy's. We will apply the features we have learned in the previous parts to implement this type. 25 | 1. [EXTEND](./extend/extend): The sixth part introduces several third-party packages that extend the functionality of Mojo. For efficiency, the standard library of Mojo cannot provide all the features. These third-party packages may help fulfill our needs. 26 | 1. [MISCELLANEOUS](./misc/misc): The final part covers miscellaneous topics, which may be further integrated into the previous parts in the future. This part also includes a [glossary](./misc/glossary) of key terms and concepts related to Mojo, as well as a list of [information, tips, and warnings](./misc/tips) that are scattered throughout the Miji. It is particularly helpful if you are already a Magician but want to avoid common pitfalls and mistakes that I encountered while using Mojo. 27 | 28 | Please be fully aware that Mojo is still in the early stages of development, with significant syntax changes and potential keyword alterations. The Mojo compiler is written in C++, is not yet open-sourced, and has not achieved self-hosting (compiling its own code)^[https://github.com/modularml/mojo/discussions/3049]. The official team has no immediate plans for this. 29 | 30 | Therefore, this Miji is expected to be updated continuously over a long period (around five years). Many of the examples and explanations in this Miji may become outdated as Mojo evolves. Thank you in advance for your understanding and patience! If you see any errors or inaccuracies in this Miji, please feel free to open an issue on the [GitHub repository](https://github.com/forfudan/MojoMiji). Your feedback is greatly appreciated. 31 | 32 | Yuhao Zhu 33 | May 2025 34 | Rotterdam, the Netherlands 35 | 36 | ::: info Quick glance at Mojo's syntax 37 | 38 | Mojo borrows Python’s syntax style, making it very similar to Python code with type annotations. In terms of usage, Mojo allows for easy importation of Python functions and packages. Below are examples of Python and Mojo code, demonstrating their similar styles. 39 | 40 | ::: code-group 41 | 42 | ```mojo 43 | a = String("Hello, world!") 44 | print(a) 45 | 46 | b: Int = 8 47 | for i in range(10): 48 | b = b + i 49 | print(b) 50 | ``` 51 | 52 | ```python 53 | a = str("Hello, world!") 54 | print(a) 55 | 56 | b: int = 8 57 | for i in range(10): 58 | b = b + i 59 | print(b) 60 | ``` 61 | 62 | ::: 63 | -------------------------------------------------------------------------------- /src/miji/start/project.md: -------------------------------------------------------------------------------- 1 | # Initiate Mojo project 2 | 3 | Now we have installed the pixi CLI. In this chapter, we will cover how to create a Mojo project and install the Mojo compiler. 4 | 5 | [[toc]] 6 | 7 | ## Virtual environment 8 | 9 | As a Python user, you may or may not be familiar with the concept of virtual environments. Many people install a certain version of Python and packages globally. No matter where your Python code is, you can always run it using the Python interpreter with the command `python file.py`. 10 | 11 | While being convenient, this approach has some drawbacks. For example, you have a project which is relying on version 1.9 of `numpy` and another project which is relying on version 2.0 of `numpy`. If you install `numpy` version 1.9 globally, you will not be able to run the second project. You have to update your `numpy` to version 2.0, after which the first project cannot be run anymore. 12 | 13 | For light users or when you are working alone, this may not be a problem: you always install the latest version of the package and update the code to be compatible with the latest version. However, if you are working with other people, or you are working on a larger projects, this can be a problem. You cannot always update or downgrade the package manually. 14 | 15 | This brings us to the concept of virtual environment, which is actually an isolated directory that contains a particular version of Python and particular versions of packages. When you run a Python script within in this virtual environment, it will use the Python interpreter and packages in this directory. 16 | 17 | Since the Python versions are project-specific and are located in different folders, you can working on multiple projects with different dependencies without any conflicts. 18 | 19 | ## Create new Mojo project 20 | 21 | ::: tip Repository of the project 22 | 23 | You can find the example programs in this Miji in the [GitHub repository](https://github.com/forfudan/my-first-mojo-project). 24 | 25 | ::: 26 | 27 | Mojo relies on the concept of virtual environment. Each mojo project contains a folder with specific Mojo version and packages. 28 | When you run a Mojo script, it will use the Mojo compiler and packages only in this folder. 29 | 30 | Therefore, before we start writing our first Mojo script, we need to first create a Mojo project (and the virtual environment). 31 | 32 | To do this, you can navigate to the directory where you want to create your Mojo project folder. For me, it is `/Users/ZHU/Programs`. Then run the following command in your terminal. 33 | 34 | ```bash 35 | pixi init my-first-mojo-project -c "https://conda.modular.com/max" -c "https://repo.prefix.dev/modular-community" -c "conda-forge" 36 | ``` 37 | 38 | This will create a folder called `my-first-mojo-project` in the current directory. You can go in to this folder by typing: 39 | 40 | ```bash 41 | cd my-first-mojo-project 42 | ``` 43 | 44 | Or, more conveniently, you can use your VS Code to open this folder as a workspace. 45 | 46 | ## Transfer existing folder into Mojo project 47 | 48 | If you want to create a Mojo project in an existing folder, you can run the following command: 49 | 50 | ::: code-group 51 | 52 | ```bash 53 | pixi init -c "https://conda.modular.com/max" -c "https://repo.prefix.dev/modular-community" -c "conda-forge" 54 | ``` 55 | 56 | ```zsh 57 | pixi init -c "https://conda.modular.com/max" -c "https://repo.prefix.dev/modular-community" -c "conda-forge" 58 | ``` 59 | 60 | ::: 61 | 62 | ## Look into `pixi.toml` 63 | 64 | When you look into the folder `my-first-mojo-project`. You may find out that there is only one non-hidden file being created in this folder[^hidden], called `pixi.toml`. Not so fancy, right? But this is the file that contains all the information about your Mojo project. Anyone want to run your Mojo code can use this file to set up the same environment as yours. 65 | 66 | Let's now open this `pixi.toml` file and take a close look. 67 | 68 | ::: code-group 69 | 70 | ```toml 71 | [workspace] 72 | authors = ["ZHU Yuhao 朱宇浩 "] 73 | channels = ["https://conda.modular.com/max", "https://repo.prefix.dev/modular-community", "conda-forge"] 74 | name = "my-first-mojo-project" 75 | platforms = ["osx-arm64"] 76 | version = "0.1.0" 77 | 78 | [tasks] 79 | 80 | [dependencies] 81 | ``` 82 | 83 | ::: 84 | 85 | This file contains the following sections: 86 | 87 | - `[workspace]`: This section contains the information about your Mojo project, such as the name of the project, the list of authors, version, platform, and channels. The "channels" field indicate where pixi can find and download the files that are necessary to run our Mojo code. We just specified three channels when we created the Mojo project. 88 | - `[dependencies]`: This section contains the list of dependencies for your Mojo project. You can add any version of Mojo compiler and other packages here. 89 | - `[tasks]`: This section is used to define tasks for your Mojo project. We will cover this section in later chapters. 90 | 91 | At the moment, let's focus on the `[dependencies]` section. 92 | 93 | You can see that there is currently nothing in the `[dependencies]` section. This is because pixi does not know that you want to run Mojo code. You have to manually add the Mojo compiler as a dependency in order to run any Mojo code. To do this, add the following line to the `[dependencies]` section: 94 | 95 | ::: code-group 96 | 97 | ```toml 98 | [dependencies] 99 | mojo = "==0.25.6" 100 | ``` 101 | 102 | ::: 103 | 104 | The word on the left side of the `=` is the name of the package. In this case, it is the `mojo` package, which contains all necessary files to run Mojo code. This also includes the Mojo compiler. 105 | 106 | The word on the right side of the `=` is the version of the package you want to install. Here are some examples: 107 | 108 | - `mojo = "==0.25.6"`: This means to install a specific version of the package, e.g., version 0.25.6[^versioning]. 109 | - `mojo = ">=0.25.6"`: This means to install any version from 0.25.6 and later. 110 | - `mojo = ">=0.25.6, <0.25.7"`: This means to install version 0.25.6 or later, but less than 0.25.7. 111 | - `mojo = "*"`: This means to install the latest version of the package (as available in the channels specified in the "channels" field). 112 | 113 | ## Install Mojo compiler 114 | 115 | After we put `mojo = "==0.25.6"` in the dependencies field, pixi knows that we want to install the latest version of the `mojo` package. To finish the installation, we need to run the following command in your terminal (Use `Cmd + J` to open the terminal in VS Code): 116 | 117 | ::: code-group 118 | 119 | ```bash 120 | pixi install 121 | ``` 122 | 123 | ::: 124 | 125 | You will see that installation immediately start: pixi downloads all the necessary files and packages from the channels. Depending on your internet connection, it may take a while to finish the installation. Finally, you will see the following message in your terminal: 126 | 127 | ```console 128 | ✔ The default environment has been installed. 129 | ``` 130 | 131 | This means that Mojo compiler is ready to use. Now, let's start with our first Mojo program! 132 | 133 | ::: info The `.pixi` folder 134 | 135 | By running `pixi install`, you actually created an virtual environment (folder `envs`) for your Mojo project in the hidden folder `.pixi`. This folder contains all the necessary files for your Mojo project. 136 | 137 | ::: 138 | 139 | ::: tip Update dependencies 140 | 141 | You can always update the dependencies in `pixi.toml` file. Then, just run `pixi install` again to update the dependencies. You can also run `pixi clean` to delete the current environment, which empty the `.pixi` folder. 142 | 143 | ::: 144 | 145 | For more information about pixi, you can refer to the [official documentation](https://pixi.sh/latest/getting_started/). 146 | 147 | [^hidden]: There are also three hidden files: `.gitignore`, `.pixi` and `.gitattributes`. These files are used for version control and package management. You can ignore them for now. 148 | [^versioning]: Before Mojo v0.25.6 (2025-09-22), the versioning scheme was different. It was like Mojo v25.1 for the release in January 2025. From v0.25.6, a extra `0.` is added in front of the version number to be more consistent with semantic versioning. 149 | -------------------------------------------------------------------------------- /src/miji/misc/layout.md: -------------------------------------------------------------------------------- 1 | # Memory Layout of Mojo objects 2 | 3 | How the objects are stored in memory is an important aspect of programming languages. In Mojo, the memory layout of objects is different from that of Python, which can lead to some confusion for Python programmers. 4 | 5 | In Python, objects are not directly stored in the memory. Instead, they are stored as a reference to a Python object, which is a structure that contains metadata about the object, such as its type, size, and reference count. The actual data of the object is stored in a separate memory block on the heap. This even applies to simple types like integers and floats. 6 | 7 | ::: info Python variable assignment 8 | 9 | Because the python variable `a` is a pointer to a Python object (and its metadata) rather than a direct pointer to the value at a certain memory address, we can say that the assignment `a = 5` is actually to attach the name `a` to the Python object that represents the integer `5`. 10 | 11 | ::: 12 | 13 | When you want to access the value of an object, for example, `print(a)` where `a` is an integer, Python go to the Python object, retrieves the metadata of the object including the pointer to the actual value, and then dereferences that pointer to access the actual data in the memory block. 14 | 15 | This means that Python list object has multiple layers in its memory layout. The first layer is a pointer that points to a list object. This list object is the second layer, which contains a reference counter, a size, a capacity, and another pointer to a continuous memory block on heap. This memory block is the third layer, which contains pointers to the elements (Python objects) of the list. The elements are the final layer (if the elements are composite types like lists, there are more layers). 16 | 17 | For example, below is an abstract representation of how the Python list `my_list = [0.125, True, 0.5, "Hello", 42]` is stored in the memory. 18 | 19 | ```console 20 | # Mojo Miji - Memory Layout - Python list 21 | 22 | Variable `my_list`: list 23 | ↓ (points to the PyListObject) 24 | ┌────────┬────────┬────────┬────────┐ 25 | Item │Counter │Size │Pointer │Capacity│ 26 | ├────────┼────────┼────────┼────────┤ 27 | Value │ 1 │ 5 │17ca81f8│ 8 │ 28 | ├────────┼────────┼────────┼────────┤ 29 | Address │26c6a89a│26c6a8a2│26c6a8aa│26c6a8b2│ 30 | └────────┴────────┴────────┴────────┘ 31 | │ 32 | ┌─────────────────┘ 33 | ↓ (points to a continuous memory block that contains the pointers to the list elements) 34 | ┌────────┬────────┬────────┬────────┬────────┬────────┬────────┬────────┐ 35 | Item │Pointer │Pointer │Pointer │Pointer │Pointer │ │ │ │ 36 | ├────────┼────────┼────────┼────────┼────────┼────────┼────────┼────────┤ 37 | Value │18c1fc0a│18c128e3│1544340b│1815412c│2da3510f│ │ │ │ 38 | ├────────┼────────┼────────┼────────┼────────┼────────┼────────┼────────┤ 39 | Address │17ca81f8│17ca8200│17ca8208│17ca8210│17ca8218│17ca8220│17ca8228│17ca8230│ 40 | └────────┴────────┴────────┴────────┴────────┴────────┴────────┴────────┘ 41 | │ │ ... ... ... 42 | │ │ 43 | │ └──────────────────────────────────────────────────────────────────────────────────┐ 44 | │ │ 45 | ↓ (points to the first element of the list, which is a float of value 0.125) │ 46 | ┌────────┬────────┐ │ 47 | Item │Counter │Pointer │ │ 48 | ├────────┼────────┤ │ 49 | Value │00000001│18c1fc12│ │ 50 | ├────────┼────────┤ │ 51 | Address │19df23ea│18c1fcf2| │ 52 | └────────┴────────┘ │ 53 | │ │ 54 | ┌────────┘ │ 55 | ↓ (points to the value of the float) │ 56 | ┌───────────────────────────────────────────────────────────────────────┐ │ 57 | Item │ Binary representation of value 0.125 │ │ 58 | ├────────┬────────┬────────┬────────┬────────┬────────┬────────┬────────┤ │ 59 | Value │00111111│11000000│00000000│00000000│00000000│00000000│00000000│00000000│ │ 60 | ├────────┼────────┼────────┼────────┼────────┼────────┼────────┼────────┤ │ 61 | Address │18c1fc12│18c1fc13│18c1fc14│18c1fc15│18c1fc16│18c1fc17│18c1fc18│18c1fc12│ │ 62 | └────────┴────────┴────────┴────────┴────────┴────────┴────────┴────────┘ │ 63 | │ 64 | ┌────────────────────────────────────────────────────────────────────────────────────────────┘ 65 | ↓ (points to the second element of the list, which is an boolean of value true) 66 | ┌────────┬─────────────────────────────────────┐ 67 | Item │Counter │ Binary representation of value true │ 68 | ├────────┼─────────────────────────────────────┤ 69 | Value │00000001│ 00000001 │ 70 | ├────────┼─────────────────────────────────────┤ 71 | Address │18c128e3│ 18c128fb │ 72 | └────────┴─────────────────────────────────────┘ 73 | ``` 74 | 75 | In Mojo, objects are stored in a more straightforward way. When you create an variable, it asks for a space in the memory (stack) to store the value directly. For example, when you declare `a: Int64 = 5`, Mojo allocates a space in the memory that is large enough to hold an integer (8 bytes, or equivalently 64-bit, for `Int`), and stores the value `5` directly in that space. When you want to access the value of `a`, for example `print(a)`, Mojo simply go to the corresponding memory address and read the value directly. 76 | 77 | For composite data types like lists, Mojo uses a similar approach but with some additional structures. When you create a list, such as `my_list = List[Float64](0.125, 12.0, 12.625, -2.0, -12.0)`, Mojo allocates a space in the memory (stack) for the list object which contains metadata about the list, i.e., length, capacity, and a pointer to a continuous memory block that holds the actual elements of the list. Then it allocates another continuous memory block on the memory (heap) to store the values of the elements of the list, which are of type `Float64`. The values of the elements are stored directly in this memory block, without further referring to another locations in the memory. 78 | 79 | Thus, there are only two layers in the memory layout of a Mojo list: the first layer is the list object that contains metadata and a pointer to the second layer, which is a continuous memory block that holds the actual values of the elements. 80 | 81 | Below is a similar abstract representation of how the Mojo list `my_list = List[Float64](0.125, 12.0, 12.625, -2.0, -12.0)` is stored in the memory. 82 | 83 | ```console 84 | # Mojo Miji - Memory Layout - Mojo List 85 | 86 | local variable `my_list`: List[Float64] 87 | ↓ (points to the instance of the struct `List[Float64]`) 88 | ┌────────────────┬────────────┬────────────────┐ 89 | Item │ data (pointer) │ _len (Int) │ capacity (Int) │ 90 | ├────────────────┼────────────┼────────────────┤ 91 | Value │ 17ca81f8 │ 5 │ 8 │ 92 | ├────────────────┼────────────┼────────────────┤ 93 | Address │ 26c6a89a │ 26c6a8a2 │ 26c6a8aa │ 94 | └────────────────┴────────────┴────────────────┘ 95 | │ 96 | ↓ (points to a continuous memory block that represents the list elements) 97 | ┌──────────────────┬──────────────────┬──────────────────┬──────────────┬───────────────┬────────┬────────┬────────┐ 98 | Item │0.125 (Float64) │ 12.0 (Float64) │ 12.625 (Float64) │-2.0 (Float64)│-12.0 (Float64)│ │ │ │ 99 | ├──────────────────┼──────────────────┼──────────────────┼──────────────┼───────────────┼────────┼────────┼────────┤ 100 | Value │0x3FC0000000000000|0x4028000000000000│0x4029400000000000│ 101 | ├──────────────────┼──────────────────┼──────────────────┼──────────────┼───────────────┼────────┼────────┼────────┤ 102 | Address │17ca81f8 │17ca8200 │17ca8208 │17ca8210 │17ca8218 │17ca8220│17ca8228│17ca8230│ 103 | └──────────────────┴──────────────────┴──────────────────┴──────────────┴───────────────┴────────┴────────┴────────┘ 104 | ``` 105 | 106 | The Python and Mojo memory layouts are fundamentally different. The former has multiple layers of indirection, being flexible and inefficient, while the latter is more efficient but less flexible (in the above example, Mojo's list can only contain elements of the same type). 107 | 108 | Which one is better? There is no definitive answer. A wise person would choose the one that best suits their needs and fits their use cases. 109 | -------------------------------------------------------------------------------- /src/.vitepress/config.mts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitepress' 2 | import mdFootnote from "markdown-it-footnote" 3 | import path from "node:path" 4 | import tailwind from "tailwindcss" 5 | import autoprefixer from "autoprefixer" 6 | import { groupIconMdPlugin, groupIconVitePlugin } from 'vitepress-plugin-group-icons' 7 | 8 | // https://vitepress.dev/reference/site-config 9 | export default defineConfig({ 10 | title: "Mojo Miji", 11 | description: "From Pythonic to Magician", 12 | lang: "en", 13 | outDir: "../dist", 14 | markdown: { 15 | math: true, 16 | theme: { 17 | light: "light-plus", 18 | dark: "material-theme-palenight", 19 | }, 20 | config: (md) => { 21 | md.use(mdFootnote, groupIconMdPlugin) 22 | } 23 | }, 24 | head: [ 25 | ['link', { rel: 'icon', href: '/icon.png', type: 'image/png' }] 26 | ], 27 | vite: { 28 | build: { 29 | chunkSizeWarningLimit: 1000, // Increase limit to 1000KB to suppress warning 30 | }, 31 | css: { 32 | postcss: { 33 | // plugins: [tailwind(), autoprefixer()], 34 | }, 35 | }, 36 | resolve: { 37 | alias: { 38 | '@': path.resolve(__dirname, '../../components'), 39 | } 40 | }, 41 | plugins: [ 42 | groupIconVitePlugin({ 43 | customIcon: { 44 | 'mojo': 'vscode-icons:file-type-mojo', 45 | 'python': 'vscode-icons:file-type-python', 46 | 'ruby': 'vscode-icons:file-type-ruby', 47 | 'rust': 'vscode-icons:file-type-rust', 48 | 'perl': 'vscode-icons:file-type-perl', 49 | 'c': 'vscode-icons:file-type-c', 50 | 'zig': 'vscode-icons:file-type-zig', 51 | 'go': 'vscode-icons:file-type-go', 52 | 'swift': 'vscode-icons:file-type-swift', 53 | 'toml': 'vscode-icons:file-type-toml', 54 | 'yaml': 'vscode-icons:file-type-yaml', 55 | 'json': 'vscode-icons:file-type-json', 56 | 'md': 'vscode-icons:file-type-markdown', 57 | } 58 | }) 59 | ] 60 | }, 61 | themeConfig: { 62 | logo: '/icon.png', 63 | nav: [ 64 | { text: 'List of Mojo resources', link: '/miji/misc/resources' }, 65 | { text: 'Repo of programs in this Miji', link: 'https://github.com/forfudan/my-first-mojo-project' }, 66 | ], 67 | logoLink: { 68 | link: 'https://mojo-lang.com/miji', 69 | target: '_self', 70 | }, 71 | sidebar: { 72 | '/miji': [ 73 | { 74 | text: 'Table of Content', 75 | link: '/miji/' 76 | }, 77 | { 78 | text: 'Introduction', 79 | link: '/miji/intro' 80 | }, 81 | { 82 | text: 'PART I: START WITH MOJO', 83 | link: '/miji/start/start', 84 | items: [ 85 | // { 86 | // text: 'Install magic (being deprecated)', link: '/miji/start/magic' 87 | // }, 88 | { 89 | text: 'Install pixi and extension', link: '/miji/start/pixi' 90 | }, 91 | { 92 | text: 'Initiate Mojo project', link: '/miji/start/project' 93 | }, 94 | { 95 | text: 'My first Mojo program', link: '/miji/start/hello' 96 | } 97 | ], 98 | }, 99 | { 100 | text: 'PART II: MOVE INTO MOJO', 101 | link: '/miji/move/move', 102 | items: [ 103 | { 104 | text: 'Convert Python code into Mojo', link: '/miji/move/examples' 105 | }, 106 | { 107 | text: 'Things that are in common', link: '/miji/move/common' 108 | }, 109 | { 110 | text: 'Things that are different', link: '/miji/move/different' 111 | }, 112 | ], 113 | }, 114 | { 115 | text: 'PART III: BASIC MOJO', 116 | link: '/miji/basic/basic', 117 | items: [ 118 | { 119 | text: 'Variables', link: '/miji/basic/variables', 120 | }, 121 | { 122 | text: 'Copy and move', link: '/miji/basic/copy', 123 | }, 124 | { 125 | text: 'Data types - basic', link: '/miji/basic/types', 126 | }, 127 | { 128 | text: 'Data types - composite', link: '/miji/basic/composite', 129 | }, 130 | { 131 | text: 'Data type - string', link: '/miji/basic/string', 132 | }, 133 | { 134 | text: 'Literals and type inference', link: '/miji/basic/literal', 135 | }, 136 | { 137 | text: 'Functions', link: '/miji/basic/functions', 138 | }, 139 | { 140 | text: 'Operators and assignment', link: '/miji/basic/operators', 141 | }, 142 | { 143 | text: 'Control flows', link: '/miji/basic/control', 144 | }, 145 | { 146 | text: "Error handling and raises", link: '/miji/basic/errors', 147 | }, 148 | { 149 | text: 'Structs', link: '/miji/basic/structs', 150 | }, 151 | { 152 | text: "Modules and packages", link: '/miji/basic/packages', 153 | }, 154 | ], 155 | }, 156 | { 157 | text: 'PART IV: ADVANCED MOJO', link: '/miji/advanced/advanced', 158 | items: [ 159 | { 160 | text: 'Data type - SIMD', link: '/miji/advanced/simd', 161 | }, 162 | { 163 | text: 'Parameterization', link: '/miji/advanced/parameterization', 164 | }, 165 | { 166 | text: 'Generic and traits', link: '/miji/advanced/generic', 167 | }, 168 | { 169 | text: 'Ownership', 170 | link: '/miji/advanced/ownership' 171 | }, 172 | { 173 | text: 'References and pointers', 174 | link: '/miji/advanced/references' 175 | }, 176 | { 177 | text: 'Lifetimes and origins', 178 | link: '/miji/advanced/lifetimes' 179 | } 180 | ] 181 | }, 182 | { 183 | text: 'PART V: APPLY MOJO', link: '/miji/apply/apply', 184 | items: [ 185 | { 186 | text: 'Design of MatMojo library', 187 | link: '/miji/apply/design' 188 | }, 189 | { 190 | text: 'Make it work', 191 | link: '/miji/apply/work' 192 | }, 193 | ] 194 | }, 195 | { 196 | text: 'PART VI: EXTEND MOJO', link: '/miji/extend/extend', 197 | items: [ 198 | { 199 | text: 'Arbitrary-precision numbers', 200 | link: '/miji/extend/decimojo' 201 | }, 202 | { 203 | text: 'Multi-dimensional arrays', 204 | link: '/miji/extend/numojo' 205 | }, 206 | ] 207 | }, 208 | { 209 | text: 'PART VII: MISCELLANEOUS', 210 | items: [ 211 | // { 212 | // text: 'Memory layout of Mojo objects', 213 | // link: '/miji/misc/layout' 214 | // }, 215 | { 216 | text: 'Glossary of Mojo terms', 217 | link: '/miji/misc/glossary' 218 | }, 219 | { 220 | text: 'Tips and warnings', 221 | link: '/miji/misc/tips' 222 | }, 223 | { 224 | text: 'Useful resources and links', 225 | link: '/miji/misc/resources' 226 | }, 227 | { 228 | text: 'About the author', 229 | link: '/miji/misc/author' 230 | } 231 | ] 232 | }, 233 | ], 234 | '/numojo': [ 235 | { items: [{ text: 'Introduction', link: '/numojo/intro' }] }, 236 | { 237 | text: 'TYPES', 238 | items: [ 239 | { 240 | text: 'NDArray', 241 | link: '/numojo/ndarray' 242 | }, 243 | { 244 | text: 'View of array', 245 | link: '/numojo/view' 246 | }, 247 | ] 248 | }, 249 | ] 250 | }, 251 | footer: { 252 | message: "Mojo Miji - A Guide to Mojo Programming Language from A Pythonista's Perspective · 魔咒秘籍 - Pythonista 視角下的 Mojo 編程語言指南", 253 | copyright: "Yuhao Zhu 朱宇浩 © 2024 Under CC BY-NC-ND 4.0 license", 254 | }, 255 | darkModeSwitchLabel: "Dark mode", 256 | langMenuLabel: "Select Language", 257 | returnToTopLabel: "Back to top", 258 | sidebarMenuLabel: "Table of content - Miji", 259 | outline: { 260 | level: "deep", 261 | label: "Sections of this chapter" 262 | }, 263 | search: { 264 | provider: "local", 265 | options: { 266 | translations: { 267 | button: { 268 | buttonAriaLabel: "Search", 269 | buttonText: "Search", 270 | }, 271 | modal: { 272 | displayDetails: "Display details", 273 | resetButtonTitle: "Rest keywords", 274 | noResultsText: "Found nothing, please use other keywords", 275 | backButtonTitle: "Back", 276 | footer: { 277 | selectText: "Go to the page", 278 | navigateText: "browse", 279 | navigateDownKeyAriaLabel: "Down", 280 | navigateUpKeyAriaLabel: "Up", 281 | closeKeyAriaLabel: "Close", 282 | closeText: "Quit search", 283 | }, 284 | }, 285 | }, 286 | }, 287 | }, 288 | socialLinks: [ 289 | { icon: 'github', link: 'https://github.com/forFudan/MojoMiji' }, 290 | ] 291 | }, 292 | 293 | // locales: { 294 | // root: { 295 | // label: 'English', 296 | // lang: 'en' 297 | // }, 298 | // zht: { 299 | // label: "傳統漢字", 300 | // lang: "zh-Hans-CN", 301 | // title: "Mojo入門指南", 302 | // description: "從Python出發", 303 | // themeConfig: { 304 | // logo: '/icon.png', 305 | // nav: [ 306 | // { text: '開啓攻略', link: '/zht/miji/intro' }, 307 | // { text: 'Mojo官網', link: 'https://www.modular.com/max/mojo/' } 308 | // ], 309 | // sidebar: { 310 | // '/zht/miji': [ 311 | // { 312 | // items: [{ text: '簡介', link: '/zht/miji/intro' },] 313 | // }, 314 | // { 315 | // items: [{ text: '變量', link: '/zht/miji/variables' },] 316 | // }, 317 | // { 318 | // items: [{ text: '類型', link: '/zht/miji/types' },] 319 | // }, 320 | // ], 321 | // }, 322 | // } 323 | // }, 324 | // }, 325 | }) 326 | -------------------------------------------------------------------------------- /src/miji/advanced/parameterization.md: -------------------------------------------------------------------------------- 1 | # Parameterization 2 | 3 | Parametrization is not difficult, as you can see in this chapter soon. But it is a very powerful tool that allows us to improve the performance of our programs during execution. We do not have this feature in Python and, I have to say, it is not something that is mandatory to write a program to solve daily problems. So I put this topic in the advanced section of the Miji. 4 | 5 | [[toc]] 6 | 7 | ## Compile time vs runtime 8 | 9 | We have learnt this in the first Part of this Miji when we write our [first Mojo program](../start/hello.md): running a Mojo program takes two steps: **compile** and **run** (we can also say **build** and **execute**): 10 | 11 | 1. The first step **translates** the Mojo code into very efficient machine code. This period is called **compile time**. You can think that the compile time is when you type `pixi run mojo build file.mojo` in your terminal. 12 | 1. After that, the Mojo compiler is no longer needed, and the machine code can be **executed** alone on any machine that supports the architecture of the compiled code. This period is called **runtime**. You can think that the runtime is when you type `./file` in your terminal. 13 | 14 | In our previous examples, we usually combine these step into one. So every time we run a Mojo program, we do one compile and one execute. However, this is only for tutorial purposes. In real life, we can compile our Mojo code once and then run it many, many times. Think of a game, a calculator, or a web server. We just run the executable file. 15 | 16 | Since do do fewer compilations but more executions, a question arises: **can we shift some work from the run time to the compile time?** This is what parameterization is about. 17 | 18 | Parametrization in Mojo is done via the concept "**parameter**". A parameter in Mojo is **a variable at compile time but is a constant at runtime**. How can this be achieved? The answer is that the compiler will replace the parameter with the value you provide at the compile time. When you execute the program, the parameter becomes a fixed value, and you cannot change it or assess it anymore. 19 | 20 | ## Parameterized functions 21 | 22 | To understand how parameters work, let's start with "parameterized function". A parameterized function is a function that takes a parameter and uses it to generate code at compile time. 23 | 24 | Let's see the following example. You ask the user to input three sentences. The first sentence is printed two times, the second sentence is printed four times, and the third sentence is printed six time. The code looks like this: 25 | 26 | ```mojo 27 | # src/advanced/parameterization/print_sentences.mojo 28 | def print_sentence_twice(sentence: String): 29 | for _i in range(2): 30 | print(sentence) 31 | 32 | def print_sentence_four_times(sentence: String): 33 | for _i in range(4): 34 | print(sentence) 35 | 36 | def print_sentence_six_times(sentence: String): 37 | for _i in range(6): 38 | print(sentence) 39 | 40 | def main(): 41 | var first_sentence = String(input("Please enter the first sentence: ")) 42 | print_sentence_twice(first_sentence) 43 | 44 | var second_sentence = String(input("Please enter the second sentence: ")) 45 | print_sentence_four_times(second_sentence) 46 | 47 | var third_sentence = String(input("Please enter the third sentence: ")) 48 | print_sentence_six_times(third_sentence) 49 | ``` 50 | 51 | If we run this program, we will see that it works as expected. However, the code is not very efficient. We have three functions that do almost the same thing, and we have to write three times the same code. This is not good. 52 | 53 | You must have come out with a better solution: we can write a single function that takes an argument for the number of times to print the sentence, as in the following code: 54 | 55 | ```mojo 56 | # src/advanced/parameterization/print_sentences_argument.mojo 57 | def print_sentence(sentence: String, times: Int): 58 | for _i in range(times): 59 | print(sentence) 60 | 61 | 62 | def main(): 63 | var first_sentence = String(input("Please enter the first sentence: ")) 64 | print_sentence(first_sentence, times=2) 65 | 66 | var second_sentence = String(input("Please enter the second sentence: ")) 67 | print_sentence(second_sentence, times=4) 68 | 69 | var third_sentence = String(input("Please enter the third sentence: ")) 70 | print_sentence(third_sentence, times=6) 71 | ``` 72 | 73 | It is better. But we have another question: 74 | 75 | At runtime, the only thing that users need to input is the sentences. The number of times to print each sentence is fixed: two, four, and six. The users cannot change them. Can we then make `times` argument a constant number at run time, so that there is no need to allocate memory for it? 76 | 77 | Yes, we can. What we need to do is simply take the `times` argument out of the parentheses `()` and put it in the square brackets `[]`. In this way, it becomes a **parameter**. The code will look like this: 78 | 79 | ```mojo 80 | # src/advanced/parameterization/print_sentences_parameter.mojo 81 | def print_sentence[times: Int](sentence: String): 82 | for _i in range(times): 83 | print(sentence) 84 | 85 | 86 | def main(): 87 | var first_sentence = String(input("Please enter the first sentence: ")) 88 | print_sentence[2](first_sentence) 89 | 90 | var second_sentence = String(input("Please enter the second sentence: ")) 91 | print_sentence[4](second_sentence) 92 | 93 | var third_sentence = String(input("Please enter the third sentence: ")) 94 | print_sentence[times=6](third_sentence) 95 | ``` 96 | 97 | That is it! Now, if you run this program, you will see that it works exactly the same as the previous one. However, the compiler will do more work at compile time: 98 | 99 | - It will replace the `times` parameter everywhere in the body the function `print_sentence()` with the value you provide when you call the function. 100 | - It will expand the code by doing this replacement for each value of `times` you provide. That is to say, there will be three copies of the function `print_sentence()` in the compiled code, each with a different value of `times`. Your code will be expanded to something similar to `src/advanced/print_sentences.mojo`. 101 | 102 | ::: tip Keyword parameters and positional parameters 103 | 104 | Just like what we learnt in Chapter [Functions](../basic/functions.md) under Sections [Keyword arguments](../basic/functions.md#keyword-arguments) and [Positional arguments](../basic/functions.md#positional-arguments), you can also put the name of the parameter in the square brackets `[]` and make it a **keyword parameter**. This is useful when you have multiple parameters and you want to specify only some of them so that it is more readable. In the previous example, you can see that the last call to `print_sentence` uses a keyword parameter `times=6`. 105 | 106 | ::: 107 | 108 | Below is an intuitive illustration of how the code is expanded at compile time. (Note that the real expansion is more complex, so the following code is just a general idea of how it works.) 109 | 110 | ```mojo 111 | def print_sentence[times: Int](sentence: String): 112 | for _i in range(times): 113 | print(sentence) 114 | ``` 115 | 116 | is expanded to: 117 | 118 | ```mojo 119 | # This is how the expanded code looks like, not that it is just a 120 | def print_sentence_times_2(sentence: String): 121 | for _i in range(2): 122 | print(sentence) 123 | # # Or even this: 124 | # print(sentence) 125 | # print(sentence) 126 | 127 | def print_sentence_times_4(sentence: String): 128 | for _i in range(4): 129 | print(sentence) 130 | 131 | def print_sentence_times_6(sentence: String): 132 | for _i in range(6): 133 | print(sentence) 134 | ``` 135 | 136 | Since the times of print is constant at runtime, there is no need to create a variable for it. Moreover, the loops can also be unfolded by the compiler at compile time. So the code is more efficient in terms of memory and speed. 137 | 138 | ::: info parameter and macro 139 | 140 | You may find that the parameter is pretty similar to the macro in C/C++. In fact, they both serve the same purpose: write code that generates code. However, the parameters in Mojo is much easier to use and more flexible. 141 | 142 | ::: 143 | 144 | ## Advantages of parameterization 145 | 146 | The main advantage of parameterization is that it allows you to shift some work from runtime to compile time. It make take more time to compile the code, but it will make the runtime faster. Since the final executable is expected to run many times or by many people, the overall social benefit is positive. 147 | 148 | Moreover, by moving some work to compile time, you can also take advantage of the compiler's **optimizations**: The Mojo compiler analyzes the code your wrote, and see whether some values can be re-used, some calculations can be simplified, or some code can be removed. It can also do some early calculations and replace the code with the results before the run time. 149 | 150 | ## Parameterized data structures 151 | 152 | Some data structures (defined by the `struct` keyword) in Mojo can also be parameterized. For example, the `SIMD` type is stored on stack and can not be resized at runtime. This means that when you declare a `SIMD` instance in Mojo, you have to specify the size of it. However, arguments are evaluated at runtime, you can only do that via a parameter. 153 | 154 | Remember that in the [SIMD chapter](./simd.md), we have seen the following code: 155 | 156 | ```mojo 157 | # src/advanced/simd/create_simd.mojo 158 | def main(): 159 | var a = SIMD[DType.float64, 4](1.0, 2.0, 3.0, 4.0) 160 | var b = SIMD[DType.int64, 8](89, 117, 104, 97, 111, 90, 104, 117) 161 | var c = SIMD[DType.bool, 2](True, False) 162 | var d = SIMD[DType.float32](1.0) 163 | print("a =", a) 164 | print("b =", b) 165 | print("c =", c) 166 | print("d =", d) 167 | ``` 168 | 169 | Actually the data type and the numbers we put in the square brackets `[]` of the `SIMD` constructor are just "parameters". They indicate the data types and sizes of the SIMD instances. The compiler will replace it with the value you provide at compile time, and then generate the code for that specific size. When you run the executable, SIMD instances of fixed datatype and fixed sizes are created on the stack. 170 | 171 | Just like in a function call, you can use keyword parameters in the `SIMD` constructor. The constructors in the previous example can also be written as: 172 | 173 | ```mojo 174 | # src/advanced/parameterization/create_simd_with_keyword_parameters.mojo 175 | def main(): 176 | var a = SIMD[dtype = DType.float64, size=4](1.0, 2.0, 3.0, 4.0) 177 | var b = SIMD[dtype = DType.int64, size=8]( 178 | 89, 117, 104, 97, 111, 90, 104, 117 179 | ) 180 | var c = SIMD[dtype = DType.bool, size=2](True, False) 181 | var d = SIMD[dtype = DType.float32, size=1](1.0) 182 | 183 | print("a =", a) 184 | print("b =", b) 185 | print("c =", c) 186 | print("d =", d) 187 | ``` 188 | 189 | ## Next step 190 | 191 | Now you have a basic understanding of how parameterization works in Mojo. You can use it to write more efficient code. However, it is not the end of the story. There is another, yet more powerful, concept called **generics** in Mojo. It can further improve the efficiency and readability of your code, and can make your code more Pythonic. 192 | -------------------------------------------------------------------------------- /src/miji/basic/operators.md: -------------------------------------------------------------------------------- 1 | # Operators and assignment expressions 2 | 3 | > Sugar is good, but bad. 4 | > -- Yuhao Zhu, *Gate of Heaven* 5 | 6 | Operators are symbols that perform operations on one or more operands. They are fundamental building blocks of any programming language, including Mojo and Python. Operators allow you to perform calculations, comparisons, logical operations, and more. 7 | 8 | The Mojo's operators and assignment expressions are very similar to Python's. If you are already familiar with Python, just scan through this chapter and focus on the differences. 9 | 10 | [[toc]] 11 | 12 | ## Operators 13 | 14 | In Python, we have arithmetic operators (`+`, `-`, `*`, `/`, `%`, `**`), comparison operators (`==`, `!=`, `<`, `>`, `<=`, `>=`), logical operators (`and`, `or`, `not`), bitwise operators (`&`, `|`, `^`, `~`), and assignment operators (`=`, `+=`, `-=`, etc.). 15 | 16 | Mojo starts from **exactly the same operator system** from Python (Horary!). The operators have the same meaning and functionality. The **operator precedence and associativity** are also the same in both languages. 17 | 18 | Moreover, there some additional operators in Mojo that are not available in Python. These operators are used to support Mojo's unique features. 19 | 20 | The following table summarizes the most important operators in Mojo, ranking from highest to lowest precedence. Within the same precedence level, the operators associativity is applied from left to right, except for the exponentiation operator `**`, which is right associative, e.g., `2**3**4` is evaluated as `2**(3**4)`. 21 | 22 | | Operator | Description | Dunder | Notes | 23 | | ---------------------------------------------------------------- | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------- | --------------------------------------------- | 24 | | `()` | Parentheses | | | 25 | | `x[]` | De-referencing (a pointer) | | **Not in Python**; equivalent to `*x` in Rust | 26 | | `x[i]`, `x[i:j]`, `x[i:j:k]` | Indexing (subscription) and slicing | `__getitem__()` | | 27 | | `**` | Power (exponentiation) | `__pow__()` | | 28 | | `-x`, `~x` | Negative and bitwise not | `__neg__()`, `__invert__()` | | 29 | | `*`, `@`, `/`, `//`, `%` | Multiplication, matrix multiplication, division, floor division, modulo | `__mul__()`, `__matmul__()`, `__truediv__()`, `__floordiv__()`, `__mod__()` | | 30 | | `+`, `-` | Addition and subtraction | `__add__()`, `__sub__()`, `__radd__()`, `__rsub__()` | | 31 | | `<<`, `>>` | Bitwise left and right shift | `__lshift__()`, `__rshift__()` | | 32 | | `&` | Bitwise and | | | 33 | | `^` | Bitwise exclusive or | | | 34 | | `\|` | Bitwise or | | | 35 | | `in`, `not in`, `is`, `is not`, `<`, `<=`, `>`, `>=`, `==`, `!=` | Comparisons | `__lt__()`, `__le__()`, `__gt__()`, `__ge__()`, `__eq__()`, `__ne__()` | | 36 | | `not x` | Boolean not (unary) | | | 37 | | `and` | Boolean and (binary) | | | 38 | | `or` | Boolean or (binary) | | | 39 | | `y if x else z` | Conditional expression (ternary) | | | 40 | 41 | Similar to Python, Mojo also supports **operator overloading**. You can define custom behavior for operators by implementing the corresponding dunder methods in your structs. We will cover this topic in more detail in Chapter [Structs](../basic/structs) and Chapter [Generic](../advanced/generic.md). 42 | 43 | ## Chained comparison 44 | 45 | In Mojo, you can chain comparison operators (e.g., `>`, `<`, `>=`, `<=`, `==`, `!=`) together, similar to Python. For example, `(a < b) and (b < c)` can be shortened as `a < b < c`. This feature is known as **chained comparison**. 46 | 47 | More specifically, the compiler will do the following transformation when it sees a chained comparison: 48 | 49 | `a operator1 b operator2 c` => `(a operator1 b) and (b operator2 c)` 50 | 51 | The chained comparison can be extended to even more than operands. The compiler will compare each consecutive pair of operands and then combine the results with `and`s. For example, `a < b < c < d < e` will be transformed into `(a < b) and (b < c) and (c < d) and (d < e)`. 52 | 53 | This feature is actually closer to the `mathematical` definition. In mathematics, when we use multiple comparison operators, we mean that **all consecutive pairs of comparisons are true at the same time**. For example, 54 | 55 | $$a < b < c = d \le e < f$$ 56 | 57 | means $a < b$, $b < c$, $c = d$, $d \le e$, and $e < f$ are all true at the same time. 58 | 59 | ::: danger Chained comparison: Syntax sugar or poison? 60 | 61 | I would say that I love almost every piece of Python syntax, except for two things: (1) [no optional block ending keyword](../misc/wishes#optional-block-ending-keyword) and (2) chained comparison. 62 | 63 | Some people may find chained comparison a very sweet and useful syntax sugar. For example, you can use `a < b < c` instead of writing `(a < b) and (b < c)`, which is more intuitive. Other people, including me, may find this syntax sugar rather toxic, which may cause confusion and unintended behavior. For example, the following code may not behave as you expect: 64 | 65 | ```mojo 66 | print(True == False == False) 67 | ``` 68 | 69 | Since the two `==` operators are of the same precedence, you may naturally think that the code will be evaluated from left to right as `(True == False) == False`, which is then evaluated as `False == False`, resulting in `True`. 70 | 71 | However, the output is actually `False` (in both Python and Mojo). Why? 72 | 73 | This is because `==` is also a comparison operator, and the chained comparison will be applied here. The expression `True == False == False` will first be transformed into `(True == False) and (False == False)`, which is then evaluated as `False and True`, resulting in `False`. 74 | 75 | This phenomenon can be confusing, especially when you previously programmed in other languages that do not support chained comparison. So you need to be very careful when using chained comparison in Mojo. If you are not sure, always use parentheses to make the order of evaluation explicit, e.g., `(True == False) == False`. 76 | 77 | ::: 78 | 79 | ## Assignment expressions 80 | 81 | Mojo's assignment expressions are also similar to Python's, with one more operator for transfer of ownership. Below is a summary of the assignment expressions in Mojo. Note that the dunders for the augmented assignment operators starts with `i`, which stands for "in-place". 82 | 83 | | Operator | Description | Dunder | Notes | 84 | | --------------------- | ------------------------------------- | ----------------- | ------------------------------------------------------------- | 85 | | `y = x` | Assignment | `__copyinit__()` | Different from Python. It copies values instead of references | 86 | | `y = x^`, `y = f(x^)` | Assignment with transfer ownership | `__moveinit__()` | **Not available in Python**, similar to `x = y` in Rust | 87 | | `y += x` | Augmented assignment (addition) | `__iadd__()` | | 88 | | `y -= x` | Augmented assignment (subtraction) | `__isub__()` | | 89 | | `y *= x` | Augmented assignment (multiplication) | `__imul__()` | | 90 | | `y /= x` | Augmented assignment (division) | `__itruediv__()` | | 91 | | `y //= x` | Augmented assignment (floor division) | `__ifloordiv__()` | | 92 | 93 | We can see that Mojo has an additional assignment operator `x = y^`, which is used to transfer ownership of the value from `y` to `x`. This operator is not available in Python, but it is very useful in Mojo especially when we want to avoid excessive copying of large data structures. This is an advanced feature and you do not need to use it in most cases. We have briefly discussed it in Chapter [Copy vs Move](../basic/copy) earlier and will cover this topic in more detail in Section ["copyinit" vs "moveinit"](../advanced/ownership#copyinit-vs-moveinit) of Chapter [Ownership](../advanced/ownership.md). 94 | -------------------------------------------------------------------------------- /src/miji/move/different.md: -------------------------------------------------------------------------------- 1 | # Things that are different 2 | 3 | The last chapter of the Miji showed you how much Mojo is similar to Python. In most situations, you can safely rely on your Python knowledge and experience to write Mojo code. This chapter continues to show you the differences between the two languages, so that you won't be surprised when you encounter error messages or strange results. 4 | 5 | [[toc]] 6 | 7 | ## Data types 8 | 9 | In previous chapter, we introduced common data types in Mojo and Python, such as `int`, `float`, `str`, `list`, `tuple`, `set`, and `dict`. In this chapter, we will focus on the differences (nuances) between these data types in Mojo and Python. 10 | 11 | A table is better than hundreds of words. Let's first summarize the differences in the following table: 12 | 13 | | Python type | Default Mojo type | Be careful that | 14 | | ----------- | ------------------ | ------------------------------------------------------------------------------ | 15 | | `int` | `Int` | Integers in Mojo has ranges. Be careful of overflow. | 16 | | `float` | `Float64` | Almost same behaviors. You can safely use it. | 17 | | `str` | `String` | Similar behaviors. Note that `String` in Mojo is rapidly evolving. | 18 | | `bool` | `Bool` | Same. | 19 | | `list` | `List` | Elements in `List` in Mojo must be of the same data type. | 20 | | `tuple` | `Tuple` | Very similar, but you cannot iterate over a `Tuple` in Mojo. | 21 | | `set` | `collections.Set` | Elements in `Set` in Mojo must be of the same data type. | 22 | | `dict` | `collections.Dict` | Keys and values in `Dict` in Mojo must be of the same data type, respectively. | 23 | 24 | ### Integer 25 | 26 | In Mojo, there are multiple types for representing integers, but the most common integer type (or integral type) is `Int`, which is either a 32-bit or 64-bit signed integer depending on your system. It is ensured to cover the range of addresses on your system. It is similar to the `numpy.intp` type in Python and the `isize` type in Rust. Mojo also has other integer types with different sizes in bits, such as `Int8`, `Int16`, `Int32`, `Int64`, `Int128`, `Int256` and their unsigned counterparts. 27 | 28 | You may think that this `Int` type is the same as the `int` type in Python, but it is not true. The `int` type in Python is an arbitrary-precision integer type, which means it can grow as large as the memory allows ,e.g., 1 followed by 1000 zeros. In contrast, the `Int` type in Mojo is a fixed-size integer type, which means it has a limited range of values. On a 64-bit system, the range of `Int` of Mojo is from `-2^63` to `2^63 - 1`. If you try to conduct operations that exceed this range, you will encounter an overflow. 29 | 30 | Thus, you need to always be careful when you are doing big integer calculations in Mojo. If you really need to work on big integers that are larger than the capacity of `Int`, you can consider using the `BigInt` type in the [decimojo package](../extend/decimojo.md), which has the similar functionality as the `int` type in Python. 31 | 32 | ::: info More on integers 33 | 34 | We will discuss integral types in more detail in Section [Integer](../basic/types#integers) of Chapter [Data Types](../basic/types.md). 35 | 36 | ::: 37 | 38 | ### Floating-point number 39 | 40 | In Mojo, there are several types for representing floating-point numbers. They differ in the number of bits they use to store the value, such as `Float16`, `Float32`, `Float64`, and `Float128`. The most commonly used type is `Float64`, which is a double-precision floating-point number. It is similar to the `float` type in Python. In you do not specify the type of a floating-point number, Mojo will automatically use `Float64` as the default type. 41 | 42 | In short, Mojo's `Float64` type is almost the same as Python's `float` type. You can safely use it without worrying about the differences. Mojo also provides other floating-point types with different sizes. You can wisely choose the appropriate type based on your needs (desired precision and range). 43 | 44 | ::: info More on floats 45 | 46 | We will discuss floating-point types in more detail in Section [Floating-point number](../basic/types#floating-point-numbers) of Chapter [Data Types](../basic/types.md). 47 | 48 | ::: 49 | 50 | ### String 51 | 52 | ::: warning String is changing rapidly 53 | 54 | The behavior of `String` has been changing rapidly since Mojo version 24.5. Some of the features mentioned in this section may be deprecated or removed in future versions. 55 | 56 | ::: 57 | 58 | ::: info More on string 59 | 60 | We will discuss the string type in more detail in Chapter [String](../basic/string). 61 | 62 | ::: 63 | 64 | Mojo's `String` type is similar to Python's `str` type, representing a sequence of Unicode characters. However, there are some key differences that you should be aware of: 65 | 66 | | Functionality | Python `str` | Mojo `String` | 67 | | --------------------------------------- | ----------------------------------------- | ------------------------------------------------------ | 68 | | Constructed string from string literals | Use `str()` constructor | Use `String()` constructor | 69 | | Use string methods on string literals | Yes, string literals are coerced to `str` | No, some methods are not applicable to string literals | 70 | | Print string with `print()` | Yes. | Yes. | 71 | | Format string with `format()` | Yes, use `{}`. | Yes, but you cannot specify formatting, e.g, `.2f`. | 72 | | f-strings | Yes. | Not supported. | 73 | | Iteration over UTF-8 code points | Yes, use `for i in s:` directly. | Yes, but more complicated. | 74 | | UTF8-assured indexing and slicing | Yes, use `s[i]` or `s[i:j]` directly. | Not supported. | 75 | 76 | If you use only quotation marks to define a string, Mojo will treat it as a string literal type. You format them with `format()`. For example, the following code will raise an error: 77 | 78 | ```mojo 79 | def main(): 80 | var a = 18 81 | print("The value of a is {}".format(a)) # This will raise an error 82 | ``` 83 | 84 | To avoid this error, you need to use `String()` constructor to convert the string literal to a `String` type: 85 | 86 | ```mojo 87 | def main(): 88 | var a = 18 89 | print(String("The value of a is {}".format(a))) # This will work 90 | ``` 91 | 92 | Currently, you cannot indicate formatting style for `String`, such as `.2f` for floating-point numbers or `.3%` for percentages. 93 | 94 | You cannot use f-strings in Mojo at the moment. 95 | 96 | To iterate over the UTF-8 code points in a `String`, you can use a `for` loop, but it is more complicated than in Python. You have to iterate over `String.codepoints()` and then wrap the items with `String()` constructor. See the following comparison: 97 | 98 | ```python 99 | def main(): 100 | s: str = "Hello, world!" 101 | for c in s: 102 | print(c, end="") 103 | main() 104 | # Output: Hello, world! 你好,世界! 105 | ``` 106 | 107 | ```mojo 108 | def main(): 109 | my_string = String("Hello, world! 你好,世界!") 110 | for char in my_string.codepoints(): 111 | print(String(char), end="") 112 | # Output: Hello, world! 你好,世界! 113 | ``` 114 | 115 | To access a code point in a Python `str`, you can just use indexing or slicing, such as `s[i]` or `s[i:j]`. However, in Mojo, you cannot do this directly at the moment. This feature is still under development. 116 | 117 | ### List 118 | 119 | In Python, a `list` is a mutable sequence type that can hold Python objects of **any type**. In Mojo, a `List` is also a mutable sequence type but can only hold objects of the **same type**. Here are some key differences between Python's `list` and Mojo's `List`: 120 | 121 | | Functionality | Mojo `List` | Python `list` | 122 | | ------------------ | ---------------------- | ------------------ | 123 | | Type of elements | Homogeneous type | Heterogenous types | 124 | | Inialization | `List[Type]()` or `[]` | `list()` or `[]` | 125 | | Printing | Not supported | Use `print()` | 126 | | Iterating | Use `for` loop | Use `for` loop | 127 | | Iterator returns | Reference to element | Copy of element | 128 | | List comprehension | Partially supported | Supported | 129 | 130 | The following things are common between `List` in Mojo and `list` in Python: 131 | 132 | - You can retrieve the elements of a `List` in Mojo using **indexing**. 133 | - You can create another `List` by **slicing** an existing `List`. 134 | - You can also **append** elements to a `List` in Mojo using the `append()` method. 135 | - You can use the `+` operator to concatenate two `List` objects. 136 | 137 | The other functionalities of `List` in Mojo would be different. Your knowledge of Python's `list` will not help you much in Mojo. Let's look at them one by one. 138 | 139 | To construct a `List` in Mojo, you have to use the ***list constructor***. For example, to create a list of `Int` numbers, you can use the following code: 140 | 141 | ```mojo 142 | def main(): 143 | my_list_of_integers = List[Int](1, 2, 3, 4, 5) 144 | my_list_of_floats = List[Float64](0.125, 12.0, 12.625, -2.0, -12.0) 145 | my_list_of_strings = List[String]("Mojo", "is", "awesome") 146 | ``` 147 | 148 | You cannot print the `List` object directly in Mojo for now. You have to write your own function to iterate over the elements of the `List` and print them one by one. 149 | 150 | When you iterate over a `List` in Python, you get the elements directly. However, in Mojo, you get references to these elements (pointers to their address in the memory). so you have to de-reference them first before using them. The dereferencing is done via the `[]` operator. See the following comparison: 151 | 152 | ```python 153 | def main(): 154 | my_list: list[int] = [1, 2, 3, 4, 5] 155 | for i in my_list: 156 | print(i, end=" ") # Directly printing the element 157 | # Output: 1 2 3 4 5 158 | 159 | ``` 160 | 161 | ```mojo 162 | def main(): 163 | my_list = List[Int](1, 2, 3, 4, 5) 164 | for i in my_list: 165 | print(i[], end=" ") # De-referencing the element to get its value 166 | # Output: 1 2 3 4 5 167 | ``` 168 | 169 | We will discuss the list type in more detail in Chapter [Composite data types](../basic/composite#lists). Chapter [Memory Layout of Mojo objects](../misc/layout.md) provides some abstract diagrams to illustrate the memory layouts of a list in Python and Mojo. 170 | 171 | ## Other difference 172 | 173 | In addition to the differences in data types, there are some other differences (maybe nuances) between Mojo and Python that you should be aware of: 174 | 175 | | feature | python | mojo | 176 | | ------------------------------------------- | ------------------------------- | -------------------------------------------------- | 177 | | Variable definition | No keyword | `var` (in some cases optional) | 178 | | Variable re-definition possible? | Yes | No | 179 | | Function definition | `def` | `def` (looser) or `fn` (stricter) | 180 | | Argument behavior depends on? | Type | Modifiers, e.g., `read`, `mut`, `var` | 181 | | Argument passed by? | Reference | Value (`var`) or reference (`read`, `mut`) | 182 | | Default argument mutability | Mutable or a new copy | Immutable (default to `read` modifier) | 183 | | Raises in the function signature | No need | No need for `def` function, need for `fn` function | 184 | | A main function is needed | No | Yes | 185 | | Function overloading | No | Yes | 186 | | Chained comparison | Yes | Yes | 187 | | `a = b` for lists | Copy reference | Copy value (deep copy) | 188 | | Define a type | `class` | `struct` | 189 | | Class or struct inheritable? | Yes | No | 190 | | Define attributes in class / struct | No, but can use type annotation | Yes, use `var` | 191 | | Compile time parametrization | No | Yes | 192 | | Generic and traits | No | Yes | 193 | | Dunder methods to access built-in functions | Yes | Yes, via traits | 194 | | Operators overloading | Yes | Yes, via traits | 195 | | Transfer ownership of variables | No | Yes, use `a = b^` | -------------------------------------------------------------------------------- /src/miji/basic/literal.md: -------------------------------------------------------------------------------- 1 | # Literal types and type inference 2 | 3 | In this section, we will learn about **literal types** and **type inference** in Mojo. Literals are values that you write in the source code, and type inference is the process by which the Mojo compiler automatically determines the data type of a value based on its context. 4 | 5 | [[toc]] 6 | 7 | ## Literal values 8 | 9 | We have used the term "literal" and "literal value" in the previous sections. Literals are values that you write in the source code, *as it is*, such as `0x1F2D`, `1234567890`, `3.14`, or `"I am a sentence."` in the following example: 10 | 11 | ::: code-group 12 | 13 | ```mojo 14 | def main(): 15 | var a = 0x1F2D # 0x1F2D is a literal 16 | var b = 1234567890 # 1234567890 is a literal 17 | abs(3.14) # 3.14 is a literal 18 | print("I am a sentence.") # "I am a sentence." is a literal 19 | ``` 20 | 21 | ```python 22 | def main(): 23 | a = 0x1F2D # 0x1F2D is a literal 24 | b = 1234567890 # 1234567890 is a literal 25 | abs(3.14) # 3.14 is a literal 26 | print("I am a sentence.") # "I am a sentence." is a literal 27 | ``` 28 | 29 | ::: 30 | 31 | When can see that a literal has some features: 32 | 33 | 1. It usually appears in the **right-hand side** of an assignment, or as an argument to a function. 34 | 1. It is not linked to a name, so you are not able to re-use it in other parts of the code, nor can you modify it. 35 | 36 | Thus, the literal can be seen as a temporary value and is only used once. They are used to construct variables or pass arguments to functions. 37 | 38 | ::: tip R-value and L-value 39 | 40 | Literals are usually **R-values** that does not have a memory address and you cannot use any address-related operations on it. The name "R-value" comes from the fact that they usually appear on the right-hand side of an assignment. On contrary, **L-values** are values that have a memory address and can be assigned to a variable. Although being called "L-value", they can appear on both sides of an assignment. 41 | 42 | For example, `var a = 123` is an assignment where `a` is an L-value and `123` is an R-value (as you can use `Pointer(to=a)` to get the address of `a` but you cannot do that for the literal `123`). In the expression `var c = a + b`, `a`, `b`, `c` are all L-values (as you can find the addresses of these variables). 43 | 44 | Back to literals again. As said before, literals are usually R-values. However, some literals can be L-values, e.g., string literals. This is because string literals are stored in a memory location at runtime and can be referenced by their address. 45 | 46 | ::: 47 | 48 | ## Literal types 49 | 50 | What happens to literal values when you run the code? 51 | 52 | Mojo will first store these literal values into specific **literal types** according to their values and contexts. For example, `12` will be stored with the type `IntLiteral`, `3.14` will be stored with the type `FloatLiteral` (because there is a decimal point), and `"I am a sentence."` will be stored as a `StringLiteral` (because there are quotation marks), etc. 53 | 54 | These literal types are primitive types that are built into the Mojo language or in the standard library and they are **not directly exposed** to users. The instances of these literal types are usually pointing to some locations in the memory where the compiled source code is stored, and thus, they are immutable. 55 | 56 | If you follow the correct patterns, prefixes, or formats, your input iterals will always be correctly inferred by the Mojo compiler and transferred into the corresponding literal types. 57 | 58 | During compilation, these literals will also be evaluated by the compiler, with some optimizations if possible. For example, in the following code, the string literal is split into multiple lines for better readability. During compilation, the compiler will automatically concatenate these lines into a single string literal: 59 | 60 | ::: code-group 61 | 62 | ```mojo 63 | def main(): 64 | print( 65 | "This is a very long string that will be split into multiple lines for" 66 | " better readability. But it is still one string literal." 67 | ) 68 | ``` 69 | 70 | ::: 71 | 72 | ## Type inference 73 | 74 | The Mojo compiler will infer the type of the literal based on its value and context, including **patterns**, **prefixes**, and **formats**. This is called **type inference**. 75 | 76 | The table below summarizes the literal types and their corresponding patterns, prefixes, or formats: 77 | 78 | | Literal Type | Pattern, prefix, or format | Example | 79 | | --------------- | ---------------------------------------------------------------------- | ---------------------------------- | 80 | | `IntLiteral` | Starts with a digit or `0b` (binary), `0o` (octal), `0x` (hexadecimal) | `123`, `0b1010`, `0o755`, `0x1F2D` | 81 | | `FloatLiteral` | Contains a decimal point or in scientific notation | `3.14`, `2.71828e-10`, `1.0` | 82 | | `StringLiteral` | Enclosed in single quotes, double quotes, or triple quotes | `"Hello"`, `'World'`, `"""Mojo"""` | 83 | | `ListLiteral` | Enclosed in square brackets with elements separated by commas | `[1, 2, 3]`, `["a", "b", "c"]` | 84 | 85 | If your literal does not match any of the patterns, prefixes, or formats, you will get an error message during compilation. 86 | 87 | ## Conversion of literal types at declaration 88 | 89 | When you run the code, these literal types will be converted into common data types depending on the context of the code. There are three scenarios: 90 | 91 | ### No type annotation 92 | 93 | If you **do not explicitly annotate the data type** during variable declaration, the literal types will be **automatically converted** into the default data types shown in the table below. 94 | 95 | | Literal Type | Default Data Type
to be converted into | Description | 96 | | --------------- | ----------------------------------------- | -------------------------------------- | 97 | | `IntLiteral` | `Int` | 32-bit or 64-bit signed integer | 98 | | `FloatLiteral` | `Float64` | 64-bit double-precision floating-point | 99 | | `ListLiteral` | `List` | List of elements of the same type | 100 | | `StringLiteral` | `StringLiteral` | No convertion is made automatically | 101 | 102 | Note that `StringLiteral` is a special case, where it is not automatically converted to a `String` type for memory efficiency. For example, the following code will automatically convert the `IntLiteral`, `FloatLiteral`, and `ListLiteral` into `Int`, `Float64`, and `List` respectively, while the `StringLiteral` will remain as `StringLiteral`: 103 | 104 | ::: code-group 105 | 106 | ```mojo 107 | def main(): 108 | var a = 42 # `42` is inferred as `IntLiteral` and is converted to `Int` by default 109 | var b = 0x1F2D # `0x1F2D` is inferred as `IntLiteral` and is converted to `Int` by default 110 | var c = 3.14 # `3.14` is inferred as `FloatLiteral` and is converted to `Float64` by default 111 | var d = 2.71828e-10 # `2.71828e-10` is inferred as `FloatLiteral` and is converted to `Float64` by default 112 | var e = [1, 2, 3] 113 | # `e` is inferred as `ListLiteral[IntLiteral]` and is converted to `List[Int]` by default 114 | var f = [[1.0, 1.1, 1.2], [2.0, 2.1, 2.2]] 115 | # `f` is inferred as `ListLiteral[ListLiteral[FloatLiteral]]` and 116 | # is converted to `List[List[Float64]]` by default 117 | var h = "Hello, World!" # `e` is inferred as `StringLiteral` and is not converted by default 118 | ``` 119 | 120 | ::: 121 | 122 | If the literal is too big or too small to fit into the default data type, you will also get an error message. For example, if you try to assign a very large integer literal to a variable without type annotation, you will get an error message like this: 123 | 124 | ::: code-group 125 | 126 | ```mojo 127 | def main(): 128 | var a = 10000000000000000000000000000000 129 | print(a) 130 | ``` 131 | 132 | ::: 133 | 134 | ```console 135 | error: integer value 10000000000000000000000000000000 requires 104 bits to store, but the destination bit width is only 64 bits wide 136 | print(a) 137 | ^ 138 | ``` 139 | 140 | This is because the default data type for integer literals is `Int`, which is a 64-bit signed integer. The literal `10000000000000000000000000000000` requires 104 bits to store, which exceeds the maximum size of `Int`. This causes an error. 141 | 142 | ### With type annotation 143 | 144 | If you annotate the data type during variable declaration, the literal types will be converted into the **specified** data types. 145 | 146 | In the following example, we use type annotations to specify the data type of each variable. The literal types will be converted into the specified data types even though these types are not the default data types. 147 | 148 | ::: code-group 149 | 150 | ```mojo 151 | def main(): 152 | var a: UInt8 = 42 # IntLiteral `42` is converted to `UInt8` 153 | var b: UInt32 = 0x1F2D # IntLiteral `0x1F2D` is converted to `UInt32` 154 | var c: Float16 = 3.14 # FloatLiteral `3.14` is converted to `Float16` 155 | var d: Float32 = ( 156 | 2.71828e-10 # FloatLiteral `2.71828e-10` is converted to `Float32` 157 | ) 158 | var e: List[Float32] = [1, 2, 3] 159 | # `ListLiteral[IntLiteral]` is converted to `List[Float32]` 160 | var f: String = "Hello, World!" # `StringLiteral` is converted to `String` 161 | ``` 162 | 163 | ::: 164 | 165 | Notably, the variable `e` is annotated as a list of floating-point numbers; even though the literal is a list of integers, it will still be successfully converted to a list of `Float32` numbers. This is because a integer literal is **compatible** with a floating-point type. 166 | 167 | If your type annotation is **incompatible** with the type of the literal, things will be different: you will get an error message. For example, you try to assign a float literal to an `Int` variable: 168 | 169 | ::: code-group 170 | 171 | ```mojo 172 | # src/basic/types/incompatible_literal_type_and_annotation.mojo 173 | # This code will not compile 174 | def main(): 175 | var a: Int = 42.5 176 | print(a) 177 | ``` 178 | 179 | ::: 180 | 181 | Running the code will give you an error message like this: 182 | 183 | ```console 184 | error: cannot implicitly convert 'FloatLiteral[42.5]' value to 'Int' 185 | var a: Int = 42.5 186 | ^~~~ 187 | ``` 188 | 189 | ::: warning Incompatible type annotation in Python 190 | 191 | Note that the error above will not happen in Python. The type annotation in Python is just a **hint** for users and type checkers, but it does not affect the runtime behavior of the code. In other words, when there are conflicts between the type annotation and the literal type of the value, Python will take the literal type of the value and ignore the type annotation. 192 | 193 | If you want to ensure that the type annotation is compatible with the literal type, you can use a type checker like `mypy` to check your code before running it. 194 | 195 | ::: 196 | 197 | ### By constructors 198 | 199 | The last scenario is when you use a **constructor** to create a variable. In this case, the literal types will be converted into the data types specified in the constructor. 200 | 201 | To call a constructor, we can simply use the name of the type followed by parentheses `()`, and then pass the literal into it. The above code can be re-written with constructors as follows: 202 | 203 | ::: code-group 204 | 205 | ```mojo 206 | def main(): 207 | var a = UInt8(42) # IntLiteral `42` is converted to `UInt8` 208 | var b = UInt32(0x1F2D) # IntLiteral `0x1F2D` is converted to `UInt32` 209 | var c = Float16(3.14) # FloatLiteral `3.14` is converted to `Float16` 210 | var d = Float32( 211 | 2.71828e-10 # FloatLiteral `2.71828e-10` is converted to `Float32` 212 | ) 213 | var e = List[Float32](1, 2, 3) 214 | # `ListLiteral[IntLiteral]` is converted to `List[Float32]` 215 | var f = String("Hello, World!") # `StringLiteral` is converted to `String` 216 | ``` 217 | 218 | ::: 219 | 220 | In Mojo as well as in Python, a constructor is a special method of the corresponding type that initializes a new instance of the type, defined by the method `__init__()`. Calling a constructor by its type name is just a shortcut for calling the `__init__()` method of the type. 221 | 222 | For example, the built-in `Int` type has a `__init__()` method that takes a single argument `IntLiteral` and initializes a new instance of `Int`. See the following code from the standard library of Mojo: 223 | 224 | ```mojo 225 | # Mojo standard library 226 | # https://github.com/modular/modular/blob/main/mojo/stdlib/stdlib/builtin/int.mojo 227 | 228 | struct Int: 229 | ... 230 | 231 | fn __init__(out self, value: IntLiteral): 232 | """Construct Int from the given IntLiteral value. 233 | 234 | Args: 235 | value: The init value. 236 | """ 237 | self = value.__int__() 238 | ``` 239 | 240 | This allows you to create a new instance of `Int` by passing using the syntax `Int(value)`, where `value` is an `IntLiteral`. 241 | 242 | You can also define such a `__init__()` method in **your own types** to allow users to create instances from any literal types. 243 | 244 | ::: tip type coercion 245 | 246 | In the code above, `var a: Int = 42.5` will not compile successfully because the literal type `FloatLiteral` is not compatible with the type annotation `Int`. A deeper reason is that the `Int` type does not have a `__init__(out self, value: FloatLiteral)` method defined, so the compiler cannot convert the `FloatLiteral` into `Int` automatically. 247 | 248 | However, if you use a constructor to create a variable, such as `var a = Int(42.5)`, it will compile successfully. 249 | 250 | ```mojo 251 | def main(): 252 | var a = Int(42.5) # This will compile successfully 253 | print(a) # Output: 42 254 | ``` 255 | 256 | Why? This is because, when being passed into a function, the literal type `FloatLiteral` will be **coerced** (automatically converted) into the corresponding default type `Float64`. This means that the following steps will be performed: 257 | 258 | 1. `42.5` is inferred as `FloatLiteral`. 259 | 1. The `FloatLiteral` is coerced into `Float64` by the Mojo compiler. 260 | 1. The `Float64` value is passed to the `Int.__init__()` method as an argument. 261 | 1. There is a method `__init__[T: Intable](out self, value: T)` defined in the `Int` type. Since [`Float64` conforms to the `Intable` trait](../advanced/generic), the program will compile successfully. 262 | 263 | On the contrary, this type coercion will not happen when you use a type annotation during variable declaration. This is why you get an error message when you try to assign a `FloatLiteral` to an `Int` variable with type annotation. 264 | -------------------------------------------------------------------------------- /src/miji/apply/design.md: -------------------------------------------------------------------------------- 1 | # Design of a matrix type 2 | 3 | From this Chapter, we will work a project that implements a matrix type in Mojo. The matrix type is a fundamental data structure in numerical computing. It is not as complicated and flexible as and N-dimensional array (`NDArray`), but it is more efficient if we are only dealing with two-dimensional data. 4 | 5 | The final outcome will be a **Mojo package** which can be imported and used by other Mojo programs and by other users. Let's call it `MatMojo`, short for "Matrix in Mojo". The package will contain a `Matrix` type that provides basic functionalities for matrix operations, such as element-wise addition, subtraction, multiplication, division, and matrix multiplication. It will also support basic statistical operations, such as sum, max, and min. 6 | 7 | [[toc]] 8 | 9 | ## Internal representation of a matrix 10 | 11 | Before we start implementing the Matrix type, we need to prepare ourself with some basic knowledge about matrices, e.g., how to represent them in Mojo, how to access their elements, and how to perform basic operations on them. 12 | 13 | ### Contiguous memory layout 14 | 15 | A matrix can be regarded as stacks of several vectors, i.e., multiple rows of column vectors, or multiple columns of row vectors. We can naturally represent a matrix as a nested list of lists. For example, a 2x3 matrix 16 | 17 | $$ 18 | \begin{pmatrix} 19 | 1.12 & 2.3 & -0.12 \\ 20 | 2.1 & -0.2 & 1.45 21 | \end{pmatrix} 22 | $$ 23 | 24 | can be represented as a nested list of lists: 25 | 26 | ```console 27 | A = [[1.12, 2.3, -0.12], [2.1, -0.2, 1.45]] 28 | ``` 29 | 30 | This is easy way to represent a matrix, and it is flexible, as the list is allocated dynamically on the heap. However, it is not efficient: The data is stored as a list of lists, which means that each row is a separate list object, leading to additional overhead. For example, when we want to calculate the sum of all elements in the matrix, we need to iterate over each row and then each element in the row, which is not efficient. 31 | 32 | A more efficient way to represent a matrix is to use a single list to store all the elements in the matrix, and then use the row and column indices to access the elements. For example, we can represent the same 2x3 matrix as a single list: 33 | 34 | ```console 35 | B = [1.12, 2.3, -0.12, 2.1, -0.2, 1.45] 36 | ``` 37 | 38 | ### Indices of a matrix 39 | 40 | You may ask, then, how do we access the elements in the one-dimensional list according to the two-dimensional indices of the matrix, i.e., row and column? 41 | 42 | The answer is simple: we can set up a mapping between the two-dimensional indices and the one-dimensional index (we also call it **offset**). For a matrix with `m` rows and `n` columns, the element at row `i` and column `j` can be accessed by the formula: 43 | 44 | $$ 45 | A_{i, j}= B_{i \cdot n + j} 46 | $$ 47 | 48 | where $A$ is a 2x3 matrix, and $B$ is the one-dimensional list that stores all the elements of the matrix by row. 49 | 50 | This formula is intuitive: if we want to get access to the element at row `i` and column `j`, we have to skip the first `i` rows, which contain `i * n` elements, and then skip the first `j` elements in the `i`-th row. 51 | 52 | ### Strides of a matrix 53 | 54 | We define the vector $(n, i)$ the **strides** of the matrix. This vector indicates how many elements we need to skip to access the next row or column. 55 | 56 | In the above example, the strides of the matrix are $(3, 1)$, which means that, if you are now at $A_{0, 2}$, you need to skip `3` elements to access the element of the same column number but the next row number, i.e., $A_{1, 2}$. 57 | 58 | Strides is very useful when we want to access the elements of the matrix given the indices: you can simply take the inner product of the strides and the indices and get its offset in the one-dimensional list. 59 | 60 | $$ 61 | A_{index1, index2} = B_{(index1, index2) \cdot (strides1, strides2)} = B_{index1 \cdot strides1 + index2 \cdot strides2} 62 | $$ 63 | 64 | For example, if we want to access the element at row `i` and column `j`, we can use the formula: 65 | 66 | $$ 67 | A_{i, j} = B_{(i, j) \cdot (3, 1)} = B_{i \cdot 3 + j} 68 | $$ 69 | 70 | ### Row-major vs column-major 71 | 72 | In the above example, we store the elements of the matrix by row. That is to say, the elements of each row are stored in contiguous memory locations, and one row after another. This is called **row-major** order. The strides of a matrix of the size `m x n` in row-major order is `(n, 1)`, e.g., 73 | 74 | ```console 75 | B = [1.12, 2.3, -0.12, 2.1, -0.2, 1.45] 76 | ``` 77 | 78 | To access the element at row `i` and column `j` in a row-major order matrix, we can use the formula: 79 | $$ 80 | A_{i, j} = B_{(i, j) \cdot (n, 1)} = B_{i \cdot n + j} 81 | $$ 82 | 83 | We can also store the elements of the matrix by column, i.e., the elements of each column are stored in contiguous memory locations, and one column after another. This is called **column-major** order. The strides of a matrix of the size `m x n` in column-major order is `(1, m)`, e.g., 84 | 85 | ```console 86 | C = [1.12, 2.1, 2.3, -0.2, -0.12, 1.45] 87 | ``` 88 | 89 | To access the element at row `i` and column `j` in a column-major order matrix, we can use the formula: 90 | 91 | $$ 92 | A_{i, j} = C_{(i, j) \cdot (1, m)} = C_{i + j \cdot m} 93 | $$ 94 | 95 | Whether to use row-major or column-major order depends on the application. For example, if you are doing a lot of matrix multiplication on narrow but long matrices (like datasets with few features but many observations), you may want to use column-major order, as it is more efficient for accessing the elements in the same column. On the other hand, if you are doing a lot of matrix multiplication on wide but short matrices (like images with many pixels but few channels), you may want to use row-major order, as it is more efficient for accessing the elements in the same row. 96 | 97 | The default order in Fortran is column-major, while the default order in C is row-major. So we also call the row-major order as "C-contiguous" memory layout, and the column-major order as "F-contiguous" memory layout. 98 | 99 | ### Row-major for our matrix type 100 | 101 | In our matrix type, we will use the row-major order to store the elements of the matrix. So the strides of the matrix will be `(n, 1)`, where `n` is the number of columns in the matrix. 102 | 103 | This choice is for simplicity: I do not want to complicate the implementation of the matrix type, but focus on how Mojo can be used to implement the basic functionalities of a matrix. 104 | 105 | You can always extend the matrix type to support column-major order by allowing the user to specify the memory layout when creating the matrix, and the initialize the strides accordingly. This is also the approach taken by the `NuMojo.Matrix` type ([PR #234](https://github.com/Mojo-Numerics-and-Algorithms-group/NuMojo/pull/234)). 106 | 107 | ::: info `numpy.matrix` is row-major 108 | 109 | Notably, the `numpy.matrix` type in Python is also row-major by default, and it does not support column-major order. 110 | 111 | In contrast, the `numpy.ndarray` type is more flexible and can be either row-major or column-major, depending on the `order` parameter that are given by users when they create the array. The default order for `numpy.ndarray` is row-major, but you can specify `order='F'` to make it column-major. 112 | 113 | The `numojo.NDArray` type in Mojo is similar to `numpy.ndarray`, and it also supports both row-major and column-major orders. 114 | 115 | ::: 116 | 117 | ### Fixed-size vs dynamic-size matrix 118 | 119 | Since the size of the matrix is not necessarily known at the compile time (e.g., users may read a matrix from a file), we cannot store the elements of the matrix in a fixed-size array. Thus, we need to use a dynamic-size array-like data structure to store the elements of the matrix **on heap**. 120 | 121 | Nevertheless, if you are particularly interested in some fixed-size matrix, e.g., 2x2, 3x3, or 4x4 matrices, you can use an SIMD to store the elements of the matrix **on stack**. This will allow you to improve the performance of the matrix operations. 122 | 123 | ## Fields of the matrix type 124 | 125 | To represent a matrix in Mojo, we need to define a struct that contains the following fields: 126 | 127 | - `dtype`: a `DType` value that indicates the data type of the elements in the matrix, e.g., `int64`, `float32`, `bool`, etc. This is similar to the `dtype` argument in NumPy. However, we can make use of the parameterization feature of Mojo to make the matrix type known at the compile time. 128 | - `data`: a one-dimensional array-like data structure (e.g., `List`) that stores all the elements of the matrix on the heap. 129 | - `size`: an integral value that stores the total number of elements in the matrix. 130 | - `shape`: a tuple that stores the shape of the matrix, i.e., the number of rows and columns. The shape and size are related: `size == shape[0] * shape[1]`. 131 | - `strides`: a tuple that stores the strides of the matrix, i.e., how many elements we need to skip to access the next row or column. Since we use row-major order, the strides will be `(n, 1)`, where `n` is the number of columns in the matrix. Thus, this field is optional, as we can always calculate the strides from the size of the matrix. Nevertheless, we will keep it for convenience and allow you to extend the matrix type to support column-major order in the future. 132 | 133 | Additional fields to consider in future: 134 | 135 | - `flags`: a mapping table that stores information on the memory layout of the matrix, e.g., whether it is row-major or column-major, whether it is contiguous or not, etc. This field can be optional if we always ensure, by design, that the matrix is contiguous in memory and is of row-major order. However, when you introduce data referencing in future, you need this field to keep track that of the memory layout as well as the ownership. 136 | 137 | ## Methods of the matrix type 138 | 139 | The matrix type should provide the following methods: 140 | 141 | ### Lifetime methods 142 | 143 | - `__init__()`: a constructor that initializes the matrix with the given data (a 1D list), size (a tuple of two integers), and data type (a `DType` value). The constructor will also calculate the number of elements, the strides, and the flags of the matrix. If the data is not provided, it will initialize an empty matrix with the given size and data type. 144 | - `__copyinit__()` and `__moveinit__()`: constructors that initialize the matrix by copying or moving the data from another matrix. 145 | 146 | ### IO methods 147 | 148 | - `__str__()`: a method that returns a string representation of the matrix. 149 | - `write_to()`: a method that enables the `print()` function to print the matrix in a human-readable format. 150 | 151 | ### Access methods 152 | 153 | - `__getitem__()`: a method that allows you to access the elements of the matrix using the row and column indices, e.g., `A[i, j]`. This method will use the strides to calculate the offset in the one-dimensional list that stores the elements of the matrix. Moreover, this method will also support slicing, e.g., `A[i, :]` or `A[:, j]`, to access the whole row or column of the matrix. 154 | - `__setitem__()`: a method that allows you to set the elements of the matrix using the row and column indices, e.g., `A[i, j] = x`. This method will also support slicing, e.g., `A[i, :] = x` or `A[:, j] = x`, to set the whole row or column of the matrix. 155 | 156 | ### Mathematical methods 157 | 158 | There are a lot of mathematical or statistical methods that we can implement for the matrix type, but we will focus on the most common ones that are useful for basic matrix operations. 159 | 160 | - `__add__()`, `__sub__()`, `__mul__()`, `__truediv__()`: methods that allow you to perform element-wise addition, subtraction, multiplication, and division on the matrix. These methods will return a new matrix with the same size and data type as the original matrix. 161 | - `__matmul__()`: a method that allows you to perform matrix multiplication on the matrix. This method will return a new matrix with the size of the result of the matrix multiplication. 162 | - `sum()`: a method that allows you to calculate the sum of all elements in the matrix. This method will return a scalar value of the same data type as the elements in the matrix. Moreover, this method will also support summing along a specific axis, e.g., `A.sum(axis=0)` to sum all rows in each column, or `A.sum(axis=1)` to sum all columns in each row. This overload will return a new matrix with one dimension equals to one (a vector). 163 | - `max()`, `min()`: methods that allow you to calculate the maximum and minimum values in the matrix. These methods will return a scalar value of the same data type as the elements in the matrix. Moreover, these methods will also support finding the maximum or minimum value along a specific axis, e.g., `A.max(axis=0)` to find the maximum value in each column, or `A.max(axis=1)` to find the maximum value in each row. This overload will return a new matrix with one dimension equals to one (a vector). 164 | 165 | ### Manipulation methods 166 | 167 | - `reshape()`: a method that allows you to reshape the matrix to a new size, e.g., from a 2x3 matrix to a 3x2 matrix. This method will return a new matrix with the same data but a different size. 168 | - `transpose()`: a method that allows you to transpose the matrix, i.e., swap the rows and columns. 169 | 170 | ## Package structure 171 | 172 | Modular programming is a good practice in software development, it divides the code into smaller, manageable pieces of files, each of which has a specific functional purpose. This makes the code in your project more organized, easier to read, easier to maintain, and easier to collaborate with others. For example, if you are using Git for version control and collaboration, you will just work on a specific file for a specific feature or functionality, without touching the other files. This reduces the risk of conflicting with others' work. 173 | 174 | So, let's divide our matrix type implementation into several files, each of which has a specific functional purpose. The package structure will look like this: 175 | 176 | 1. `matrix.mojo`: the main file that contains the implementation of the `Matrix` type and its methods. 177 | 1. `creation.mojo`: a file that contains functions for creating a matrix, such as `from_list()`. We can then use `matmojo.from_list()` to create a matrix from a list of lists. It also contains functions for creating a matrix with random values. 178 | 1. `math.mojo`: a file that contains mathematical and statistical methods for the matrix type, such as `min()`, `max()`, `sum()`, `mean()`, etc. 179 | 1. `utils.mojo`: a file that contains miscellaneous methods for the matrix type, such as `reshape()`, `transpose()`, etc. 180 | 181 | Of course, you can add more files as needed, or split the existing files into smaller ones. Since this is a small project, we will keep the number of files to a minimum but still allows us to organize the code in a modular way. 182 | 183 | ## Other directories for the project 184 | 185 | As your project grows, you may want to add some other directories to hold the documentation, tests, benchmarks, and other auxiliary files. Here are some suggestions: 186 | 187 | 1. `docs/`: a directory that contains the documentation of the project, e.g., users guide, API reference, roadmap, etc. 188 | 1. `tests/`: a directory that contains the test cases for the project. 189 | 1. `benches/`: a directory that contains the benchmark tests for the project. 190 | -------------------------------------------------------------------------------- /src/miji/apply/work.md: -------------------------------------------------------------------------------- 1 | # Make it work 2 | 3 | After a general design of the library, we can start to implement it. We will first try to **make it work**: to implement the basic functionalities of the matrix type. 4 | 5 | [[toc]] 6 | 7 | ## Initiate the project 8 | 9 | I assume that you have already installed pixi according to Chapter [pixi](../start/pixi.md). Then, you navigate to your preferred directory and run the following command to initiate a new Mojo project: 10 | 11 | ```bash 12 | pixi init matrix-mojo -c "https://conda.modular.com/max" -c "https://repo.prefix.dev/modular-community" -c "conda-forge" 13 | ``` 14 | 15 | This will create a new folder called `matrix-mojo` in the current directory. You can then use your VS Code to open this folder as a workspace. 16 | 17 | Now, we open the `pixi.toml` file in this folder. We learnt about this file in Chapter [Initiate Mojo project](../start/project.md). It is a configuration file that contains the information about the Mojo project, such as the name, version, authors, and dependencies. 18 | 19 | We can add more information to this file so that it should look like this: 20 | 21 | ```toml 22 | [workspace] 23 | authors = ["This is your name"] 24 | channels = ["https://conda.modular.com/max", "https://repo.prefix.dev/modular-community", "conda-forge"] 25 | name = "matmojo" 26 | platforms = ["osx-arm64"] 27 | version = "0.1.0" 28 | 29 | [tasks] 30 | 31 | [dependencies] 32 | max = "*" 33 | ``` 34 | 35 | For now, it is sufficient. We will update this file later when we add more features to the library. 36 | 37 | --- 38 | 39 | Now, we can install the dependencies of the project by typing the following command in the terminal: 40 | 41 | ```bash 42 | pixi install 43 | ``` 44 | 45 | This will install the latest version of the `max` package, which also contains the Mojo compiler. 46 | 47 | ## Create folders and files 48 | 49 | Next, we will create some folders and files to organize our code. The workspace should look like this: 50 | 51 | ```console 52 | matrix-mojo 53 | ├── pixi.toml 54 | ├─- tests 55 | └── src 56 | └── matmojo 57 | ├── __init__.mojo 58 | ├── creation.mojo 59 | ├── math.mojo 60 | ├── matrix.mojo 61 | └── utils.mojo 62 | ``` 63 | 64 | This structure is similar to the `my-first-mojo-project` we created in Chapter [My first Mojo program](../start/hello.md). Here are some explanations of the folders and files: 65 | 66 | - The `src` folder contains the source code of the project. 67 | - The `matmojo` folder contains the code of the matrix library. Although it is the only folder in the `src` folder, it is still a good practice to put the code of the library in a separate sub-folder. For bigger projects, you may have multiple auxiliary libraries. You can then put them in different sub-folders under `src`. 68 | - `__init__.mojo` is a file that indicating that this folder is a valid Mojo package. It is identical to Python's convention, so your knowledge of Python packages can be directly applied here. In this file, you can also write some code that will be executed when the package is imported. 69 | - `matrix.mojo` is the main file that contains the definition of the `Matrix` type. It is the core of the library. 70 | - `creation.mojo`, `math.mojo`, and `utils.mojo` are modules containing the implementation of the matrix creation, mathematical operations, and utility functions, respectively. Your knowledge of Python modules can also be directly applied here: **every mojo file is a module.** 71 | - The `tests` folder is where we will put interactive tests and, in future, unit tests for the library. It is a good practice to put tests in a separate folder so that they are not mixed with the source code of the library. 72 | 73 | ## Define struct and its fields 74 | 75 | Now, we can start to implement the `Matrix` type. We will first create the `Matrix` struct in the `matrix.mojo` file. Open the `matrix.mojo` file and type the following code: 76 | 77 | ```mojo 78 | struct Matrix[dtype: DType = DType.float64]: 79 | """A 2D matrix type. 80 | 81 | Parameters: 82 | dtype: The data type of the matrix elements. Defaults to `DType.float64`. 83 | """ 84 | 85 | var data: List[Scalar[dtype]] 86 | """The elements of the matrix stored in row-major layout (C-contiguous).""" 87 | var size: Int 88 | """The total number of elements in the matrix.""" 89 | var shape: Tuple[Int, Int] 90 | """The shape of the matrix as a tuple (rows, cols).""" 91 | var strides: Tuple[Int, Int] 92 | """The strides of the matrix in memory (row stride, col stride).""" 93 | ``` 94 | 95 | This code defines a `Matrix` struct with four fields: 96 | 97 | - `data`: A list of elements of type `Scalar[dtype]`, which stores the elements of the matrix in a row-major layout (C-contiguous). 98 | - `size`: An integer that represents the total number of elements in the matrix. 99 | - `shape`: A tuple of two integers that represents the shape of the matrix as `(rows, cols)`. 100 | - `strides`: A tuple of two integers that represents the strides of the matrix in memory, which is `(row stride, col stride)`. 101 | 102 | Notably, the `Matrix` struct is [parameterized](../advanced/parameterization.md) by a type parameter `dtype`, which allows us to create matrices of different data types. The `dtype` can be any type that is an [SIMD](../advanced/simd.md) type, such as `Float32`, `Float64`, `Int32`, or `Int64`. This `dtype` parameters, different from the four fields, is known at compile time. This means that the `dtype` parameter will be replaced with a realized type when you compile your code. During run time, the `dtype` will be a constant value. 103 | 104 | You may notice that we also use this parameter `dtype` in the type annotation of the `data` field. This means that the list that contains the elements of the matrix will be also parameterized by the `dtype`. 105 | 106 | The square brackets `[]` containing the parameters is **a part of the struct name**. This means that, when you want to create a new matrix instance, the corresponding constructor should contains both the struct name and the type parameters, e.g., `Matrix[Float32]()`. 107 | 108 | If you do not specify the `dtype` parameter when creating a new `Matrix` instance, it will default to `DType.float64`, as specified in the struct definition. 109 | 110 | ::: info Scalar and SIMD 111 | 112 | When there is only one element in the `SIMD` type, it can also be referred to as a `Scalar`. The `Scalar` type is not new type, but just a type alias for the `SIMD` type, just like `Float32` is a type alias for `SIMD[Float32, 1]`. So we have the following equation: 113 | 114 | `Float32` = `Scalar[DType.float32]` = `SIMD[DType.float32, 1]` 115 | 116 | You can use either alias in your code for convenience. 117 | 118 | ::: 119 | 120 | ## Implement the constructor 121 | 122 | Next, we will implement the constructor of the `Matrix` type, the `__init__()` method. It allows us to create a new `Matrix` instance with the name of the struct, e.g., `Matrix[Float32]()`. 123 | 124 | For this `__init__()` method, we will pass in the following information: 125 | 126 | - The elements of the matrix. In our implementation, we will accept a list of values. 127 | - The shape of the matrix, which is a tuple of two integers `(rows, cols)`. We can use it to calculate the size and strides of the matrix. 128 | 129 | Here is the code: 130 | 131 | ```mojo 132 | # src/matmojo/__init__.mojo 133 | fn __init__(out self, data: List[Scalar[dtype]], shape: Tuple[Int, Int]) raises: 134 | """Initialize the matrix with data and shape. 135 | 136 | Args: 137 | data: A list of elements to initialize the matrix. 138 | shape: A tuple specifying the shape of the matrix (rows, cols). 139 | """ 140 | self.data = data 141 | self.size = shape[0] * shape[1] 142 | self.shape = shape 143 | self.strides = (shape[1], 1) # Row-major order 144 | if len(data) != shape[0] * shape[1]: 145 | raise "Data length does not match the specified shape." 146 | ``` 147 | 148 | Let's see what we should pay attention to in this code: 149 | 150 | - We use the `out self` as the first argument to indicate that a instance of the `Matrix` struct will be created by this method. Similar to Python, `self` is a reference to the instance being created, but an explicit `out` keyword is required in Mojo. 151 | - We need to specify the type of all remaining arguments explicitly. 152 | - We use the `raises` keyword to indicate that this method may raise an exception. It is because we will check at the initialization whether the length of the `data` list matches the specified shape. If not, we raise an exception with a message. 153 | - The code inside the method body is similar to Python. We use `self.field_name` to access the fields of the struct that are defined at the top of the file. 154 | 155 | ## Test your code in time 156 | 157 | You may want to already test your code to see if it works as expected. Although we we in future set up some unit tests for the library, we can still test the code with a more interactive way. There are many ways to do this, but I will show you rather convenient way to do this in VS Code. 158 | 159 | ### Step 1: Create a test file 160 | 161 | You can create a file called `test.mojo` in the `tests` folder. This file will be used to test the code interactively. You can then type the following code in this file to create a `Matrix` instance. 162 | 163 | ```mojo 164 | # tests/test.mojo 165 | import matmojo as mm 166 | 167 | def main(): 168 | var mat = mm.matrix.Matrix[DType.float64](data=List[Float64](1.0, 2.0, 3.0, 4.0, 5.0), shape=(2, 2)) 169 | ``` 170 | 171 | In the code, we import the `matmojo` package into the file by `import matmojo as mm`, and then use the `mm.matrix.Matrix[DType.float64]` to call call the constructor of the `Matrix` type. This is very similar to Python's import statement, so you can safely use your knowledge of Python import system here. 172 | 173 | However, we have a problem here: The Mojo compiler does not know where the `matmojo` package is located. If is not that smart to guess that the `matmojo` package is in the path `../src/`. Thus, we have to do something for the compiler to find the package. 174 | 175 | ### Step 2: Build the package 176 | 177 | To make the Mojo compiler aware of the `matmojo` package, we need to build a Mojo package and put it in the same directory as the `test.mojo` file. Mojo compiler will search for the package in the current directory and its subdirectories. When it sees the `matmojo` package, it will be able to import it. 178 | 179 | To build the package, you can run the following command in the terminal: 180 | 181 | ```bash 182 | pixi run mojo package -o tests/matmojo.mojopkg src/matmojo 183 | ``` 184 | 185 | What does this mean? Let's break it down: 186 | 187 | - `pixi run mojo package`: This command tells pixi to run the Mojo compiler with the `package` command, which is used to build a Mojo package. A Mojo package is a standalone file with extension `.mojopkg` that contains all the code in your `matmojo` package, but a more compact form. Note it is not compiled yet to machine code. 188 | - `-o tests/matmojo.mojopkg`: This option specifies the output file of the package. The package will be saved as `tests/matmojo.mojopkg`. 189 | - `src/matmojo`: This is the path to the source code of the `matmojo` package that we want to build. 190 | 191 | After running this command, you should see a new file called `matmojo.mojopkg` in the `tests` folder. This file is functionally equivalent to the `matmojo` folder in the `src` folder. You can even copy it to other computers and use it. 192 | 193 | ### Step 3: Run the test file 194 | 195 | Now, you can run the `test.mojo` file to see if it works. You can do this by running the following command in the terminal: 196 | 197 | ```bash 198 | pixi run mojo run tests/test.mojo 199 | ``` 200 | 201 | Success! You should see the following output: 202 | 203 | ```console 204 | (base) ZHU@MBP-Dr-Yuhao-Zhu matrix-mojo % pixi run mojo run tests/test.mojo 205 | Unhandled exception caught during execution: Data length does not match the specified shape. 206 | /Users/ZHU/Programs/matrix-mojo/.pixi/envs/default/bin/mojo: error: execution exited with a non-zero result: 1 207 | ``` 208 | 209 | This means that the `Matrix` constructor was successfully called, but it raised an exception because the length of the `data` list does not match the specified shape. This is expected, as we only provided 5 elements in the `data` list, while the shape is `(2, 2)`, which requires 4 elements. 210 | 211 | If you change the code in the `test.mojo` file and provide a valid `data` list, such as `data=List[Float64](1.0, 2.0, 3.0, 4.0)`, you should see no output, which means the code ran successfully without any exceptions. 212 | 213 | ### Use pixi tasks as a shortcut 214 | 215 | It will be a little bit tedious to run the commands above every time you want to test your code. Fortunately, we can use pixi "tasks" to automate this process. 216 | 217 | Pixi "tasks" is a feature that allows you to define custom commands in the `pixi.toml` file. You can define a task that will build the package and run the test file with a single command. To do this, you can add the following lines to the `pixi.toml` file: 218 | 219 | ```toml 220 | [tasks] 221 | # Build the package into the root directory 222 | pack = "pixi run mojo package -o tests/matmojo.mojopkg src/matmojo" 223 | 224 | # Run the test.mojo file 225 | test = "pixi run pack && pixi run mojo run tests/test.mojo" 226 | ``` 227 | 228 | It is simply the commands we ran above. We define two tasks: `pack` and `test`: 229 | 230 | - The `pack` task builds the package. 231 | - The `test` task runs the `pack` task first and then runs the `test.mojo` file. 232 | 233 | Now, you can use the `pixi run` command to build the package and run the test file in one go. Just type the following command in the terminal: 234 | 235 | ```bash 236 | pixi run test 237 | ``` 238 | 239 | ## Print the matrix 240 | 241 | Although we have successfully created a `Matrix` instance and run the test file without any compilation errors, we cannot see the contents of the matrix. So the next priority is to implement a way to print the matrix in a human-readable format. 242 | 243 | To do this, we can implement the `__str__()` method and the `write_to()` method for the `Matrix` struct. We have seen the functionality of these two methods in Chapter [Structs](../basic/structs.md). Moreover, we want some formatting for the output that: 244 | 245 | 1. The string representation of a matrix is in a grid-like format. Element in the same row will be printed in the same line, separated by tabs. 246 | 1. When we print the matrix using the `print()` function, we need to wrap it with square brackets `[]` to indicate that it is a matrix. 247 | 248 | You can try yourself first. Below is my implementation of the two methods. 249 | 250 | ```mojo 251 | # src/matmojo/matrix.mojo 252 | fn __str__(self) -> String: 253 | """Return a string representation of the matrix.""" 254 | result = String("") 255 | for i in range(self.shape[0]): 256 | for j in range(self.shape[1]): 257 | result += String(self.data[i * self.strides[0] + j]) + "\t" 258 | if i < self.shape[0] - 1: 259 | result += "\n" 260 | return result 261 | 262 | fn write_to[W: Writer](self, mut writer: W): 263 | """Write the matrix to a writer.""" 264 | for i in range(self.shape[0]): 265 | if i == 0: 266 | writer.write("[[\t") 267 | else: 268 | writer.write(" [\t") 269 | for j in range(self.shape[1]): 270 | writer.write(self.data[i * self.strides[0] + j]) 271 | writer.write("\t") 272 | writer.write("]") 273 | if i < self.shape[0] - 1: 274 | writer.write("\n") 275 | else: 276 | writer.write("]") 277 | ``` 278 | 279 | Let's update our test file and see if it works: 280 | 281 | ```mojo 282 | # tests/test.mojo 283 | import matmojo as mm 284 | 285 | 286 | def main(): 287 | var mat = mm.matrix.Matrix[DType.float64]( 288 | data=List[Float64]( 289 | 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0 290 | ), 291 | shape=(3, 4), 292 | ) 293 | 294 | print("String representation of the matrix:") 295 | print(String(mat)) 296 | 297 | print("Printing the matrix directly:") 298 | print(mat) 299 | ``` 300 | 301 | Now, running the command `pixi run test` should give you the following output: 302 | 303 | ```console 304 | String representation of the matrix: 305 | 1.0 2.0 3.0 4.0 306 | 5.0 6.0 7.0 8.0 307 | 9.0 10.0 11.0 12.0 308 | Printing the matrix directly: 309 | [[ 1.0 2.0 3.0 4.0 ] 310 | [ 5.0 6.0 7.0 8.0 ] 311 | [ 9.0 10.0 11.0 12.0 ]] 312 | ``` 313 | 314 | It looks good! Now we know that the `Matrix` type is working as expected: it correctly initializes the matrix with the given data and shape. 315 | 316 | ::: info More comprehensive implementation 317 | 318 | The methods above are not a comprehensive implementation, but it is sufficient for our Miji. If you want to put it into production, you need to also think about the following: 319 | 320 | 1. How many digits to print for each element, based on the data type of the matrix? 321 | 1. How to handle the case when the matrix is too large to fit in the console? 322 | 1. How to align the elements in the same column? 323 | 1. When we should print the elements in scientific notation? 324 | 325 | ::: 326 | -------------------------------------------------------------------------------- /src/miji/misc/glossary.md: -------------------------------------------------------------------------------- 1 | # Glossary 2 | 3 | This glossary lists the core Mojo terms with short definitions. 4 | 5 | | Term | Definition | 6 | | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 7 | | variable | Named storage (name, type, address, value) owning its value unless borrowed (See: [Variables](../basic/variables); [Ownership](../advanced/ownership); [Copy & move](../basic/copy); [References](../advanced/references)) | 8 | | identifier | Symbolic name bound to a value/type/function (See: [Variables](../basic/variables#identifiers)) | 9 | | uninitialized variable | Declared name without a valid value yet (See: [Variables](../basic/variables#create-variables)) | 10 | | shadowing | Inner-scope variable hides an outer one (See: [Variables](../basic/variables)) | 11 | | dunder method | Special protocol method `__name__` (e.g. `__copyinit__`) (See: [Generics & Traits](../advanced/generic); [Copy & move](../basic/copy)) | 12 | | literal | Source constant forming a value (See: [Literals](../basic/literal); [Data types](../basic/types)) | 13 | | L-value / R-value | Addressable value vs temporary / literal (See: [Literals](../basic/literal#l-values-and-r-values); [Copy & move](../basic/copy)) | 14 | | constructor | Type-named creator / converter (See: [Data types](../basic/types); [Copy & move](../basic/copy)) | 15 | | implicit copy | Automatic `=` copy for cheap types (See: [Copy & move](../basic/copy); [Ownership](../advanced/ownership)) | 16 | | implicit copyable | Conforming type enabling implicit copy (See: [Copy & move](../basic/copy)) | 17 | | implicit copy mechanism | Language feature triggering auto copy (See: [Copy & move](../basic/copy)) | 18 | | explicit copy | `.copy()` producing a new independent value (See: [Copy & move](../basic/copy); [Ownership](../advanced/ownership)) | 19 | | deep copy | Copy duplicating nested / heap data (See: [Copy & move](../basic/copy); [Composite](../basic/composite)) | 20 | | move / transfer | Ownership hand-off via `^` (See: [Copy & move](../basic/copy#move-transfer-of-values); [Ownership](../advanced/ownership)) | 21 | | move-capable type | Type whose values can be moved with `^` (See: [Copy & move](../basic/copy#move-transfer-of-values); [Ownership](../advanced/ownership)) | 22 | | transfer operator `^` | Symbol marking ownership move (See: [Copy & move](../basic/copy#move-transfer-of-values); [Ownership](../advanced/ownership)) | 23 | | ownership | One-owner model ensuring safety (See: [Ownership](../advanced/ownership); [Lifetimes](../advanced/lifetimes)) | 24 | | owner | Unique controller of a value lifetime (See: [Ownership](../advanced/ownership#a-value-has-only-one-owner); [Lifetimes](../advanced/lifetimes)) | 25 | | owner destruction | End of owner lifetime releasing value (See: [Ownership](../advanced/ownership); [Lifetimes](../advanced/lifetimes)) | 26 | | borrow | Non-owning access (reference or pointer) (See: [Ownership](../advanced/ownership#four-statuses-of-ownership); [References](../advanced/references); [Lifetimes](../advanced/lifetimes)) | 27 | | borrower | Any reference or pointer without ownership (See: [Ownership](../advanced/ownership#four-statuses-of-ownership); [References](../advanced/references)) | 28 | | reference (alias) | Same-type same-address alias (See: [References](../advanced/references); [Ownership](../advanced/ownership)) | 29 | | alias | Alternate name to same value (See: [References](../advanced/references)) | 30 | | mutable reference | Alias permitting mutation (See: [References](../advanced/references)) | 31 | | immutable reference | Read-only alias (See: [References](../advanced/references)) | 32 | | safe pointer | Pointer with target + origin metadata (See: [References](../advanced/references#safe-pointer); [Ownership](../advanced/ownership)) | 33 | | unsafe pointer | Pointer lacking lifetime tracking (See: [References](../advanced/references#unsafe-pointer); [Ownership](../advanced/ownership)) | 34 | | dereferencing operator | `[]` to access pointee (See: [Ownership](../advanced/ownership#pointed-status); [References](../advanced/references)) | 35 | | isolated status | Independent owned value (See: [Ownership](../advanced/ownership#isolated-status)) | 36 | | referenced status | Alias-based access (See: [Ownership](../advanced/ownership#referenced-status)) | 37 | | pointed status | Indirect through safe pointer (See: [Ownership](../advanced/ownership#pointed-status)) | 38 | | unsafe status | Access via unsafe pointer (See: [Ownership](../advanced/ownership#unsafe-status); [References](../advanced/references)) | 39 | | unsafe code | Code using unchecked constructs (See: [Ownership](../advanced/ownership#unsafe-status)) | 40 | | dangling pointer | Pointer to freed / reused memory (See: [Ownership](../advanced/ownership#unsafe-status); [References](../advanced/references); [Lifetimes](../advanced/lifetimes)) | 41 | | dangling reference | Alias used after owner ended (See: [Ownership](../advanced/ownership#unsafe-status); [References](../advanced/references); [Lifetimes](../advanced/lifetimes)) | 42 | | memory leak | Lost allocation unreleased (See: [Ownership](../advanced/ownership)) | 43 | | double free | Duplicate deallocation attempt (See: [Ownership](../advanced/ownership)) | 44 | | accidental overwriting | Unintended mutation (See: [Ownership](../advanced/ownership)) | 45 | | lifetime | Valid duration of a value (See: [Lifetimes](../advanced/lifetimes); [Ownership](../advanced/ownership)) | 46 | | lifetime annotation | Syntax tying borrower to origin (See: [Lifetimes](../advanced/lifetimes); [References](../advanced/references)) | 47 | | origin | Source owner metadata (See: [Lifetimes](../advanced/lifetimes#origin-chains)) | 48 | | origin chain | Linked borrowing lineage (See: [Lifetimes](../advanced/lifetimes#origin-chains)) | 49 | | parametric mutability | Mutability encoded as parameter (See: [References](../advanced/references#parametric-mutability); [Parameterization](../advanced/parameterization)) | 50 | | compile time | Static analysis & validation phase (See: [Ownership](../advanced/ownership#rules-of-ownership); [Parameterization](../advanced/parameterization)) | 51 | | run time | Execution phase for values (See: [Ownership](../advanced/ownership#rules-of-ownership)) | 52 | | SIMD | Fixed-size homogeneous vector (See: [SIMD](../advanced/simd); [Parameterization](../advanced/parameterization)) | 53 | | SIMD lane | Single element position (See: [SIMD](../advanced/simd#why-simd)) | 54 | | parameterization | Compile-time specialization (See: [Parameterization](../advanced/parameterization); [Generics & Traits](../advanced/generic)) | 55 | | parameter | Compile-time argument in `[]` (See: [Parameterization](../advanced/parameterization#parameterize-a-struct); [Generics & Traits](../advanced/generic)) | 56 | | generic | Trait-constrained polymorphic pattern (See: [Generics & Traits](../advanced/generic); [Parameterization](../advanced/parameterization)) | 57 | | generic function | Function with trait-bounded type params (See: [Generics & Traits](../advanced/generic)) | 58 | | trait | Behavioral contract (See: [Generics & Traits](../advanced/generic#traits)) | 59 | | conformance | Meeting all trait requirements (See: [Generics & Traits](../advanced/generic#traits)) | 60 | | default implementation | Trait-provided method body (See: [Generics & Traits](../advanced/generic#default-implementation-of-methods-in-traits)) | 61 | | implicit trait conformance | Naming-based satisfaction (See: [Generics & Traits](../advanced/generic#double-underscore-methods-that-conform-to-certain-traits)) | 62 | | trait placeholder `...` | Ellipsis marking requirement (See: [Generics & Traits](../advanced/generic#traits)) | 63 | | overflow | Integer wrap-around (See: [Data types](../basic/types#integer-overflow)) | 64 | | BigInt | Arbitrary-precision integer (See: [DeciMojo](../extend/decimojo#bigint-type)) | 65 | | stack | Fast scoped fixed-size storage (See: [Layout](../misc/layout); [Ownership](../advanced/ownership)) | 66 | | heap | Dynamic allocation region (See: [Layout](../misc/layout); [Ownership](../advanced/ownership)) | 67 | | contiguous memory | Sequential addresses (See: [Layout](../misc/layout); [Composite](../basic/composite)) | 68 | | metadata (list) | Header: pointer, length, capacity (See: [Composite](../basic/composite#memory-layout-of-list-type); [Layout](../misc/layout)) | 69 | | capacity (list) | Reserved element slots (See: [Composite](../basic/composite#memory-layout-of-list-type)) | 70 | | list element layout | Consecutive element storage (See: [Composite](../basic/composite#memory-layout-of-list-type); [Layout](../misc/layout)) | 71 | | StringLiteral | Immutable inline string form (See: [String](../basic/string#stringliteral-type); [Copy & move](../basic/copy)) | 72 | | materialization (string) | Converting to mutable `String` (See: [String](../basic/string#stringliteral-type); [Structs](../basic/structs)) | 73 | | stack vs heap optimization (String) | Small-string inline optimization (See: [Structs](../basic/structs#memory-layout-of-struct); [String](../basic/string)) | 74 | | vault metaphor | Variables as vaults at addresses (See: [Ownership](../advanced/ownership)) | 75 | -------------------------------------------------------------------------------- /src/.vitepress/vars.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Colors: Solid 3 | * -------------------------------------------------------------------------- */ 4 | 5 | :root { 6 | --vp-c-white: #ffffff; 7 | --vp-c-black: #000000; 8 | 9 | --vp-c-neutral: var(--vp-c-black); 10 | --vp-c-neutral-inverse: var(--vp-c-white); 11 | } 12 | 13 | .dark { 14 | --vp-c-neutral: var(--vp-c-white); 15 | --vp-c-neutral-inverse: var(--vp-c-black); 16 | } 17 | 18 | /** 19 | * Colors: Palette 20 | * 21 | * The primitive colors used for accent colors. These colors are referenced 22 | * by functional colors such as "Text", "Background", or "Brand". 23 | * 24 | * Each colors have exact same color scale system with 3 levels of solid 25 | * colors with different brightness, and 1 soft color. 26 | * 27 | * - `XXX-1`: The most solid color used mainly for colored text. It must 28 | * satisfy the contrast ratio against when used on top of `XXX-soft`. 29 | * 30 | * - `XXX-2`: The color used mainly for hover state of the button. 31 | * 32 | * - `XXX-3`: The color for solid background, such as bg color of the button. 33 | * It must satisfy the contrast ratio with pure white (#ffffff) text on 34 | * top of it. 35 | * 36 | * - `XXX-soft`: The color used for subtle background such as custom container 37 | * or badges. It must satisfy the contrast ratio when putting `XXX-1` colors 38 | * on top of it. 39 | * 40 | * The soft color must be semi transparent alpha channel. This is crucial 41 | * because it allows adding multiple "soft" colors on top of each other 42 | * to create a accent, such as when having inline code block inside 43 | * custom containers. 44 | * -------------------------------------------------------------------------- */ 45 | 46 | :root { 47 | --vp-c-gray-1: #dddde3; 48 | --vp-c-gray-2: #e4e4e9; 49 | --vp-c-gray-3: #ebebef; 50 | --vp-c-gray-soft: rgba(142, 150, 170, 0.14); 51 | 52 | --vp-c-indigo-1: #3451b2; 53 | --vp-c-indigo-2: #3a5ccc; 54 | --vp-c-indigo-3: #5672cd; 55 | --vp-c-indigo-soft: rgba(100, 108, 255, 0.14); 56 | 57 | --vp-c-purple-1: #6f42c1; 58 | --vp-c-purple-2: #7e4cc9; 59 | --vp-c-purple-3: #8e5cd9; 60 | --vp-c-purple-soft: rgba(159, 122, 234, 0.14); 61 | 62 | --vp-c-green-1: #18794e; 63 | --vp-c-green-2: #299764; 64 | --vp-c-green-3: #30a46c; 65 | --vp-c-green-soft: rgba(16, 185, 129, 0.14); 66 | 67 | --vp-c-yellow-1: #915930; 68 | --vp-c-yellow-2: #946300; 69 | --vp-c-yellow-3: #9f6a00; 70 | --vp-c-yellow-soft: rgba(234, 179, 8, 0.14); 71 | 72 | --vp-c-red-1: #b8272c; 73 | --vp-c-red-2: #d5393e; 74 | --vp-c-red-3: #e0575b; 75 | --vp-c-red-soft: rgba(244, 63, 94, 0.14); 76 | 77 | --vp-c-sponsor: #db2777; 78 | } 79 | 80 | .dark { 81 | --vp-c-gray-1: #515c67; 82 | --vp-c-gray-2: #414853; 83 | --vp-c-gray-3: #32363f; 84 | --vp-c-gray-soft: rgba(101, 117, 133, 0.16); 85 | 86 | --vp-c-indigo-1: #a8b1ff; 87 | --vp-c-indigo-2: #5c73e7; 88 | --vp-c-indigo-3: #3e63dd; 89 | --vp-c-indigo-soft: rgba(100, 108, 255, 0.16); 90 | 91 | --vp-c-purple-1: #c8abfa; 92 | --vp-c-purple-2: #a879e6; 93 | --vp-c-purple-3: #8e5cd9; 94 | --vp-c-purple-soft: rgba(159, 122, 234, 0.16); 95 | 96 | --vp-c-green-1: #3dd68c; 97 | --vp-c-green-2: #30a46c; 98 | --vp-c-green-3: #298459; 99 | --vp-c-green-soft: rgba(16, 185, 129, 0.16); 100 | 101 | --vp-c-yellow-1: #f9b44e; 102 | --vp-c-yellow-2: #da8b17; 103 | --vp-c-yellow-3: #a46a0a; 104 | --vp-c-yellow-soft: rgba(234, 179, 8, 0.16); 105 | 106 | --vp-c-red-1: #f66f81; 107 | --vp-c-red-2: #f14158; 108 | --vp-c-red-3: #b62a3c; 109 | --vp-c-red-soft: rgba(244, 63, 94, 0.16); 110 | } 111 | 112 | /** 113 | * Colors: Background 114 | * 115 | * - `bg`: The bg color used for main screen. 116 | * 117 | * - `bg-alt`: The alternative bg color used in places such as "sidebar", 118 | * or "code block". 119 | * 120 | * - `bg-elv`: The elevated bg color. This is used at parts where it "floats", 121 | * such as "dialog". 122 | * 123 | * - `bg-soft`: The bg color to slightly distinguish some components from 124 | * the page. Used for things like "carbon ads" or "table". 125 | * -------------------------------------------------------------------------- */ 126 | 127 | :root { 128 | --vp-c-bg: #ffffff; 129 | --vp-c-bg-alt: #f6f6f7; 130 | --vp-c-bg-elv: #ffffff; 131 | --vp-c-bg-soft: #f6f6f7; 132 | } 133 | 134 | .dark { 135 | --vp-c-bg: #1b1b1f; 136 | --vp-c-bg-alt: #161618; 137 | --vp-c-bg-elv: #202127; 138 | --vp-c-bg-soft: #202127; 139 | } 140 | 141 | /** 142 | * Colors: Borders 143 | * 144 | * - `divider`: This is used for separators. This is used to divide sections 145 | * within the same components, such as having separator on "h2" heading. 146 | * 147 | * - `border`: This is designed for borders on interactive components. 148 | * For example this should be used for a button outline. 149 | * 150 | * - `gutter`: This is used to divide components in the page. For example 151 | * the header and the lest of the page. 152 | * -------------------------------------------------------------------------- */ 153 | 154 | :root { 155 | --vp-c-border: #c2c2c4; 156 | --vp-c-divider: #e2e2e3; 157 | --vp-c-gutter: #e2e2e3; 158 | } 159 | 160 | .dark { 161 | --vp-c-border: #3c3f44; 162 | --vp-c-divider: #2e2e32; 163 | --vp-c-gutter: #000000; 164 | } 165 | 166 | /** 167 | * Colors: Text 168 | * 169 | * - `text-1`: Used for primary text. 170 | * 171 | * - `text-2`: Used for muted texts, such as "inactive menu" or "info texts". 172 | * 173 | * - `text-3`: Used for subtle texts, such as "placeholders" or "caret icon". 174 | * -------------------------------------------------------------------------- */ 175 | 176 | :root { 177 | --vp-c-text-1: rgba(60, 60, 67); 178 | --vp-c-text-2: rgba(60, 60, 67, 0.78); 179 | --vp-c-text-3: rgba(60, 60, 67, 0.56); 180 | } 181 | 182 | .dark { 183 | --vp-c-text-1: rgba(255, 255, 245, 0.86); 184 | --vp-c-text-2: rgba(235, 235, 245, 0.6); 185 | --vp-c-text-3: rgba(235, 235, 245, 0.38); 186 | } 187 | 188 | /** 189 | * Colors: Function 190 | * 191 | * - `default`: The color used purely for subtle indication without any 192 | * special meanings attached to it such as bg color for menu hover state. 193 | * 194 | * - `brand`: Used for primary brand colors, such as link text, button with 195 | * brand theme, etc. 196 | * 197 | * - `tip`: Used to indicate useful information. The default theme uses the 198 | * brand color for this by default. 199 | * 200 | * - `warning`: Used to indicate warning to the users. Used in custom 201 | * container, badges, etc. 202 | * 203 | * - `danger`: Used to show error, or dangerous message to the users. Used 204 | * in custom container, badges, etc. 205 | * 206 | * To understand the scaling system, refer to "Colors: Palette" section. 207 | * -------------------------------------------------------------------------- */ 208 | 209 | :root { 210 | --vp-c-default-1: var(--vp-c-gray-1); 211 | --vp-c-default-2: var(--vp-c-gray-2); 212 | --vp-c-default-3: var(--vp-c-gray-3); 213 | --vp-c-default-soft: var(--vp-c-gray-soft); 214 | 215 | --vp-c-brand-1: var(--vp-c-indigo-1); 216 | --vp-c-brand-2: var(--vp-c-indigo-2); 217 | --vp-c-brand-3: var(--vp-c-indigo-3); 218 | --vp-c-brand-soft: var(--vp-c-indigo-soft); 219 | 220 | /* DEPRECATED: Use `--vp-c-brand-1` instead. */ 221 | --vp-c-brand: var(--vp-c-brand-1); 222 | 223 | --vp-c-tip-1: var(--vp-c-brand-1); 224 | --vp-c-tip-2: var(--vp-c-brand-2); 225 | --vp-c-tip-3: var(--vp-c-brand-3); 226 | --vp-c-tip-soft: var(--vp-c-brand-soft); 227 | 228 | --vp-c-note-1: var(--vp-c-brand-1); 229 | --vp-c-note-2: var(--vp-c-brand-2); 230 | --vp-c-note-3: var(--vp-c-brand-3); 231 | --vp-c-note-soft: var(--vp-c-brand-soft); 232 | 233 | --vp-c-success-1: var(--vp-c-green-1); 234 | --vp-c-success-2: var(--vp-c-green-2); 235 | --vp-c-success-3: var(--vp-c-green-3); 236 | --vp-c-success-soft: var(--vp-c-green-soft); 237 | 238 | --vp-c-important-1: var(--vp-c-purple-1); 239 | --vp-c-important-2: var(--vp-c-purple-2); 240 | --vp-c-important-3: var(--vp-c-purple-3); 241 | --vp-c-important-soft: var(--vp-c-purple-soft); 242 | 243 | --vp-c-warning-1: var(--vp-c-yellow-1); 244 | --vp-c-warning-2: var(--vp-c-yellow-2); 245 | --vp-c-warning-3: var(--vp-c-yellow-3); 246 | --vp-c-warning-soft: var(--vp-c-yellow-soft); 247 | 248 | --vp-c-danger-1: var(--vp-c-red-1); 249 | --vp-c-danger-2: var(--vp-c-red-2); 250 | --vp-c-danger-3: var(--vp-c-red-3); 251 | --vp-c-danger-soft: var(--vp-c-red-soft); 252 | 253 | --vp-c-caution-1: var(--vp-c-red-1); 254 | --vp-c-caution-2: var(--vp-c-red-2); 255 | --vp-c-caution-3: var(--vp-c-red-3); 256 | --vp-c-caution-soft: var(--vp-c-red-soft); 257 | } 258 | 259 | /** 260 | * Typography 261 | * -------------------------------------------------------------------------- */ 262 | 263 | /** 264 | * Shadows 265 | * -------------------------------------------------------------------------- */ 266 | 267 | :root { 268 | --vp-shadow-1: 0 1px 2px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.06); 269 | --vp-shadow-2: 0 3px 12px rgba(0, 0, 0, 0.07), 0 1px 4px rgba(0, 0, 0, 0.07); 270 | --vp-shadow-3: 0 12px 32px rgba(0, 0, 0, 0.1), 0 2px 6px rgba(0, 0, 0, 0.08); 271 | --vp-shadow-4: 0 14px 44px rgba(0, 0, 0, 0.12), 0 3px 9px rgba(0, 0, 0, 0.12); 272 | --vp-shadow-5: 0 18px 56px rgba(0, 0, 0, 0.16), 0 4px 12px rgba(0, 0, 0, 0.16); 273 | } 274 | 275 | /** 276 | * Z-indexes 277 | * -------------------------------------------------------------------------- */ 278 | 279 | :root { 280 | --vp-z-index-footer: 10; 281 | --vp-z-index-local-nav: 20; 282 | --vp-z-index-nav: 30; 283 | --vp-z-index-layout-top: 40; 284 | --vp-z-index-backdrop: 50; 285 | --vp-z-index-sidebar: 60; 286 | } 287 | 288 | @media (min-width: 960px) { 289 | :root { 290 | --vp-z-index-sidebar: 25; 291 | } 292 | } 293 | 294 | /** 295 | * Layouts 296 | * -------------------------------------------------------------------------- */ 297 | 298 | :root { 299 | --vp-layout-max-width: 1440px; 300 | } 301 | 302 | /** 303 | * Component: Header Anchor 304 | * -------------------------------------------------------------------------- */ 305 | 306 | :root { 307 | --vp-header-anchor-symbol: '#'; 308 | } 309 | 310 | /** 311 | * Component: Code 312 | * -------------------------------------------------------------------------- */ 313 | 314 | :root { 315 | --vp-code-line-height: 1.7; 316 | --vp-code-font-size: 0.875em; 317 | --vp-code-color: var(--vp-c-brand-1); 318 | --vp-code-link-color: var(--vp-c-brand-1); 319 | --vp-code-link-hover-color: var(--vp-c-brand-2); 320 | --vp-code-bg: var(--vp-c-default-soft); 321 | 322 | --vp-code-block-color: var(--vp-c-text-2); 323 | --vp-code-block-bg: var(--vp-c-bg-alt); 324 | --vp-code-block-divider-color: var(--vp-c-gutter); 325 | 326 | --vp-code-lang-color: var(--vp-c-text-3); 327 | 328 | --vp-code-line-highlight-color: var(--vp-c-default-soft); 329 | --vp-code-line-number-color: var(--vp-c-text-3); 330 | 331 | --vp-code-line-diff-add-color: var(--vp-c-success-soft); 332 | --vp-code-line-diff-add-symbol-color: var(--vp-c-success-1); 333 | 334 | --vp-code-line-diff-remove-color: var(--vp-c-danger-soft); 335 | --vp-code-line-diff-remove-symbol-color: var(--vp-c-danger-1); 336 | 337 | --vp-code-line-warning-color: var(--vp-c-warning-soft); 338 | --vp-code-line-error-color: var(--vp-c-danger-soft); 339 | 340 | --vp-code-copy-code-border-color: var(--vp-c-divider); 341 | --vp-code-copy-code-bg: var(--vp-c-bg-soft); 342 | --vp-code-copy-code-hover-border-color: var(--vp-c-divider); 343 | --vp-code-copy-code-hover-bg: var(--vp-c-bg); 344 | --vp-code-copy-code-active-text: var(--vp-c-text-2); 345 | --vp-code-copy-copied-text-content: 'Copied'; 346 | 347 | --vp-code-tab-divider: var(--vp-code-block-divider-color); 348 | --vp-code-tab-text-color: var(--vp-c-text-2); 349 | --vp-code-tab-bg: var(--vp-code-block-bg); 350 | --vp-code-tab-hover-text-color: var(--vp-c-text-1); 351 | --vp-code-tab-active-text-color: var(--vp-c-text-1); 352 | --vp-code-tab-active-bar-color: var(--vp-c-brand-1); 353 | } 354 | 355 | /** 356 | * Component: Button 357 | * -------------------------------------------------------------------------- */ 358 | 359 | :root { 360 | --vp-button-brand-border: transparent; 361 | --vp-button-brand-text: var(--vp-c-white); 362 | --vp-button-brand-bg: var(--vp-c-brand-3); 363 | --vp-button-brand-hover-border: transparent; 364 | --vp-button-brand-hover-text: var(--vp-c-white); 365 | --vp-button-brand-hover-bg: var(--vp-c-brand-2); 366 | --vp-button-brand-active-border: transparent; 367 | --vp-button-brand-active-text: var(--vp-c-white); 368 | --vp-button-brand-active-bg: var(--vp-c-brand-1); 369 | 370 | --vp-button-alt-border: transparent; 371 | --vp-button-alt-text: var(--vp-c-text-1); 372 | --vp-button-alt-bg: var(--vp-c-default-3); 373 | --vp-button-alt-hover-border: transparent; 374 | --vp-button-alt-hover-text: var(--vp-c-text-1); 375 | --vp-button-alt-hover-bg: var(--vp-c-default-2); 376 | --vp-button-alt-active-border: transparent; 377 | --vp-button-alt-active-text: var(--vp-c-text-1); 378 | --vp-button-alt-active-bg: var(--vp-c-default-1); 379 | 380 | --vp-button-sponsor-border: var(--vp-c-text-2); 381 | --vp-button-sponsor-text: var(--vp-c-text-2); 382 | --vp-button-sponsor-bg: transparent; 383 | --vp-button-sponsor-hover-border: var(--vp-c-sponsor); 384 | --vp-button-sponsor-hover-text: var(--vp-c-sponsor); 385 | --vp-button-sponsor-hover-bg: transparent; 386 | --vp-button-sponsor-active-border: var(--vp-c-sponsor); 387 | --vp-button-sponsor-active-text: var(--vp-c-sponsor); 388 | --vp-button-sponsor-active-bg: transparent; 389 | } 390 | 391 | /** 392 | * Component: Custom Block 393 | * -------------------------------------------------------------------------- */ 394 | 395 | :root { 396 | --vp-custom-block-font-size: 14px; 397 | --vp-custom-block-code-font-size: 13px; 398 | 399 | --vp-custom-block-info-border: transparent; 400 | --vp-custom-block-info-text: var(--vp-c-text-1); 401 | --vp-custom-block-info-bg: var(--vp-c-default-soft); 402 | --vp-custom-block-info-code-bg: var(--vp-c-default-soft); 403 | 404 | --vp-custom-block-note-border: transparent; 405 | --vp-custom-block-note-text: var(--vp-c-text-1); 406 | --vp-custom-block-note-bg: var(--vp-c-default-soft); 407 | --vp-custom-block-note-code-bg: var(--vp-c-default-soft); 408 | 409 | --vp-custom-block-tip-border: transparent; 410 | --vp-custom-block-tip-text: var(--vp-c-text-1); 411 | --vp-custom-block-tip-bg: var(--vp-c-tip-soft); 412 | --vp-custom-block-tip-code-bg: var(--vp-c-tip-soft); 413 | 414 | --vp-custom-block-important-border: transparent; 415 | --vp-custom-block-important-text: var(--vp-c-text-1); 416 | --vp-custom-block-important-bg: var(--vp-c-important-soft); 417 | --vp-custom-block-important-code-bg: var(--vp-c-important-soft); 418 | 419 | --vp-custom-block-warning-border: transparent; 420 | --vp-custom-block-warning-text: var(--vp-c-text-1); 421 | --vp-custom-block-warning-bg: var(--vp-c-warning-soft); 422 | --vp-custom-block-warning-code-bg: var(--vp-c-warning-soft); 423 | 424 | --vp-custom-block-danger-border: transparent; 425 | --vp-custom-block-danger-text: var(--vp-c-text-1); 426 | --vp-custom-block-danger-bg: var(--vp-c-danger-soft); 427 | --vp-custom-block-danger-code-bg: var(--vp-c-danger-soft); 428 | 429 | --vp-custom-block-caution-border: transparent; 430 | --vp-custom-block-caution-text: var(--vp-c-text-1); 431 | --vp-custom-block-caution-bg: var(--vp-c-caution-soft); 432 | --vp-custom-block-caution-code-bg: var(--vp-c-caution-soft); 433 | 434 | --vp-custom-block-details-border: var(--vp-custom-block-info-border); 435 | --vp-custom-block-details-text: var(--vp-custom-block-info-text); 436 | --vp-custom-block-details-bg: var(--vp-custom-block-info-bg); 437 | --vp-custom-block-details-code-bg: var(--vp-custom-block-info-code-bg); 438 | } 439 | 440 | /** 441 | * Component: Input 442 | * -------------------------------------------------------------------------- */ 443 | 444 | :root { 445 | --vp-input-border-color: var(--vp-c-border); 446 | --vp-input-bg-color: var(--vp-c-bg-alt); 447 | 448 | --vp-input-switch-bg-color: var(--vp-c-default-soft); 449 | } 450 | 451 | /** 452 | * Component: Nav 453 | * -------------------------------------------------------------------------- */ 454 | 455 | :root { 456 | --vp-nav-height: 64px; 457 | --vp-nav-bg-color: var(--vp-c-bg); 458 | --vp-nav-screen-bg-color: var(--vp-c-bg); 459 | --vp-nav-logo-height: 24px; 460 | } 461 | 462 | .hide-nav { 463 | --vp-nav-height: 0px; 464 | } 465 | 466 | .hide-nav .VPSidebar { 467 | --vp-nav-height: 22px; 468 | } 469 | 470 | /** 471 | * Component: Local Nav 472 | * -------------------------------------------------------------------------- */ 473 | 474 | :root { 475 | --vp-local-nav-bg-color: var(--vp-c-bg); 476 | } 477 | 478 | /** 479 | * Component: Sidebar 480 | * -------------------------------------------------------------------------- */ 481 | 482 | :root { 483 | --vp-sidebar-width: 272px; 484 | --vp-sidebar-bg-color: var(--vp-c-bg-alt); 485 | } 486 | 487 | /** 488 | * Colors Backdrop 489 | * -------------------------------------------------------------------------- */ 490 | 491 | :root { 492 | --vp-backdrop-bg-color: rgba(0, 0, 0, 0.6); 493 | } 494 | 495 | /** 496 | * Component: Home 497 | * -------------------------------------------------------------------------- */ 498 | 499 | :root { 500 | --vp-home-hero-name-color: var(--vp-c-brand-1); 501 | --vp-home-hero-name-background: transparent; 502 | 503 | --vp-home-hero-image-background-image: none; 504 | --vp-home-hero-image-filter: none; 505 | } 506 | 507 | /** 508 | * Component: Badge 509 | * -------------------------------------------------------------------------- */ 510 | 511 | :root { 512 | --vp-badge-info-border: transparent; 513 | --vp-badge-info-text: var(--vp-c-text-2); 514 | --vp-badge-info-bg: var(--vp-c-default-soft); 515 | 516 | --vp-badge-tip-border: transparent; 517 | --vp-badge-tip-text: var(--vp-c-tip-1); 518 | --vp-badge-tip-bg: var(--vp-c-tip-soft); 519 | 520 | --vp-badge-warning-border: transparent; 521 | --vp-badge-warning-text: var(--vp-c-warning-1); 522 | --vp-badge-warning-bg: var(--vp-c-warning-soft); 523 | 524 | --vp-badge-danger-border: transparent; 525 | --vp-badge-danger-text: var(--vp-c-danger-1); 526 | --vp-badge-danger-bg: var(--vp-c-danger-soft); 527 | } 528 | 529 | /** 530 | * Component: Carbon Ads 531 | * -------------------------------------------------------------------------- */ 532 | 533 | :root { 534 | --vp-carbon-ads-text-color: var(--vp-c-text-1); 535 | --vp-carbon-ads-poweredby-color: var(--vp-c-text-2); 536 | --vp-carbon-ads-bg-color: var(--vp-c-bg-soft); 537 | --vp-carbon-ads-hover-text-color: var(--vp-c-brand-1); 538 | --vp-carbon-ads-hover-poweredby-color: var(--vp-c-text-1); 539 | } 540 | 541 | /** 542 | * Component: Local Search 543 | * -------------------------------------------------------------------------- */ 544 | 545 | :root { 546 | --vp-local-search-bg: var(--vp-c-bg); 547 | --vp-local-search-result-bg: var(--vp-c-bg); 548 | --vp-local-search-result-border: var(--vp-c-divider); 549 | --vp-local-search-result-selected-bg: var(--vp-c-bg); 550 | --vp-local-search-result-selected-border: var(--vp-c-brand-1); 551 | --vp-local-search-highlight-bg: var(--vp-c-brand-1); 552 | --vp-local-search-highlight-text: var(--vp-c-neutral-inverse); 553 | } --------------------------------------------------------------------------------