From 580ed8a754dad06f1945968571b333c1545d8294 Mon Sep 17 00:00:00 2001 From: Masayuki Takeda Date: Sun, 3 Aug 2025 16:29:06 +0800 Subject: [PATCH 01/10] add language stanza --- src/ocamlorg_data/data_intf.ml | 3 +++ tool/ood-gen/lib/tutorial.ml | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/ocamlorg_data/data_intf.ml b/src/ocamlorg_data/data_intf.ml index 782e408db5..f7efdada9e 100644 --- a/src/ocamlorg_data/data_intf.ml +++ b/src/ocamlorg_data/data_intf.ml @@ -475,6 +475,8 @@ module Tutorial = struct slug : string; } + type language = English | Japanese [@@deriving show] + type t = { title : string; short_title : string; @@ -489,6 +491,7 @@ module Tutorial = struct body_html : string; recommended_next_tutorials : recommended_next_tutorials; prerequisite_tutorials : prerequisite_tutorials; + language : language; } end diff --git a/tool/ood-gen/lib/tutorial.ml b/tool/ood-gen/lib/tutorial.ml index 9ae7239273..76d798f533 100644 --- a/tool/ood-gen/lib/tutorial.ml +++ b/tool/ood-gen/lib/tutorial.ml @@ -34,6 +34,24 @@ type search_document_section = type search_document = [%import: Data_intf.Tutorial.search_document] [@@deriving show] +type language = [%import: Data_intf.Tutorial.language] [@@deriving show] + +(* Deriving of_yaml doesn't seem to work in an intuitive way for regular variant; + in order to have a type like [language] to be parsed, we need to write + something like: + + {v + language: + English: [] + v} + + So we instead write a custom parser to get the behavior we want. *) +let language_of_yaml : Yaml.value -> (language, _) result = + function + | `String "English" -> Ok English + | `String "Japanese" -> Ok Japanese + | value -> Error (`Msg ("Unexpected language value: " ^ Yaml.to_string_exn value)) + type t = [%import: Data_intf.Tutorial.t] [@@deriving show] type metadata = { @@ -45,6 +63,7 @@ type metadata = { external_tutorial : external_tutorial option; recommended_next_tutorials : recommended_next_tutorials option; prerequisite_tutorials : prerequisite_tutorials option; + language : language; } [@@deriving of_yaml, From 4f7b1715448d5fcf83657e26d570772539a28aae Mon Sep 17 00:00:00 2001 From: Masayuki Takeda Date: Sun, 3 Aug 2025 16:29:43 +0800 Subject: [PATCH 02/10] add language stanza to relevant markdown files --- data/tutorials/getting-started/1_00_install_OCaml.md | 1 + data/tutorials/getting-started/1_01_a_tour_of_ocaml.md | 1 + data/tutorials/getting-started/1_02_your_first_ocaml_program.md | 1 + data/tutorials/getting-started/2_00_editor_setup.md | 1 + data/tutorials/getting-started/2_01_toplevel.md | 1 + data/tutorials/getting-started/2_02_opam_switch.md | 1 + data/tutorials/getting-started/3_01_ocaml_on_windows.md | 1 + data/tutorials/getting-started/3_02_arm_fix.md | 1 + data/tutorials/getting-started/3_03_ocaml_playground.md | 1 + data/tutorials/guides/0tt_00_formatting_text.md | 1 + data/tutorials/guides/0tt_01_command_line_arguments.md | 1 + data/tutorials/guides/0tt_02_file_manipulation.md | 1 + data/tutorials/guides/0tt_03_calling_c_libraries.md | 1 + data/tutorials/guides/0tt_04_calling_fortran_libraries.md | 1 + .../guides/1wf_00_using_the_ocaml_compiler_toolchain.md | 1 + data/tutorials/guides/1wf_01_debugging.md | 1 + data/tutorials/guides/1wf_02_error_handling.md | 1 + data/tutorials/guides/1wf_03_profiling.md | 1 + data/tutorials/guides/1wf_04_multicore_ready.md | 1 + data/tutorials/guides/1wf_05_garbage_collection.md | 1 + data/tutorials/guides/rs_00_guidelines.md | 1 + data/tutorials/guides/rs_01_common_error_messages.md | 1 + data/tutorials/guides/rs_02_comparison_of_standard_containers.md | 1 + data/tutorials/language/0it_00_values_functions.md | 1 + data/tutorials/language/0it_01_basic_datatypes.md | 1 + data/tutorials/language/0it_02_loops_and_recursion.md | 1 + data/tutorials/language/0it_03_lists.md | 1 + data/tutorials/language/0it_04_higher_order_functions.md | 1 + data/tutorials/language/0it_05_labels.md | 1 + data/tutorials/language/0it_06_imperative.md | 1 + data/tutorials/language/1ms_00_modules.md | 1 + data/tutorials/language/1ms_01_functors.md | 1 + data/tutorials/language/1ms_03_dune.md | 1 + data/tutorials/language/3ds_00_options.md | 1 + data/tutorials/language/3ds_01_arrays.md | 1 + data/tutorials/language/3ds_02_maps.md | 1 + data/tutorials/language/3ds_03_sets.md | 1 + data/tutorials/language/3ds_04_hashtbl.md | 1 + data/tutorials/language/3ds_05_seq.md | 1 + data/tutorials/language/3ds_06_memoization.md | 1 + data/tutorials/language/3ds_07_monads.md | 1 + data/tutorials/language/4ad_00_metaprogramming.md | 1 + data/tutorials/language/4ad_01_operators.md | 1 + data/tutorials/language/4ad_02_objects.md | 1 + data/tutorials/language/5rt_00_memory_representation.md | 1 + data/tutorials/language/5rt_01_garbage-collector.md | 1 + data/tutorials/language/5rt_02_compiler_frontend.md | 1 + data/tutorials/language/5rt_03_compiler_backend.md | 1 + data/tutorials/platform/0_00_bootstrap_project.md | 1 + data/tutorials/platform/0_01_managing_deps.md | 1 + data/tutorials/platform/0_02_install_compiler.md | 1 + data/tutorials/platform/0_03_run_executables_and_tests.md | 1 + data/tutorials/platform/0_09_opam_path.md | 1 + data/tutorials/platform/1_07_ocamlformat.md | 1 + data/tutorials/platform/2_08_odoc.md | 1 + data/tutorials/platform/3_04_create_libraries.md | 1 + data/tutorials/platform/3_05_publish_packages.md | 1 + 57 files changed, 57 insertions(+) diff --git a/data/tutorials/getting-started/1_00_install_OCaml.md b/data/tutorials/getting-started/1_00_install_OCaml.md index caa4ed093f..c1f5158737 100644 --- a/data/tutorials/getting-started/1_00_install_OCaml.md +++ b/data/tutorials/getting-started/1_00_install_OCaml.md @@ -5,6 +5,7 @@ description: | This page will help you install OCaml and the OCaml Platform Tools. | These instructions work on Windows, and Unix systems like Linux, and macOS. category: "First Steps" +language: English --- This guide will walk you through a minimum installation of OCaml. That includes installing a package manager and [the compiler](#installation-on-unix-and-macos) itself. We'll also install some platform tools like a build system, support for your editor, and a few other important ones. diff --git a/data/tutorials/getting-started/1_01_a_tour_of_ocaml.md b/data/tutorials/getting-started/1_01_a_tour_of_ocaml.md index 16a0ef333d..1b00658a37 100644 --- a/data/tutorials/getting-started/1_01_a_tour_of_ocaml.md +++ b/data/tutorials/getting-started/1_01_a_tour_of_ocaml.md @@ -4,6 +4,7 @@ title: A Tour of OCaml description: > Hop on the OCaml sightseeing bus. This absolute beginner tutorial will drive you through the marvels and wonders of OCaml. We'll have a look at the most commonly used language features. category: "First Steps" +language: English recommended_next_tutorials: - "values-and-functions" - "basic-data-types" diff --git a/data/tutorials/getting-started/1_02_your_first_ocaml_program.md b/data/tutorials/getting-started/1_02_your_first_ocaml_program.md index 3173098a70..69ab9efa3f 100644 --- a/data/tutorials/getting-started/1_02_your_first_ocaml_program.md +++ b/data/tutorials/getting-started/1_02_your_first_ocaml_program.md @@ -4,6 +4,7 @@ title: Your First OCaml Program description: > Learn how to write your very first OCaml program. category: "First Steps" +language: English recommended_next_tutorials: - "values-and-functions" - "basic-data-types" diff --git a/data/tutorials/getting-started/2_00_editor_setup.md b/data/tutorials/getting-started/2_00_editor_setup.md index ef24c24b70..4a45313844 100644 --- a/data/tutorials/getting-started/2_00_editor_setup.md +++ b/data/tutorials/getting-started/2_00_editor_setup.md @@ -4,6 +4,7 @@ title: Configuring Your Editor description: | This page will show you how to set up your editor for OCaml. category: "Tooling" +language: English --- While the toplevel is great for interactively trying out the language, we will shortly need to write OCaml files in an editor. We already installed the tools required to enhance Merlin, our editor of choice with OCaml support. Merlin provides all features such as "jump to definition," "show type," and `ocaml-lsp-server`, a server that delivers those features to the editor through the LSP server. OCaml has plugins for many editors, but the most actively maintained are for Visual Studio Code, Emacs, and Vim. diff --git a/data/tutorials/getting-started/2_01_toplevel.md b/data/tutorials/getting-started/2_01_toplevel.md index ac10339ca9..e0449c46aa 100644 --- a/data/tutorials/getting-started/2_01_toplevel.md +++ b/data/tutorials/getting-started/2_01_toplevel.md @@ -4,6 +4,7 @@ title: Introduction to the OCaml Toplevel description: | This page will give you a brief introduction to the OCaml toplevel. category: "Tooling" +language: English --- An OCaml toplevel is a chat between the user and OCaml. The user writes OCaml code, and UTop evaluates it. This is why it is also called a Read-Eval-Print-Loop (REPL). Several OCaml toplevels exist, like `ocaml` and `utop`. We recommend using UTop, which is part of the [OCaml Platform](/docs/platform) toolchain. diff --git a/data/tutorials/getting-started/2_02_opam_switch.md b/data/tutorials/getting-started/2_02_opam_switch.md index 940d879f06..df26a2f79f 100644 --- a/data/tutorials/getting-started/2_02_opam_switch.md +++ b/data/tutorials/getting-started/2_02_opam_switch.md @@ -4,6 +4,7 @@ title: Introduction to opam Switches description: | This page will give you a brief introduction to opam switches, what they're used for, and how to create them. category: "Tooling" +language: English --- OCaml's package manager, opam, introduces the concept of a _switch_, which is an isolated OCaml environment. These switches often cause confusion amongst OCaml newcomers, so this document aims to provide a better understanding of opam switches and their usage for managing dependencies and project-specific configurations. diff --git a/data/tutorials/getting-started/3_01_ocaml_on_windows.md b/data/tutorials/getting-started/3_01_ocaml_on_windows.md index 702aa5b07f..f452a1053a 100644 --- a/data/tutorials/getting-started/3_01_ocaml_on_windows.md +++ b/data/tutorials/getting-started/3_01_ocaml_on_windows.md @@ -4,6 +4,7 @@ title: OCaml on Windows description: > Read about the state of OCaml on Windows and our roadmap to improve Windows support. category: "Resources" +language: English --- We recommend installing [opam](https://opam.ocaml.org/) for new users. Opam, the OCaml package manager, has full Windows support since version 2.2 and provides the most up-to-date OCaml environment. diff --git a/data/tutorials/getting-started/3_02_arm_fix.md b/data/tutorials/getting-started/3_02_arm_fix.md index 42b0755407..89243b17d9 100644 --- a/data/tutorials/getting-started/3_02_arm_fix.md +++ b/data/tutorials/getting-started/3_02_arm_fix.md @@ -4,6 +4,7 @@ title: Fix Homebrew Errors on Apple M1 description: | This page will walk you through the workaround for ARM64 processors on newer Macs. category: "Resources" +language: English --- Since [Homebrew has changed](https://github.com/Homebrew/brew/issues/9177) the way it installs, sometimes the executable files cannot be found on macOS ARM64 M1. This might cause errors as you work through these tutorials. We want Homebrew to install ARM64 by default, so there are a few changes we need to make in order to do this. diff --git a/data/tutorials/getting-started/3_03_ocaml_playground.md b/data/tutorials/getting-started/3_03_ocaml_playground.md index eaeaddb461..08ef656219 100644 --- a/data/tutorials/getting-started/3_03_ocaml_playground.md +++ b/data/tutorials/getting-started/3_03_ocaml_playground.md @@ -5,6 +5,7 @@ short_title: The OCaml Playground description: | This page will walk you through the OCaml Playground category: "Resources" +language: English --- Welcome to OCaml's in-browser playground! diff --git a/data/tutorials/guides/0tt_00_formatting_text.md b/data/tutorials/guides/0tt_00_formatting_text.md index 083563283d..602986f45c 100644 --- a/data/tutorials/guides/0tt_00_formatting_text.md +++ b/data/tutorials/guides/0tt_00_formatting_text.md @@ -6,6 +6,7 @@ description: > provides pretty-printing facilities to get a fancy display for printing routines category: "Tutorials" +language: English --- The `Format` module of Caml Light and OCaml's standard libraries diff --git a/data/tutorials/guides/0tt_01_command_line_arguments.md b/data/tutorials/guides/0tt_01_command_line_arguments.md index 08404e8bb2..225627f664 100644 --- a/data/tutorials/guides/0tt_01_command_line_arguments.md +++ b/data/tutorials/guides/0tt_01_command_line_arguments.md @@ -4,6 +4,7 @@ title: Command-line Arguments description: > The Arg module that comes with the compiler can help you write command line interfaces category: "Tutorials" +language: English --- In this tutorial we learn how to read command line arguments directly, using diff --git a/data/tutorials/guides/0tt_02_file_manipulation.md b/data/tutorials/guides/0tt_02_file_manipulation.md index e667a2e7f0..4c0e01235c 100644 --- a/data/tutorials/guides/0tt_02_file_manipulation.md +++ b/data/tutorials/guides/0tt_02_file_manipulation.md @@ -4,6 +4,7 @@ title: File Manipulation description: > A guide to basic file manipulation in OCaml with the standard library category: "Tutorials" +language: English --- This is a guide to basic file manipulation in OCaml using only the diff --git a/data/tutorials/guides/0tt_03_calling_c_libraries.md b/data/tutorials/guides/0tt_03_calling_c_libraries.md index e63629400a..9a474688ca 100644 --- a/data/tutorials/guides/0tt_03_calling_c_libraries.md +++ b/data/tutorials/guides/0tt_03_calling_c_libraries.md @@ -4,6 +4,7 @@ title: Calling C Libraries description: > Cross the divide and call C code from your OCaml program category: "Tutorials" +language: English --- ## MiniGtk diff --git a/data/tutorials/guides/0tt_04_calling_fortran_libraries.md b/data/tutorials/guides/0tt_04_calling_fortran_libraries.md index 39f2e11cf3..f4b9eaf5fa 100644 --- a/data/tutorials/guides/0tt_04_calling_fortran_libraries.md +++ b/data/tutorials/guides/0tt_04_calling_fortran_libraries.md @@ -4,6 +4,7 @@ title: Calling Fortran Libraries description: > Cross the divide and call Fortran code from your OCaml program category: "Tutorials" +language: English --- Fortran isn't a language the many people write new code in but it still diff --git a/data/tutorials/guides/1wf_00_using_the_ocaml_compiler_toolchain.md b/data/tutorials/guides/1wf_00_using_the_ocaml_compiler_toolchain.md index 6b544822fb..0a006e4f4e 100644 --- a/data/tutorials/guides/1wf_00_using_the_ocaml_compiler_toolchain.md +++ b/data/tutorials/guides/1wf_00_using_the_ocaml_compiler_toolchain.md @@ -4,6 +4,7 @@ title: Using the OCaml Compiler Toolchain description: > An introduction to the OCaml compiler tools for building OCaml projects as well as the most common build tools such as Dune category: "Guides" +language: English --- This tutorial explains how to compile your OCaml programs into executable form. diff --git a/data/tutorials/guides/1wf_01_debugging.md b/data/tutorials/guides/1wf_01_debugging.md index 87ba99cd55..9a8d42f0de 100644 --- a/data/tutorials/guides/1wf_01_debugging.md +++ b/data/tutorials/guides/1wf_01_debugging.md @@ -4,6 +4,7 @@ title: Debugging description: > Learn to debug OCaml programs using tracing and ocamldebug category: "Guides" +language: English --- This tutorial presents four techniques for debugging OCaml programs: diff --git a/data/tutorials/guides/1wf_02_error_handling.md b/data/tutorials/guides/1wf_02_error_handling.md index 3b6ee3e833..6ed9457265 100644 --- a/data/tutorials/guides/1wf_02_error_handling.md +++ b/data/tutorials/guides/1wf_02_error_handling.md @@ -4,6 +4,7 @@ title: Error Handling description: > Discover the different ways you can manage errors in your OCaml programs category: "Guides" +language: English --- In OCaml, errors can be handled in several ways. This document presents most of diff --git a/data/tutorials/guides/1wf_03_profiling.md b/data/tutorials/guides/1wf_03_profiling.md index 24f44592fd..96e9a1f6a3 100644 --- a/data/tutorials/guides/1wf_03_profiling.md +++ b/data/tutorials/guides/1wf_03_profiling.md @@ -4,6 +4,7 @@ title: Profiling description: > Understand how to profile your OCaml code to analyse its performance and produce faster programs category: "Guides" +language: English --- ## Speed diff --git a/data/tutorials/guides/1wf_04_multicore_ready.md b/data/tutorials/guides/1wf_04_multicore_ready.md index 9f461b3fb9..3ea05892e2 100644 --- a/data/tutorials/guides/1wf_04_multicore_ready.md +++ b/data/tutorials/guides/1wf_04_multicore_ready.md @@ -5,6 +5,7 @@ short_title: Transitioning to Multicore with TSan description: > Learn to make your OCaml code multicore ready with ThreadSanitizer category: "Guides" +language: English --- The 5.0 release brought Multicore, `Domain`-based parallelism to the diff --git a/data/tutorials/guides/1wf_05_garbage_collection.md b/data/tutorials/guides/1wf_05_garbage_collection.md index 1e94ddc230..f34dad7694 100644 --- a/data/tutorials/guides/1wf_05_garbage_collection.md +++ b/data/tutorials/guides/1wf_05_garbage_collection.md @@ -4,6 +4,7 @@ title: How to Work with the Garbage Collector description: > How to use the Gc module in OCaml and how to write your own finalisers. category: "Guides" +language: English --- In [Understanding the Garbage Collector](/docs/garbage-collector), discussed how Garbage Collection in OCaml works. diff --git a/data/tutorials/guides/rs_00_guidelines.md b/data/tutorials/guides/rs_00_guidelines.md index a6057d404f..7dd773e859 100644 --- a/data/tutorials/guides/rs_00_guidelines.md +++ b/data/tutorials/guides/rs_00_guidelines.md @@ -4,6 +4,7 @@ title: OCaml Programming Guidelines description: > Opinionated guidelines for writing OCaml code category: "Resources" +language: English --- This is a set of reasonable guidelines for writing OCaml diff --git a/data/tutorials/guides/rs_01_common_error_messages.md b/data/tutorials/guides/rs_01_common_error_messages.md index 269ce02750..95ec2964fc 100644 --- a/data/tutorials/guides/rs_01_common_error_messages.md +++ b/data/tutorials/guides/rs_01_common_error_messages.md @@ -4,6 +4,7 @@ title: Common Error Messages description: > Understand the most common error messages the OCaml compiler can throw at you category: "Resources" +language: English --- This page gives a list of quick explanations for some error or warning diff --git a/data/tutorials/guides/rs_02_comparison_of_standard_containers.md b/data/tutorials/guides/rs_02_comparison_of_standard_containers.md index ff39f0d2a1..5a4cbf1d09 100644 --- a/data/tutorials/guides/rs_02_comparison_of_standard_containers.md +++ b/data/tutorials/guides/rs_02_comparison_of_standard_containers.md @@ -4,6 +4,7 @@ title: Comparison of Standard Containers description: > Rough comparison of the different container types in OCaml category: "Resources" +language: English --- This is a rough comparison of the different container types diff --git a/data/tutorials/language/0it_00_values_functions.md b/data/tutorials/language/0it_00_values_functions.md index 8d374ff9ad..d0671b0e6b 100644 --- a/data/tutorials/language/0it_00_values_functions.md +++ b/data/tutorials/language/0it_00_values_functions.md @@ -4,6 +4,7 @@ title: Values and Functions description: | Functions, values, definitions, environments, scopes, closures, and shadowing. This tutorial will help you master the fundamentals. category: "Introduction" +language: English prerequisite_tutorials: - "toplevel-introduction" - "installing-ocaml" diff --git a/data/tutorials/language/0it_01_basic_datatypes.md b/data/tutorials/language/0it_01_basic_datatypes.md index 0d775399ba..ebf56d5301 100644 --- a/data/tutorials/language/0it_01_basic_datatypes.md +++ b/data/tutorials/language/0it_01_basic_datatypes.md @@ -4,6 +4,7 @@ title: Basic Data Types and Pattern Matching description: | Predefined Types, Variants, Records, and Pattern Matching category: "Introduction" +language: English prerequisite_tutorials: - "tour-of-ocaml" - "values-and-functions" diff --git a/data/tutorials/language/0it_02_loops_and_recursion.md b/data/tutorials/language/0it_02_loops_and_recursion.md index aed6145030..71d30e52b8 100644 --- a/data/tutorials/language/0it_02_loops_and_recursion.md +++ b/data/tutorials/language/0it_02_loops_and_recursion.md @@ -4,6 +4,7 @@ title: Loops and Recursions description: > Learn basic control-flow and recursion in OCaml category: "Introduction" +language: English --- As in other OCaml.org documentation, the code examples will either be something you can test or diff --git a/data/tutorials/language/0it_03_lists.md b/data/tutorials/language/0it_03_lists.md index bb05f3da9c..d199ce5833 100644 --- a/data/tutorials/language/0it_03_lists.md +++ b/data/tutorials/language/0it_03_lists.md @@ -4,6 +4,7 @@ title: Lists description: > Learn about one of OCaml's must used, built-in data types category: "Introduction" +language: English --- A list is an ordered sequence of elements. All elements of a list in OCaml must diff --git a/data/tutorials/language/0it_04_higher_order_functions.md b/data/tutorials/language/0it_04_higher_order_functions.md index c47b35e7d2..4af20b255f 100644 --- a/data/tutorials/language/0it_04_higher_order_functions.md +++ b/data/tutorials/language/0it_04_higher_order_functions.md @@ -4,6 +4,7 @@ title: Higher Order Functions description: > Functions describe the world; higher-order functions take functions as parameters. Learn how to manipulate and leverage functions to write composable and reusable code. category: "Introduction" +language: English prerequisite_tutorials: - "values-and-functions" - "loops-recursion" diff --git a/data/tutorials/language/0it_05_labels.md b/data/tutorials/language/0it_05_labels.md index 7218e02a79..eb8e90406e 100644 --- a/data/tutorials/language/0it_05_labels.md +++ b/data/tutorials/language/0it_05_labels.md @@ -4,6 +4,7 @@ title: Labelled and Optional Arguments description: > Provide labels to your functions arguments category: "Introduction" +language: English prerequisite_tutorials: - "values-and-functions" --- diff --git a/data/tutorials/language/0it_06_imperative.md b/data/tutorials/language/0it_06_imperative.md index 30b77c12a0..3375aa6851 100644 --- a/data/tutorials/language/0it_06_imperative.md +++ b/data/tutorials/language/0it_06_imperative.md @@ -4,6 +4,7 @@ title: Mutability and Imperative Control Flow description: > Write stateful programs in OCaml. Use for and while loops, if-then-else, mutable record fields, and references. category: "Introduction" +language: English prerequisite_tutorials: - "basic-data-types" - "values-and-functions" diff --git a/data/tutorials/language/1ms_00_modules.md b/data/tutorials/language/1ms_00_modules.md index 7a03810d5c..3be3c76530 100644 --- a/data/tutorials/language/1ms_00_modules.md +++ b/data/tutorials/language/1ms_00_modules.md @@ -4,6 +4,7 @@ title: Modules description: > Modules are collections of definitions. This is the basic means to organise OCaml software. category: "Module System" +language: English prerequisite_tutorials: - values-and-functions - basic-data-types diff --git a/data/tutorials/language/1ms_01_functors.md b/data/tutorials/language/1ms_01_functors.md index 9cceaf187a..2fa3513f70 100644 --- a/data/tutorials/language/1ms_01_functors.md +++ b/data/tutorials/language/1ms_01_functors.md @@ -4,6 +4,7 @@ title: Functors description: > In OCaml, a functor is a function at the module-level. Functors take modules as arguments and return a new module. category: "Module System" +language: English prerequisite_tutorials: - modules --- diff --git a/data/tutorials/language/1ms_03_dune.md b/data/tutorials/language/1ms_03_dune.md index 586679e9e0..4e4a54a192 100644 --- a/data/tutorials/language/1ms_03_dune.md +++ b/data/tutorials/language/1ms_03_dune.md @@ -4,6 +4,7 @@ title: Libraries With Dune description: > Dune provides several means to arrange modules into libraries. We look at Dune's mechanisms for structuring projects with libraries that contain modules. category: "Module System" +language: English prerequisite_tutorials: - modules - functors diff --git a/data/tutorials/language/3ds_00_options.md b/data/tutorials/language/3ds_00_options.md index cabd183b23..05740d2c89 100644 --- a/data/tutorials/language/3ds_00_options.md +++ b/data/tutorials/language/3ds_00_options.md @@ -4,6 +4,7 @@ title: Options description: > Add nothing-as-value to anything to avoid confusion between something and “no such thing“. category: "Data Structures" +language: English --- ## Introduction diff --git a/data/tutorials/language/3ds_01_arrays.md b/data/tutorials/language/3ds_01_arrays.md index 7a1cfbb5e6..760169f739 100644 --- a/data/tutorials/language/3ds_01_arrays.md +++ b/data/tutorials/language/3ds_01_arrays.md @@ -4,6 +4,7 @@ title: Arrays description: > The standard library's Array module category: "Data Structures" +language: English --- ## Introduction diff --git a/data/tutorials/language/3ds_02_maps.md b/data/tutorials/language/3ds_02_maps.md index 826902f77e..c79e08efb9 100644 --- a/data/tutorials/language/3ds_02_maps.md +++ b/data/tutorials/language/3ds_02_maps.md @@ -4,6 +4,7 @@ title: Maps description: > Create a mapping using the standard library's Map module category: "Data Structures" +language: English --- ## Introduction diff --git a/data/tutorials/language/3ds_03_sets.md b/data/tutorials/language/3ds_03_sets.md index 9e588b0bd2..09f81831ce 100644 --- a/data/tutorials/language/3ds_03_sets.md +++ b/data/tutorials/language/3ds_03_sets.md @@ -4,6 +4,7 @@ title: Sets description: > The standard library's Set module category: "Data Structures" +language: English --- ## Introduction diff --git a/data/tutorials/language/3ds_04_hashtbl.md b/data/tutorials/language/3ds_04_hashtbl.md index b6bc9689f1..2616e13600 100644 --- a/data/tutorials/language/3ds_04_hashtbl.md +++ b/data/tutorials/language/3ds_04_hashtbl.md @@ -4,6 +4,7 @@ title: Hash Tables description: > Discover efficient and mutable lookup tables with OCaml's Hashtbl module category: "Data Structures" +language: English --- ## Introduction diff --git a/data/tutorials/language/3ds_05_seq.md b/data/tutorials/language/3ds_05_seq.md index e0d3c6167a..b10e0e17a2 100644 --- a/data/tutorials/language/3ds_05_seq.md +++ b/data/tutorials/language/3ds_05_seq.md @@ -4,6 +4,7 @@ title: Sequences description: > Learn about sequences, of OCaml's most-used, built-in data types category: "Data Structures" +language: English prerequisite_tutorials: - "lists" - "options" diff --git a/data/tutorials/language/3ds_06_memoization.md b/data/tutorials/language/3ds_06_memoization.md index c05f8d0b9a..3482b88674 100644 --- a/data/tutorials/language/3ds_06_memoization.md +++ b/data/tutorials/language/3ds_06_memoization.md @@ -4,6 +4,7 @@ title: Memoization description: > Memoization, adapted from OCaml Programming: Correct + Efficient + Beautiful category: "Data Structures" +language: English external_tutorial: tag: "CS3110" banner: diff --git a/data/tutorials/language/3ds_07_monads.md b/data/tutorials/language/3ds_07_monads.md index 19f4360723..85c64a8914 100644 --- a/data/tutorials/language/3ds_07_monads.md +++ b/data/tutorials/language/3ds_07_monads.md @@ -4,6 +4,7 @@ title: Monads description: > Monads, adapted from OCaml Programming: Correct + Efficient + Beautiful category: "Data Structures" +language: English external_tutorial: tag: "CS3110" banner: diff --git a/data/tutorials/language/4ad_00_metaprogramming.md b/data/tutorials/language/4ad_00_metaprogramming.md index 3448eb269a..a954a6f059 100644 --- a/data/tutorials/language/4ad_00_metaprogramming.md +++ b/data/tutorials/language/4ad_00_metaprogramming.md @@ -5,6 +5,7 @@ description: | An introduction to metaprogramming in OCaml, including preprocessors, PPX extensions and the `ppxlib` library. category: "Advanced Topics" +language: English --- Preprocessors are programs meant to be called at compile time, so that they diff --git a/data/tutorials/language/4ad_01_operators.md b/data/tutorials/language/4ad_01_operators.md index d06d428613..45b85e68f9 100644 --- a/data/tutorials/language/4ad_01_operators.md +++ b/data/tutorials/language/4ad_01_operators.md @@ -4,6 +4,7 @@ title: Operators description: | Binary and prefix operators, how to use and define them, how they are parsed and evaluated. category: "Advanced Topics" +language: English --- ## Goals diff --git a/data/tutorials/language/4ad_02_objects.md b/data/tutorials/language/4ad_02_objects.md index d304c46d4c..c1e6cb3733 100644 --- a/data/tutorials/language/4ad_02_objects.md +++ b/data/tutorials/language/4ad_02_objects.md @@ -4,6 +4,7 @@ title: Objects description: > OCaml is an object-oriented, imperative, functional programming language category: "Advanced Topics" +language: English --- ## Objects and Classes diff --git a/data/tutorials/language/5rt_00_memory_representation.md b/data/tutorials/language/5rt_00_memory_representation.md index 87ab25f644..a4564c2380 100644 --- a/data/tutorials/language/5rt_00_memory_representation.md +++ b/data/tutorials/language/5rt_00_memory_representation.md @@ -4,6 +4,7 @@ title: Memory Representation of Values description: > Memory Representation of Values, excerpt from Real World OCaml category: "Runtime & Compiler" +language: English external_tutorial: tag: "RWO" banner: diff --git a/data/tutorials/language/5rt_01_garbage-collector.md b/data/tutorials/language/5rt_01_garbage-collector.md index 0a4fb6b306..f60a4b9b2a 100644 --- a/data/tutorials/language/5rt_01_garbage-collector.md +++ b/data/tutorials/language/5rt_01_garbage-collector.md @@ -4,6 +4,7 @@ title: Understanding the Garbage Collector description: > Understanding the Garbage Collector, excerpt from Real World OCaml category: "Runtime & Compiler" +language: English external_tutorial: tag: "RWO" banner: diff --git a/data/tutorials/language/5rt_02_compiler_frontend.md b/data/tutorials/language/5rt_02_compiler_frontend.md index a5ad1355d7..0d4e4d36c7 100644 --- a/data/tutorials/language/5rt_02_compiler_frontend.md +++ b/data/tutorials/language/5rt_02_compiler_frontend.md @@ -5,6 +5,7 @@ short_title: Compiler Frontend description: > The Compiler Frontend: Parsing and Type Checking, excerpt from Real World OCaml category: "Runtime & Compiler" +language: English external_tutorial: tag: "RWO" banner: diff --git a/data/tutorials/language/5rt_03_compiler_backend.md b/data/tutorials/language/5rt_03_compiler_backend.md index 1fcfac3ca9..3951fcc002 100644 --- a/data/tutorials/language/5rt_03_compiler_backend.md +++ b/data/tutorials/language/5rt_03_compiler_backend.md @@ -5,6 +5,7 @@ short_title: Compiler Backend description: > The Compiler Backend, excerpt from Real World OCaml category: "Runtime & Compiler" +language: English external_tutorial: tag: "RWO" banner: diff --git a/data/tutorials/platform/0_00_bootstrap_project.md b/data/tutorials/platform/0_00_bootstrap_project.md index 19185ed2a5..d16e2deff9 100644 --- a/data/tutorials/platform/0_00_bootstrap_project.md +++ b/data/tutorials/platform/0_00_bootstrap_project.md @@ -5,6 +5,7 @@ short_title: "Bootstrapping a Project" description: | How to set up a project with Dune category: "Projects" +language: English --- [Dune](https://dune.readthedocs.io/en/stable/overview.html) is recommended for bootstrapping projects. To install `dune`, please see [the OCaml install page](/install). diff --git a/data/tutorials/platform/0_01_managing_deps.md b/data/tutorials/platform/0_01_managing_deps.md index 2e23b0cdd8..ad6a41d466 100644 --- a/data/tutorials/platform/0_01_managing_deps.md +++ b/data/tutorials/platform/0_01_managing_deps.md @@ -4,6 +4,7 @@ title: "Managing Dependencies With opam" description: | How to manage dependencies with opam category: "Projects" +language: English --- ## Installing Existing Dependencies diff --git a/data/tutorials/platform/0_02_install_compiler.md b/data/tutorials/platform/0_02_install_compiler.md index f33cd44fe9..a837c9a135 100644 --- a/data/tutorials/platform/0_02_install_compiler.md +++ b/data/tutorials/platform/0_02_install_compiler.md @@ -5,6 +5,7 @@ short_title: "Installing a Specific Compiler Version" description: | How to install a specific version of OCaml category: "Projects" +language: English --- > **TL;DR** diff --git a/data/tutorials/platform/0_03_run_executables_and_tests.md b/data/tutorials/platform/0_03_run_executables_and_tests.md index 29fc54de73..de943b92b3 100644 --- a/data/tutorials/platform/0_03_run_executables_and_tests.md +++ b/data/tutorials/platform/0_03_run_executables_and_tests.md @@ -5,6 +5,7 @@ short_title: "Running Executables and Tests" description: | How to run executables and tests with Dune category: "Projects" +language: English --- ## Running Executables diff --git a/data/tutorials/platform/0_09_opam_path.md b/data/tutorials/platform/0_09_opam_path.md index 9d1b50d98d..c49e89579d 100644 --- a/data/tutorials/platform/0_09_opam_path.md +++ b/data/tutorials/platform/0_09_opam_path.md @@ -4,6 +4,7 @@ title: "Running Commands in an opam Switch" description: | How to use commands installed in an opam switch category: "Projects" +language: English --- Opam is a package manager for OCaml that facilitates the installation and management of OCaml libraries and tools. When working with opam, it's essential to understand how to run commands within a specific opam switch. In this tutorial, we'll explore three methods: `opam env`, `opam exec`, and `direnv`. diff --git a/data/tutorials/platform/1_07_ocamlformat.md b/data/tutorials/platform/1_07_ocamlformat.md index 03ad41ad01..7c866c3a93 100644 --- a/data/tutorials/platform/1_07_ocamlformat.md +++ b/data/tutorials/platform/1_07_ocamlformat.md @@ -5,6 +5,7 @@ short_title: "Formatting Your Code" description: | How to set up OCamlFormat to automatically format your code category: "Additional Tooling" +language: English --- Automatic formatting with OCamlFormat requires an `.ocamlformat` configuration file at the root of the project. diff --git a/data/tutorials/platform/2_08_odoc.md b/data/tutorials/platform/2_08_odoc.md index a3f5943598..e5a2632c48 100644 --- a/data/tutorials/platform/2_08_odoc.md +++ b/data/tutorials/platform/2_08_odoc.md @@ -5,6 +5,7 @@ short_title: "Generating Documentation" description: | How to use odoc to generate documentation. category: "Additional Tooling" +language: English --- The documentation rendering tool `odoc` generates documentation diff --git a/data/tutorials/platform/3_04_create_libraries.md b/data/tutorials/platform/3_04_create_libraries.md index 7365c5dc19..8082f2f64c 100644 --- a/data/tutorials/platform/3_04_create_libraries.md +++ b/data/tutorials/platform/3_04_create_libraries.md @@ -5,6 +5,7 @@ short_title: "Creating Libraries" description: | How to create libraries with Dune category: "Libraries & Packages" +language: English --- > **TL;DR** diff --git a/data/tutorials/platform/3_05_publish_packages.md b/data/tutorials/platform/3_05_publish_packages.md index de6597d7eb..1a16d0f4ba 100644 --- a/data/tutorials/platform/3_05_publish_packages.md +++ b/data/tutorials/platform/3_05_publish_packages.md @@ -5,6 +5,7 @@ short_title: "Publishing a Package" description: | How to publish a package with Dune category: "Libraries & Packages" +language: English --- > **TL;DR** From a7b4a709f5a4df7cebbfd66efebcb1ff1be41afb Mon Sep 17 00:00:00 2001 From: Masayuki Takeda Date: Sun, 3 Aug 2025 16:59:19 +0800 Subject: [PATCH 03/10] translate 1_00_install_OCaml.md --- .../getting-started/1_00_install_OCaml.ja.md | 169 ++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 data/tutorials/getting-started/1_00_install_OCaml.ja.md diff --git a/data/tutorials/getting-started/1_00_install_OCaml.ja.md b/data/tutorials/getting-started/1_00_install_OCaml.ja.md new file mode 100644 index 0000000000..19c2754471 --- /dev/null +++ b/data/tutorials/getting-started/1_00_install_OCaml.ja.md @@ -0,0 +1,169 @@ +--- +id: installing-ocaml +title: OCamlのインストール +description: | + このページでは、OCamlとOCaml Platformツールのインストール方法を説明します。 | + この手順は、Windows、およびLinuxやmacOSのようなUnix系システムで動作します。 +category: "First Steps" +language: Japanese +--- + +このガイドでは、OCamlの最小限のインストールについて説明します。これには、パッケージマネージャーと[コンパイラ](#installation-on-unix-and-macos)自体のインストールが含まれます。また、ビルドシステム、エディタのサポート、その他いくつかの重要なプラットフォームツールもインストールします。 + +このページでは、Linux、macOS、Windows、および\*BSD向けの最近のOCamlバージョンのインストール手順を説明します。Dockerについては、opamの設定時を除き、Linuxの手順が適用されます。 + +**注**: OCamlとそのツールは、[コマンドラインインターフェース(CLI)またはシェル](https://www.youtube.com/watch?v=0PxTAn4g20U)を介してインストールします。 + +## opamのインストール + +OCamlには公式のパッケージマネージャー [opam](https://opam.ocaml.org/) があり、ユーザーはOCamlのツールやライブラリをダウンロードしてインストールできます。opamはまた、異なるバージョンのOCamlを必要とする様々なプロジェクトを扱うことを容易にします。 + +opamはOCamlコンパイラもインストールします。他の選択肢も存在しますが、OCamlをインストールする最良の方法はopamです。OCamlはほとんどのLinuxディストリビューションでパッケージとして利用可能ですが、しばしばバージョンが古いことがあります。 + +opamをインストールするには、[システムのパッケージマネージャーを使用する](https://opam.ocaml.org/doc/Install.html#Using-your-distribution-39-s-package-system)か、[バイナリディストリビューション](https://opam.ocaml.org/doc/Install.html#Binary-distribution)をダウンロードします。詳細はこれらのリンクにありますが、便宜上、ここではパッケージディストリビューションを使用します。 + +**macOSの場合** + +[Homebrew](https://brew.sh/) を使ってインストールする場合: + +```shell +brew install opam +``` + +または[MacPorts](https://www.macports.org/) を使っている場合: + +```shell +port install opam +``` + +**注**: macOSでopamをインストールするのはかなり簡単ですが、Homebrewのインストールの仕組みが変更されたため、後で問題が発生する可能性があります。新しいMacで使われているM1プロセッサーなどのARM64環境では、実行ファイルが見つからない場合があります。これに対処するのはかなり複雑な手順になる可能性があるため、このインストールガイドの妨げにならないように、[短いARM64修正ドキュメント](/docs/arm64-fix)(英語)を作成しました。 + +**Linuxの場合** + +Linuxでは、システムのパッケージマネージャーを使用してスーパーユーザーとしてopamをインストールすることが望ましいです。opamのサイトで、[すべてのインストール方法の詳細](https://opam.ocaml.org/doc/Install.html)を確認できます。opamのバージョン2.0以上が、サポートされているすべてのLinuxディストリビューションでパッケージ化されています。サポートされていないLinuxディストリビューションを使用している場合は、コンパイル済みのバイナリをダウンロードするか、ソースからopamをビルドしてください。 + +DebianまたはUbuntuにインストールする場合: + +```shell +sudo apt-get install opam +``` + +Arch Linuxにインストールする場合: + +```shell +sudo pacman -S opam +``` + +**注**: Ubuntuでも使用されているDebianのopamパッケージは、OCamlコンパイラを推奨依存関係としています。デフォルトでは、そのような依存関係はインストールされます。OCamlなしでopamのみをインストールしたい場合は、次のようなコマンドを実行する必要があります: + +```shell +sudo apt-get install --no-install-recommends opam +``` + +**Windowsの場合** + +[WinGet](https://github.com/microsoft/winget-cli) を使ってopamをインストールするのが最も簡単です: + +```shell +PS C:\> winget install Git.Git OCaml.opam +``` + +**バイナリディストリビューション** + +opamの最新リリースが必要な場合は、バイナリディストリビューションを介してインストールしてください。UnixやmacOSでは、まず `gcc`、`build-essential`、`curl`、`bubblewrap`、`unzip` といったシステムパッケージをインストールする必要があります。これらのパッケージ名は、お使いのオペレーティングシステムやディストリビューションによって異なる場合があることに注意してください。また、このスクリプトは内部で `sudo` を呼び出すことにも注意してください。 + +次のコマンドは、お使いのシステムに適用される最新バージョンのopamをインストールします: + +```shell +bash -c "sh <(curl -fsSL https://opam.ocaml.org/install.sh)" +``` + +Windowsでは、wingetパッケージはopamの開発者によってメンテナンスされており、[GitHubでリリースされているバイナリ](https://github.com/ocaml/opam/releases)を使用しますが、同等のPowerShellスクリプトを使用してインストールすることもできます: + +```powershell +Invoke-Expression "& { $(Invoke-RestMethod https://opam.ocaml.org/install.ps1) }" +``` + +**上級のWindowsユーザー向け**: CygwinやWSL2に詳しい場合は、[OCaml on Windows](/docs/ocaml-on-windows)(英語)ページに記載されている他のインストール方法があります。 + +## opamの初期化 + +opamをインストールしたら、初期化する必要があります。そのためには、一般ユーザーとして次のコマンドを実行してください。完了するまでに数分かかることがあります。 + +```shell +opam init -y +``` + +**注**: Dockerコンテナ内で `opam init` を実行している場合は、サンドボックス機能を無効にする必要があります。これは `opam init --disable-sandboxing -y` を実行することで行えます。特権付きDockerコンテナを実行しない限り、これが必要です。 + +`opam init` の出力の最後に表示される指示に従って、初期化を完了させてください。通常、これは以下のようになります: + +Unixの場合: +``` +eval $(opam env) +``` + +Windowsのコマンドプロンプトの場合: +``` +for /f "tokens=*" %i in ('opam env') do @%i +``` + +PowerShellの場合: +```powershell +(& opam env) -split '\r?\n' | ForEach-Object { Invoke-Expression $_ } +``` + +opamの初期化には数分かかることがあります。インストールと設定が完了するのを待つ間、[A Tour of OCaml](/docs/tour-of-ocaml)(英語)を読み始めてください。 + +**注**: opamは_スイッチ_と呼ばれるものを管理できます。これは複数のOCamlプロジェクトを切り替える際に重要です。しかし、この「はじめの一歩」シリーズのチュートリアルでは、スイッチは必要ありません。興味があれば、[opamスイッチの紹介](/docs/opam-switch-introduction)(英語)を読むことができます。 + +**インストールで問題がありましたか?** 必ず[最新のリリースノート](https://opam.ocaml.org/blog/opam-2-2-0/)(英語)を読んでください。問題は または で報告できます。 + +## Platformツールのインストール + +OCamlコンパイラとopamパッケージマネージャーのインストールに成功したので、次はOCamlでの完全な開発体験を得るために必要な[OCaml Platformツール](https://ocaml.org/docs/platform)(英語)をいくつかインストールしましょう。 + +- [UTop](https://github.com/ocaml-community/utop): モダンな対話的トップレベル (REPL: Read-Eval-Print Loop) +- [Dune](https://dune.build): 高速で多機能なビルドシステム +- [`ocaml-lsp-server`](https://github.com/ocaml/ocaml-lsp): Language Server Protocolを実装し、VS Code、Vim、EmacsなどでOCamlのエディタサポートを可能にします。 +- [`odoc`](https://github.com/ocaml/odoc): OCamlコードからドキュメントを生成します。 +- [OCamlFormat](https://opam.ocaml.org/packages/ocamlformat/): OCamlコードを自動的にフォーマットします。 + +これらのツールはすべて、単一のコマンドでインストールできます: + +```shell +opam install ocaml-lsp-server odoc ocamlformat utop +``` + +これで準備は万端、コーディングを始める準備ができました。 + +## インストールの確認 + +すべてが正しく動作しているか確認するために、UTopトップレベルを起動してみましょう: + +```shell +$ utop +────────┬─────────────────────────────────────────────────────────────┬───────── + │ Welcome to utop version 2.13.1 (using OCaml version 5.1.0)! │ + └─────────────────────────────────────────────────────────────┘ + +Type #utop_help for help about using utop. + +─( 00:00:00 )─< command 0 >──────────────────────────────────────{ counter: 0 }─ +utop # +``` + +これでOCamlのトップレベルに入りました。OCamlの式を入力し始めることができます。例えば、`#` プロンプトで `21 * 2;;` と入力し、`Enter`キーを押してみてください。次のように表示されます: + +```ocaml +# 21 * 2;; +- : int = 42 +``` + +**おめでとうございます**! OCamlのインストールが完了しました! 🎉 + +UTopを終了するには、`#quit;;`(ここでの`#`はプロンプトではなく、入力する必要があります)と入力するか、`Ctrl+D`を押してください。 + +## コミュニティに参加する + +ぜひ[OCamlコミュニティ](/community)に参加してください。[Discuss](https://discuss.ocaml.org/)や[Discord](https://discord.com/invite/cCYQbqN)で多くのコミュニティメンバーを見つけることができます。これらは何か問題があった場合に助けを求めるのに最適な場所です。 \ No newline at end of file From 98084a6cb90a3c67d572ba95fd56734a9d52f7e3 Mon Sep 17 00:00:00 2001 From: Masayuki Takeda Date: Sun, 3 Aug 2025 17:59:03 +0800 Subject: [PATCH 04/10] choose language based off of lang query parameter --- src/ocamlorg_data/data.ml | 7 ++++++- src/ocamlorg_data/data.mli | 2 +- src/ocamlorg_data/data_intf.ml | 2 +- src/ocamlorg_data/dune | 2 +- src/ocamlorg_web/lib/handler.ml | 10 +++++++++- tool/ood-gen/lib/tutorial.ml | 6 +++++- 6 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/ocamlorg_data/data.ml b/src/ocamlorg_data/data.ml index 90b262dd8c..61a6909e4b 100644 --- a/src/ocamlorg_data/data.ml +++ b/src/ocamlorg_data/data.ml @@ -222,7 +222,12 @@ end module Tutorial = struct include Tutorial - let get_by_slug slug = List.find_opt (fun x -> String.equal slug x.slug) all + let get_by_slug_and_language slug language = + List.find_opt (fun x -> + String.equal slug x.slug && + (match language with + | None -> true + | Some language -> equal_language language x.language)) all let search_documents q = let score_document (doc : search_document) = diff --git a/src/ocamlorg_data/data.mli b/src/ocamlorg_data/data.mli index 5a906c246e..8d3cd01670 100644 --- a/src/ocamlorg_data/data.mli +++ b/src/ocamlorg_data/data.mli @@ -203,7 +203,7 @@ module Tutorial : sig val all : t list val all_search_documents : search_document list - val get_by_slug : string -> t option + val get_by_slug_and_language : string -> language option -> t option val search_documents : string -> search_document list end diff --git a/src/ocamlorg_data/data_intf.ml b/src/ocamlorg_data/data_intf.ml index f7efdada9e..ce6940670b 100644 --- a/src/ocamlorg_data/data_intf.ml +++ b/src/ocamlorg_data/data_intf.ml @@ -475,7 +475,7 @@ module Tutorial = struct slug : string; } - type language = English | Japanese [@@deriving show] + type language = English | Japanese [@@deriving show, equal, compare] type t = { title : string; diff --git a/src/ocamlorg_data/dune b/src/ocamlorg_data/dune index b2afc92643..d6492defdd 100644 --- a/src/ocamlorg_data/dune +++ b/src/ocamlorg_data/dune @@ -4,7 +4,7 @@ (modules data_intf) (libraries ptime) (preprocess - (pps ppx_deriving_yaml ppx_deriving.show))) + (pps ppx_deriving_yaml ppx_deriving.show ppx_compare))) (library (name data) diff --git a/src/ocamlorg_web/lib/handler.ml b/src/ocamlorg_web/lib/handler.ml index 1f1c28c97c..099307033b 100644 --- a/src/ocamlorg_web/lib/handler.ml +++ b/src/ocamlorg_web/lib/handler.ml @@ -764,7 +764,15 @@ let tool_page commit_hash req = let tutorial commit_hash req = let slug = Dream.param req "id" in - let? tutorial = Data.Tutorial.get_by_slug slug in + let language = + Option.bind + (Dream.query req "lang") + (function + | "en" -> Some Data.Tutorial.English + | "ja" -> Some Japanese + | _ -> None) + in + let? tutorial = Data.Tutorial.get_by_slug_and_language slug language in let all_tutorials = Data.Tutorial.all in let tutorials = diff --git a/tool/ood-gen/lib/tutorial.ml b/tool/ood-gen/lib/tutorial.ml index 76d798f533..5c19e0eadd 100644 --- a/tool/ood-gen/lib/tutorial.ml +++ b/tool/ood-gen/lib/tutorial.ml @@ -122,7 +122,11 @@ let decode (fpath, (head, body_md)) = let all () = Utils.map_md_files decode "tutorials/*/*.md" - |> List.sort (fun t1 t2 -> String.compare t1.fpath t2.fpath) + |> List.sort + (Base.Comparable.lexicographic [ + (fun (t1 : t) t2 -> Data_intf.Tutorial.compare_language t1.language t2.language); + (fun t1 t2 -> String.compare t1.fpath t2.fpath) + ]) module TutorialSearch = struct let document_from_section From e21a4ce509b043b9b7541159549dbb66cdd26c44 Mon Sep 17 00:00:00 2001 From: Masayuki Takeda Date: Fri, 8 Aug 2025 18:14:35 +0800 Subject: [PATCH 05/10] serve page in original language if version in requested language isn't found --- src/ocamlorg_data/data.ml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ocamlorg_data/data.ml b/src/ocamlorg_data/data.ml index 61a6909e4b..1c87a5058d 100644 --- a/src/ocamlorg_data/data.ml +++ b/src/ocamlorg_data/data.ml @@ -223,11 +223,15 @@ module Tutorial = struct include Tutorial let get_by_slug_and_language slug language = - List.find_opt (fun x -> + match List.find_opt (fun x -> String.equal slug x.slug && (match language with | None -> true | Some language -> equal_language language x.language)) all + with + | Some x -> Some x + | None -> + List.find_opt (fun x -> String.equal slug x.slug) all let search_documents q = let score_document (doc : search_document) = From 4a1604ae07752d33f6a92eb4f865ecf3d47bd847 Mon Sep 17 00:00:00 2001 From: Masayuki Takeda Date: Thu, 14 Aug 2025 13:35:39 +0800 Subject: [PATCH 06/10] only show one version of each tutorial in sidebar --- src/global/dune | 1 + src/global/url.ml | 14 +- src/ocamlorg_data/data.ml | 6 + src/ocamlorg_data/data.mli | 4 +- .../layouts/learn_layout.eml | 27 ++-- src/ocamlorg_frontend/pages/install.eml | 12 +- src/ocamlorg_frontend/pages/is_ocaml_yet.eml | 2 +- src/ocamlorg_frontend/pages/learn.eml | 32 ++-- src/ocamlorg_frontend/pages/tutorial.eml | 6 +- src/ocamlorg_web/lib/handler.ml | 15 +- src/ocamlorg_web/lib/redirection.ml | 146 +++++++++--------- src/ocamlorg_web/lib/router.ml | 6 +- src/ocamlorg_web/lib/sitemap.ml | 2 +- tool/ood-gen/lib/tutorial.ml | 11 +- 14 files changed, 156 insertions(+), 128 deletions(-) diff --git a/src/global/dune b/src/global/dune index 7317ddc2bd..0477c441a9 100644 --- a/src/global/dune +++ b/src/global/dune @@ -1,3 +1,4 @@ (library (name ocamlorg) + (libraries data_intf) (public_name ocamlorg.global)) diff --git a/src/global/url.ml b/src/global/url.ml index 592b3074fe..fea9629a52 100644 --- a/src/global/url.ml +++ b/src/global/url.ml @@ -85,7 +85,19 @@ let learn_platform = "/docs/tools" let tools = "/tools" let platform = "/platform" let tool_page name = "/tools/" ^ name -let tutorial name = "/docs/" ^ name +let tutorial name (language : Data_intf.Tutorial.language option) = + let language_query_param = + match language with + | None -> "" + | Some language -> + let language = + match language with + | English -> "en" + | Japanese -> "ja" + in + "?lang=" ^ language + in + "/docs/" ^ name ^ language_query_param let tutorial_search = "/docs/search" let getting_started = "/docs/get-started" let installing_ocaml = "/docs/installing-ocaml" diff --git a/src/ocamlorg_data/data.ml b/src/ocamlorg_data/data.ml index 1c87a5058d..740ad39005 100644 --- a/src/ocamlorg_data/data.ml +++ b/src/ocamlorg_data/data.ml @@ -222,6 +222,12 @@ end module Tutorial = struct include Tutorial + let language_of_query_param = + function + | "en" -> Some English + | "ja" -> Some Japanese + | _ -> None + let get_by_slug_and_language slug language = match List.find_opt (fun x -> String.equal slug x.slug && diff --git a/src/ocamlorg_data/data.mli b/src/ocamlorg_data/data.mli index 8d3cd01670..b257563ca2 100644 --- a/src/ocamlorg_data/data.mli +++ b/src/ocamlorg_data/data.mli @@ -199,7 +199,9 @@ module Tool_page : sig end module Tutorial : sig - include module type of Tutorial + include module type of struct include Tutorial end + + val language_of_query_param : string -> language option val all : t list val all_search_documents : search_document list diff --git a/src/ocamlorg_frontend/layouts/learn_layout.eml b/src/ocamlorg_frontend/layouts/learn_layout.eml index c36a052165..38dae43931 100644 --- a/src/ocamlorg_frontend/layouts/learn_layout.eml +++ b/src/ocamlorg_frontend/layouts/learn_layout.eml @@ -34,24 +34,33 @@ let tabs let render_sidebar ~current_tutorial + ~(language : Data.Tutorial.language) ~tutorials ~(section: section) = let render_tutorial_link ~title ~slug = - Sidebar.sidebar_link ~title ~href:(Url.tutorial slug) ~current:(current_tutorial = Some (slug)) + Sidebar.sidebar_link ~title ~href:(Url.tutorial slug (Some language)) ~current:(current_tutorial = Some (slug)) in let render_tutorial (tutorial : Data.Tutorial.t) = render_tutorial_link ~title:tutorial.short_title ~slug:tutorial.slug ~tag:(Option.map (fun (et: Data.Tutorial.external_tutorial) -> et.tag) tutorial.external_tutorial) in + let unique lst = + let rec unique' acc = function + | [] -> List.rev acc + | h::t -> unique' (if List.mem h acc then acc else h :: acc) t + in + unique' [] lst + in let tutorial_sidebar_links_by_category category = - tutorials |> List.filter (fun (x : Data.Tutorial.t) -> x.category = category) |> List.map render_tutorial |> String.concat "\n" + tutorials + |> List.filter (fun (x : Data.Tutorial.t) -> x.category = category) + |> List.map (fun (x : Data.Tutorial.t) -> x.slug) + |> unique + |> List.map (fun slug -> + Data.Tutorial.get_by_slug_and_language slug (Some language) + |> Option.get) + |> List.map render_tutorial + |> String.concat "\n" in let categories = - let unique lst = - let rec unique' acc = function - | [] -> List.rev acc - | h::t -> unique' (if List.mem h acc then acc else h :: acc) t - in - unique' [] lst - in tutorials |> List.map (fun (tutorial : Data.Tutorial.t) -> tutorial.category) |> unique in diff --git a/src/ocamlorg_frontend/pages/install.eml b/src/ocamlorg_frontend/pages/install.eml index 9561b84ba1..64f880a9a8 100644 --- a/src/ocamlorg_frontend/pages/install.eml +++ b/src/ocamlorg_frontend/pages/install.eml @@ -73,7 +73,7 @@ Layout.render

Opam needs to be initialised, which will create a default - ">opam switch. + ">opam switch. An opam switch is an isolated environment for the OCaml compiler and any packages you install.

@@ -113,7 +113,7 @@ Layout.render

Now you are ready to write some OCaml code!

- ">Take A Tour of OCaml <%s! Icons.arrow_small_right "h-6 w-6" %> + ">Take A Tour of OCaml <%s! Icons.arrow_small_right "h-6 w-6" %> @@ -129,7 +129,7 @@ Layout.render OCaml's package manager, opam, supports Windows natively since version 2.2.0 and is the recommended way to install OCaml on Windows. If you are looking for a different installation method, check out the alternative installation instructions - provided in the ">"OCaml on Windows" guide. + provided in the ">"OCaml on Windows" guide.

@@ -145,7 +145,7 @@ Layout.render

Now, we are ready to initialise opam, which will create a default - ">opam switch. + ">opam switch. An opam switch is an isolated environment for the OCaml compiler and any packages you install.

@@ -173,7 +173,7 @@ Layout.render <%s! Copy_to_clipboard.small_code_snippet ~id:"powershell-activate" "(& opam env) -split '\\r?\\n' | ForEach-Object { Invoke-Expression $_ }" %> - Opam initialisation may take several minutes. Start ">A Tour of OCaml while waiting. + Opam initialisation may take several minutes. Start ">A Tour of OCaml while waiting.

@@ -197,7 +197,7 @@ Layout.render

Now you are ready to write some OCaml code!

- ">Take A Tour of OCaml <%s! Icons.arrow_small_right "h-6 w-6" %> + ">Take A Tour of OCaml <%s! Icons.arrow_small_right "h-6 w-6" %> diff --git a/src/ocamlorg_frontend/pages/is_ocaml_yet.eml b/src/ocamlorg_frontend/pages/is_ocaml_yet.eml index 1ba261882a..c100a8646f 100644 --- a/src/ocamlorg_frontend/pages/is_ocaml_yet.eml +++ b/src/ocamlorg_frontend/pages/is_ocaml_yet.eml @@ -1,6 +1,6 @@ let left_sidebar ~tutorials ~current_tutorial =
- <%s! Learn_layout.render_sidebar ~tutorials ~current_tutorial ~section:Learn_layout.Guides %> + <%s! Learn_layout.render_sidebar ~tutorials ~current_tutorial ~language:English ~section:Learn_layout.Guides %>
let render diff --git a/src/ocamlorg_frontend/pages/learn.eml b/src/ocamlorg_frontend/pages/learn.eml index feec9c9966..e6a4be7771 100644 --- a/src/ocamlorg_frontend/pages/learn.eml +++ b/src/ocamlorg_frontend/pages/learn.eml @@ -63,20 +63,20 @@ Learn_layout.single_column_layout ~icon:Learn_components.beginner_section_icon ~title:"GET STARTED" ~heading:"Introduction To OCaml" ~description:"Install OCaml and gain a high-level understanding of the language" ~tutorial_links:[ - {href = Url.tutorial "installing-ocaml"; title = "Installing OCaml"}; - {href = Url.tutorial "tour-of-ocaml"; title = "A Tour of OCaml"}; - {href = Url.tutorial "your-first-program"; title = "Your First OCaml Program"}; - {href = Url.tutorial "opam-switch-introduction"; title = "Introduction to opam Switches"} + {href = Url.tutorial "installing-ocaml" None; title = "Installing OCaml"}; + {href = Url.tutorial "tour-of-ocaml" None; title = "A Tour of OCaml"}; + {href = Url.tutorial "your-first-program" None; title = "Your First OCaml Program"}; + {href = Url.tutorial "opam-switch-introduction" None; title = "Introduction to opam Switches"} ] ~see_more:{href = Url.getting_started; title = "Get Started"} %> <%s! Learn_components.tutorial_block ~icon:Learn_components.beginner_section_icon ~title:"LANGUAGE" ~heading:"The OCaml Language" ~description:"An in-depth explanation of language features and data structures from the Standard Library" ~tutorial_links:[ - {href = Url.tutorial "values-and-functions"; title = "Values and Functions"}; - {href = Url.tutorial "basic-data-types"; title = "Data Types and Pattern Matching"}; - {href = Url.tutorial "lists"; title = "Lists"}; - {href = Url.tutorial "loops-recursion"; title = "Loops and Recursions"}; + {href = Url.tutorial "values-and-functions" None; title = "Values and Functions"}; + {href = Url.tutorial "basic-data-types" None; title = "Data Types and Pattern Matching"}; + {href = Url.tutorial "lists" None; title = "Lists"}; + {href = Url.tutorial "loops-recursion" None; title = "Loops and Recursions"}; ] ~see_more:{href = Url.learn_language; title = "Language Documentation"} %> @@ -132,20 +132,20 @@ Learn_layout.single_column_layout <%s! Learn_components.tutorial_block ~icon:Learn_components.intermediate_section_icon ~title:"GUIDES" ~heading:"Practical-Minded Tutorials and Guides" ~description:"How to solve real-world problems in OCaml" ~tutorial_links:[ - {href = Url.tutorial "formatting-text"; title = "Formatting and Wrapping Text"}; - {href = Url.tutorial "debugging"; title = "Debugging"}; - {href = Url.tutorial "error-handling"; title = "Error Handling"}; - {href = Url.tutorial "profiling"; title = "Profiling"} + {href = Url.tutorial "formatting-text" None; title = "Formatting and Wrapping Text"}; + {href = Url.tutorial "debugging" None; title = "Debugging"}; + {href = Url.tutorial "error-handling" None; title = "Error Handling"}; + {href = Url.tutorial "profiling" None; title = "Profiling"} ] ~see_more:{href = Url.learn_guides; title = "See More Guides"} %> <%s! Learn_components.tutorial_block ~icon:Learn_components.intermediate_section_icon ~title:"PLATFORM" ~heading:"The OCaml Platform" ~description:"Learn to leverage the tooling around OCaml and create your own projects and libraries" ~tutorial_links:[ - {href = Url.tutorial "bootstrapping-a-dune-project"; title = "Bootstrapping a Project"}; - {href = Url.tutorial "managing-dependencies"; title = "Managing Dependencies"}; - {href = Url.tutorial "install-a-specific-ocaml-compiler-version"; title = "Install a Specific Compiler Version"}; - {href = Url.tutorial "set-up-editor"; title = "Configuring Your Editor"} + {href = Url.tutorial "bootstrapping-a-dune-project" None; title = "Bootstrapping a Project"}; + {href = Url.tutorial "managing-dependencies" None; title = "Managing Dependencies"}; + {href = Url.tutorial "install-a-specific-ocaml-compiler-version" None; title = "Install a Specific Compiler Version"}; + {href = Url.tutorial "set-up-editor" None; title = "Configuring Your Editor"} ] ~see_more:{href = Url.learn_platform; title = "Platform Tools Documentation"} %> diff --git a/src/ocamlorg_frontend/pages/tutorial.eml b/src/ocamlorg_frontend/pages/tutorial.eml index 7584407573..c68a474bea 100644 --- a/src/ocamlorg_frontend/pages/tutorial.eml +++ b/src/ocamlorg_frontend/pages/tutorial.eml @@ -13,9 +13,9 @@ let right_sidebar = <%s! Toc.render (List.map tutorial_toc_to_toc tutorial.toc) %> -let left_sidebar ~tutorials ~current_tutorial ~section = +let left_sidebar ~tutorials ~current_tutorial ~language ~section =
- <%s! Learn_layout.render_sidebar ~tutorials ~current_tutorial ~section %> + <%s! Learn_layout.render_sidebar ~tutorials ~current_tutorial ~language ~section %>
let of_tutorial_section (s: Data.Tutorial.section) = @@ -81,7 +81,7 @@ Learn_layout.three_column_layout ~title:(Printf.sprintf "%s · OCaml Documentation" tutorial.short_title) ~description:tutorial.description ~canonical -~left_sidebar_html:(Some(left_sidebar ~current_tutorial:(Some tutorial.slug) ~tutorials ~section:(of_tutorial_section tutorial.section))) +~left_sidebar_html:(Some(left_sidebar ~current_tutorial:(Some tutorial.slug) ~language:tutorial.language ~tutorials ~section:(of_tutorial_section tutorial.section))) ~right_sidebar_html:(Some(right_sidebar tutorial)) ~current:(of_tutorial_section tutorial.section) @@
diff --git a/src/ocamlorg_web/lib/handler.ml b/src/ocamlorg_web/lib/handler.ml index 099307033b..7af0b19138 100644 --- a/src/ocamlorg_web/lib/handler.ml +++ b/src/ocamlorg_web/lib/handler.ml @@ -26,28 +26,28 @@ let learn_get_started req = Data.Tutorial.all |> List.filter (fun (t : Data.Tutorial.t) -> t.section = GetStarted) in - Dream.redirect req (Url.tutorial (List.hd tutorials).slug) + Dream.redirect req (Url.tutorial (List.hd tutorials).slug None) let learn_language req = let tutorials = Data.Tutorial.all |> List.filter (fun (t : Data.Tutorial.t) -> t.section = Language) in - Dream.redirect req (Url.tutorial (List.hd tutorials).slug) + Dream.redirect req (Url.tutorial (List.hd tutorials).slug None) let learn_guides req = let tutorials = Data.Tutorial.all |> List.filter (fun (t : Data.Tutorial.t) -> t.section = Guides) in - Dream.redirect req (Url.tutorial (List.hd tutorials).slug) + Dream.redirect req (Url.tutorial (List.hd tutorials).slug None) let learn_platform req = let tutorials = Data.Tutorial.all |> List.filter (fun (t : Data.Tutorial.t) -> t.section = Platform) in - Dream.redirect req (Url.tutorial (List.hd tutorials).slug) + Dream.redirect req (Url.tutorial (List.hd tutorials).slug None) let community _req = let query = Dream.query _req "e" in @@ -767,10 +767,7 @@ let tutorial commit_hash req = let language = Option.bind (Dream.query req "lang") - (function - | "en" -> Some Data.Tutorial.English - | "ja" -> Some Japanese - | _ -> None) + Data.Tutorial.language_of_query_param in let? tutorial = Data.Tutorial.get_by_slug_and_language slug language in let all_tutorials = Data.Tutorial.all in @@ -802,7 +799,7 @@ let tutorial commit_hash req = Dream.html (Ocamlorg_frontend.tutorial commit_hash ~tutorials - ~canonical:(Url.tutorial tutorial.slug) + ~canonical:(Url.tutorial tutorial.slug None) ~related_exercises ~recommended_next_tutorials ~prerequisite_tutorials tutorial) diff --git a/src/ocamlorg_web/lib/redirection.ml b/src/ocamlorg_web/lib/redirection.ml index 3d75d11448..b3c979b6f3 100644 --- a/src/ocamlorg_web/lib/redirection.ml +++ b/src/ocamlorg_web/lib/redirection.ml @@ -86,59 +86,59 @@ let from_v2 = ("/learn/teaching-ocaml.html", Url.academic_users); ("/learn/tutorials/99problems.html", Url.exercises); ( "/learn/tutorials/a_first_hour_with_ocaml.html", - Url.tutorial "tour-of-ocaml" ); + Url.tutorial "tour-of-ocaml" None ); ( "/learn/tutorials/calling_c_libraries.html", - Url.tutorial "calling-c-libraries" ); + Url.tutorial "calling-c-libraries" None ); ( "/learn/tutorials/calling_fortran_libraries.html", - Url.tutorial "calling-fortran-libraries" ); + Url.tutorial "calling-fortran-libraries" None ); ("/learn/tutorials/camlp5.html", Url.learn); ( "/learn/tutorials/command-line_arguments.ja.html", - Url.tutorial "cli-arguments" ); + Url.tutorial "cli-arguments" None ); ( "/learn/tutorials/command-line_arguments.html", - Url.tutorial "cli-arguments" ); + Url.tutorial "cli-arguments" None ); ( "/learn/tutorials/command-line_arguments.zh.html", - Url.tutorial "cli-arguments" ); + Url.tutorial "cli-arguments" None ); ( "/learn/tutorials/common_error_messages.fr.html", - Url.tutorial "common-errors" ); + Url.tutorial "common-errors" None ); ( "/learn/tutorials/common_error_messages.ja.html", - Url.tutorial "common-errors" ); - ("/learn/tutorials/common_error_messages.html", Url.tutorial "common-errors"); + Url.tutorial "common-errors" None ); + ("/learn/tutorials/common_error_messages.html", Url.tutorial "common-errors" None); ( "/learn/tutorials/common_error_messages.zh.html", - Url.tutorial "common-errors" ); + Url.tutorial "common-errors" None ); ( "/learn/tutorials/comparison_of_standard_containers.ja.html", - Url.tutorial "data-structures-comparison" ); + Url.tutorial "data-structures-comparison" None ); ( "/learn/tutorials/comparison_of_standard_containers.ko.html", - Url.tutorial "data-structures-comparison" ); + Url.tutorial "data-structures-comparison" None ); ( "/learn/tutorials/comparison_of_standard_containers.html", - Url.tutorial "data-structures-comparison" ); + Url.tutorial "data-structures-comparison" None ); ( "/learn/tutorials/comparison_of_standard_containers.zh.html", - Url.tutorial "data-structures-comparison" ); + Url.tutorial "data-structures-comparison" None ); ( "/learn/tutorials/compiling_ocaml_projects.ja.html", - Url.tutorial "using-the-ocaml-compiler-toolchain" ); - ( Url.tutorial "compiling-ocaml-projects", - Url.tutorial "using-the-ocaml-compiler-toolchain" ); + Url.tutorial "using-the-ocaml-compiler-toolchain" None ); + ( Url.tutorial "compiling-ocaml-projects" None, + Url.tutorial "using-the-ocaml-compiler-toolchain" None ); ( "/learn/tutorials/compiling_ocaml_projects.html", - Url.tutorial "using-the-ocaml-compiler-toolchain" ); + Url.tutorial "using-the-ocaml-compiler-toolchain" None ); ( "/learn/tutorials/data_types_and_matching.fr.html", - Url.tutorial "basic-data-types" ); + Url.tutorial "basic-data-types" None ); ( "/learn/tutorials/data_types_and_matching.it.html", - Url.tutorial "basic-data-types" ); + Url.tutorial "basic-data-types" None ); ( "/learn/tutorials/data_types_and_matching.ja.html", - Url.tutorial "basic-data-types" ); + Url.tutorial "basic-data-types" None ); ( "/learn/tutorials/data_types_and_matching.html", - Url.tutorial "basic-data-types" ); + Url.tutorial "basic-data-types" None ); ( "/learn/tutorials/data_types_and_matching.zh.html", - Url.tutorial "basic-data-types" ); - (Url.tutorial "data-types", Url.tutorial "basic-data-types"); - ("/learn/tutorials/debug.html", Url.tutorial "debugging"); - ("/learn/tutorials/error_handling.html", Url.tutorial "error-handling"); + Url.tutorial "basic-data-types" None ); + (Url.tutorial "data-types" None, Url.tutorial "basic-data-types" None); + ("/learn/tutorials/debug.html", Url.tutorial "debugging" None); + ("/learn/tutorials/error_handling.html", Url.tutorial "error-handling" None); ( "/learn/tutorials/file_manipulation.ja.html", - Url.tutorial "file-manipulation" ); - ("/learn/tutorials/file_manipulation.html", Url.tutorial "file-manipulation"); + Url.tutorial "file-manipulation" None ); + ("/learn/tutorials/file_manipulation.html", Url.tutorial "file-manipulation" None); ( "/learn/tutorials/file_manipulation.zh.html", - Url.tutorial "file-manipulation" ); - ("/learn/tutorials/format.fr.html", Url.tutorial "formatting-text"); - ("/learn/tutorials/format.html", Url.tutorial "formatting-text"); + Url.tutorial "file-manipulation" None ); + ("/learn/tutorials/format.fr.html", Url.tutorial "formatting-text" None); + ("/learn/tutorials/format.html", Url.tutorial "formatting-text" None); (* FIXME: uncomment when higher-order-functions is merged ( "/learn/tutorials/functional_programming.fr.html", Url.tutorial "higher-order-functions" ); ( @@ -150,32 +150,32 @@ let from_v2 = "higher-order-functions" ); ( "/learn/tutorials/functional_programming.zh.html", Url.tutorial "higher-order-functions" ); *) - ("/learn/tutorials/functors.html", Url.tutorial "functors"); + ("/learn/tutorials/functors.html", Url.tutorial "functors" None); ( "/learn/tutorials/garbage_collection.ja.html", - Url.tutorial "garbage-collection" ); + Url.tutorial "garbage-collection" None ); ( "/learn/tutorials/garbage_collection.html", - Url.tutorial "garbage-collection" ); + Url.tutorial "garbage-collection" None ); ( "/learn/tutorials/garbage_collection.zh.html", - Url.tutorial "garbage-collection" ); - ("/learn/tutorials/guidelines.html", Url.tutorial "guidelines"); - ("/learn/tutorials/hashtbl.ja.html", Url.tutorial "hash-tables"); - ("/learn/tutorials/hashtbl.html", Url.tutorial "hash-tables"); - ("/learn/tutorials/hashtbl.zh.html", Url.tutorial "hash-tables"); + Url.tutorial "garbage-collection" None ); + ("/learn/tutorials/guidelines.html", Url.tutorial "guidelines" None); + ("/learn/tutorials/hashtbl.ja.html", Url.tutorial "hash-tables" None); + ("/learn/tutorials/hashtbl.html", Url.tutorial "hash-tables" None); + ("/learn/tutorials/hashtbl.zh.html", Url.tutorial "hash-tables" None); ("/learn/tutorials/humor_proof.html", Url.learn); ( "/learn/tutorials/if_statements_loops_and_recursion.fr.html", - Url.tutorial "loops-recursion" ); + Url.tutorial "loops-recursion" None ); ( "/learn/tutorials/if_statements_loops_and_recursion.it.html", - Url.tutorial "loops-recursion" ); + Url.tutorial "loops-recursion" None ); ( "/learn/tutorials/if_statements_loops_and_recursion.ja.html", - Url.tutorial "loops-recursion" ); + Url.tutorial "loops-recursion" None ); ( "/learn/tutorials/if_statements_loops_and_recursion.ko.html", - Url.tutorial "loops-recursion" ); + Url.tutorial "loops-recursion" None ); ( "/learn/tutorials/if_statements_loops_and_recursion.html", - Url.tutorial "loops-recursion" ); + Url.tutorial "loops-recursion" None ); ( "/learn/tutorials/if_statements_loops_and_recursion.zh.html", - Url.tutorial "loops-recursion" ); + Url.tutorial "loops-recursion" None ); ( "/learn/tutorials/mutability-loops-and-imperative", - Url.tutorial "mutability-imperative-control-flow" ); + Url.tutorial "mutability-imperative-control-flow" None ); ("/learn/tutorials/index.de.html", Url.learn); ("/learn/tutorials/index.fr.html", Url.learn); ("/learn/tutorials/index.it.html", Url.learn); @@ -185,34 +185,34 @@ let from_v2 = ("/learn/tutorials", Url.learn); ("/learn/tutorials/index.zh.html", Url.learn); ("/learn/tutorials/introduction_to_gtk.html", Url.learn); - ("/learn/tutorials/labels.ja.html", Url.tutorial "labels"); - ("/learn/tutorials/labels.html", Url.tutorial "labels"); - ("/learn/tutorials/labels.zh.html", Url.tutorial "labels"); - ("/learn/tutorials/lists.html", Url.tutorial "lists"); - ("/learn/tutorials/map.fr.html", Url.tutorial "map"); - ("/learn/tutorials/map.ja.html", Url.tutorial "map"); - ("/learn/tutorials/map.html", Url.tutorial "map"); - ("/learn/tutorials/map.zh.html", Url.tutorial "map"); - ("/learn/tutorials/modules.fr.html", Url.tutorial "modules"); - ("/learn/tutorials/modules.ja.html", Url.tutorial "modules"); - ("/learn/tutorials/modules.ko.html", Url.tutorial "modules"); - ("/learn/tutorials/modules.html", Url.tutorial "modules"); - ("/learn/tutorials/modules.zh.html", Url.tutorial "modules"); - ("/learn/tutorials/objects.ja.html", Url.tutorial "objects"); - ("/learn/tutorials/objects.html", Url.tutorial "objects"); - ("/learn/tutorials/objects.zh.html", Url.tutorial "objects"); + ("/learn/tutorials/labels.ja.html", Url.tutorial "labels" None); + ("/learn/tutorials/labels.html", Url.tutorial "labels" None); + ("/learn/tutorials/labels.zh.html", Url.tutorial "labels" None); + ("/learn/tutorials/lists.html", Url.tutorial "lists" None); + ("/learn/tutorials/map.fr.html", Url.tutorial "map" None); + ("/learn/tutorials/map.ja.html", Url.tutorial "map" None); + ("/learn/tutorials/map.html", Url.tutorial "map" None); + ("/learn/tutorials/map.zh.html", Url.tutorial "map" None); + ("/learn/tutorials/modules.fr.html", Url.tutorial "modules" None); + ("/learn/tutorials/modules.ja.html", Url.tutorial "modules" None); + ("/learn/tutorials/modules.ko.html", Url.tutorial "modules" None); + ("/learn/tutorials/modules.html", Url.tutorial "modules" None); + ("/learn/tutorials/modules.zh.html", Url.tutorial "modules" None); + ("/learn/tutorials/objects.ja.html", Url.tutorial "objects" None); + ("/learn/tutorials/objects.html", Url.tutorial "objects" None); + ("/learn/tutorials/objects.zh.html", Url.tutorial "objects" None); ( "/learn/tutorials/performance_and_profiling.ja.html", - Url.tutorial "profiling" ); - ("/learn/tutorials/performance_and_profiling.html", Url.tutorial "profiling"); + Url.tutorial "profiling" None ); + ("/learn/tutorials/performance_and_profiling.html", Url.tutorial "profiling" None); ( "/learn/tutorials/performance_and_profiling_discussion.html", - Url.tutorial "profiling" ); - ("/learn/tutorials/set.fr.html", Url.tutorial "sets"); - ("/learn/tutorials/set.ja.html", Url.tutorial "sets"); - ("/learn/tutorials/set.html", Url.tutorial "sets"); - ("/learn/tutorials/set.zh.html", Url.tutorial "sets"); - ("/learn/tutorials/streams.html", Url.tutorial "sequences"); - ("/learn/tutorials/up_and_running.html", Url.tutorial "up-and-running"); - (Url.tutorial "first-hour", Url.tutorial "tour-of-ocaml"); + Url.tutorial "profiling" None ); + ("/learn/tutorials/set.fr.html", Url.tutorial "sets" None); + ("/learn/tutorials/set.ja.html", Url.tutorial "sets" None); + ("/learn/tutorials/set.html", Url.tutorial "sets" None); + ("/learn/tutorials/set.zh.html", Url.tutorial "sets" None); + ("/learn/tutorials/streams.html", Url.tutorial "sequences" None); + ("/learn/tutorials/up_and_running.html", Url.tutorial "up-and-running" None); + (Url.tutorial "first-hour" None, Url.tutorial "tour-of-ocaml" None); ("/meetings/index.fr.html", Url.conferences); ("/meetings/index.html", Url.conferences); ("/meetings", Url.conferences); @@ -251,7 +251,7 @@ let from_v2 = ("/docs/platform-principles", Url.tool_page "platform-principles"); ("/docs/platform-users", Url.tool_page "platform-users"); ("/docs/platform-roadmap", Url.tool_page "platform-roadmap"); - ("/docs/configuring-your-editor", Url.tutorial "set-up-editor"); + ("/docs/configuring-your-editor", Url.tutorial "set-up-editor" None); ( "/success-stories/peta-byte-scale-web-crawler", Url.success_story "peta-byte-scale-web-crawling-and-data-processing" ); ] diff --git a/src/ocamlorg_web/lib/router.ml b/src/ocamlorg_web/lib/router.ml index cf0b57da30..48b7086bb3 100644 --- a/src/ocamlorg_web/lib/router.ml +++ b/src/ocamlorg_web/lib/router.ml @@ -70,10 +70,10 @@ let page_routes t = Dream.get Url.tools Handler.tools; Dream.get Url.platform Handler.tools_platform; Dream.get (Url.tool_page ":id") (Handler.tool_page Commit.hash); - Dream.get (Url.tutorial "is-ocaml-web-yet") (Handler.is_ocaml_yet t "web"); - Dream.get (Url.tutorial "is-ocaml-gui-yet") (Handler.is_ocaml_yet t "gui"); + Dream.get (Url.tutorial "is-ocaml-web-yet" None) (Handler.is_ocaml_yet t "web"); + Dream.get (Url.tutorial "is-ocaml-gui-yet" None) (Handler.is_ocaml_yet t "gui"); Dream.get Url.tutorial_search Handler.learn_documents_search; - Dream.get (Url.tutorial ":id") (Handler.tutorial Commit.hash); + Dream.get (Url.tutorial ":id" None) (Handler.tutorial Commit.hash); Dream.get Url.playground Handler.playground; Dream.get Url.logos Handler.logos; ] diff --git a/src/ocamlorg_web/lib/sitemap.ml b/src/ocamlorg_web/lib/sitemap.ml index 0c1e71980c..1e9422672c 100644 --- a/src/ocamlorg_web/lib/sitemap.ml +++ b/src/ocamlorg_web/lib/sitemap.ml @@ -62,7 +62,7 @@ let urlables = Urlable (Release.all, fun r -> to_url @@ Url.release r.version); Urlable (Success_story.all, fun r -> to_url @@ Url.success_story r.slug); Urlable (Tool_page.all, fun r -> to_url @@ Url.tool_page r.slug); - Urlable (Tutorial.all, fun r -> to_url @@ Url.tutorial r.slug); + Urlable (Tutorial.all, fun r -> to_url @@ Url.tutorial r.slug None); Urlable (Conference.all, fun r -> to_url @@ Url.conference r.slug); ] diff --git a/tool/ood-gen/lib/tutorial.ml b/tool/ood-gen/lib/tutorial.ml index 5c19e0eadd..864152470a 100644 --- a/tool/ood-gen/lib/tutorial.ml +++ b/tool/ood-gen/lib/tutorial.ml @@ -122,11 +122,12 @@ let decode (fpath, (head, body_md)) = let all () = Utils.map_md_files decode "tutorials/*/*.md" - |> List.sort - (Base.Comparable.lexicographic [ - (fun (t1 : t) t2 -> Data_intf.Tutorial.compare_language t1.language t2.language); - (fun t1 t2 -> String.compare t1.fpath t2.fpath) - ]) + |> Base.List.sort_and_group + ~compare:(fun t1 t2 -> String.compare t1.slug t2.slug) + |> List.map + (List.sort (fun (t1 : t) t2 -> Data_intf.Tutorial.compare_language t1.language t2.language)) + |> List.sort (fun t1 t2 -> String.compare (List.hd t1).fpath (List.hd t2).fpath) + |> List.concat module TutorialSearch = struct let document_from_section From 99ad6a41362feab1c19624fba0907e948eaa04af Mon Sep 17 00:00:00 2001 From: Masayuki Takeda Date: Thu, 14 Aug 2025 13:40:05 +0800 Subject: [PATCH 07/10] sidebar links shouldn't populate lang query parameters by default --- src/ocamlorg_frontend/layouts/learn_layout.eml | 6 +++--- src/ocamlorg_frontend/pages/is_ocaml_yet.eml | 2 +- src/ocamlorg_frontend/pages/tutorial.eml | 3 ++- src/ocamlorg_web/lib/handler.ml | 1 + 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/ocamlorg_frontend/layouts/learn_layout.eml b/src/ocamlorg_frontend/layouts/learn_layout.eml index 38dae43931..f044aa7435 100644 --- a/src/ocamlorg_frontend/layouts/learn_layout.eml +++ b/src/ocamlorg_frontend/layouts/learn_layout.eml @@ -34,12 +34,12 @@ let tabs let render_sidebar ~current_tutorial - ~(language : Data.Tutorial.language) + ~(language : Data.Tutorial.language option) ~tutorials ~(section: section) = let render_tutorial_link ~title ~slug = - Sidebar.sidebar_link ~title ~href:(Url.tutorial slug (Some language)) ~current:(current_tutorial = Some (slug)) + Sidebar.sidebar_link ~title ~href:(Url.tutorial slug language) ~current:(current_tutorial = Some (slug)) in let render_tutorial (tutorial : Data.Tutorial.t) = render_tutorial_link ~title:tutorial.short_title ~slug:tutorial.slug ~tag:(Option.map (fun (et: Data.Tutorial.external_tutorial) -> et.tag) tutorial.external_tutorial) in let unique lst = @@ -55,7 +55,7 @@ let render_sidebar |> List.map (fun (x : Data.Tutorial.t) -> x.slug) |> unique |> List.map (fun slug -> - Data.Tutorial.get_by_slug_and_language slug (Some language) + Data.Tutorial.get_by_slug_and_language slug language |> Option.get) |> List.map render_tutorial |> String.concat "\n" diff --git a/src/ocamlorg_frontend/pages/is_ocaml_yet.eml b/src/ocamlorg_frontend/pages/is_ocaml_yet.eml index c100a8646f..945f0224bd 100644 --- a/src/ocamlorg_frontend/pages/is_ocaml_yet.eml +++ b/src/ocamlorg_frontend/pages/is_ocaml_yet.eml @@ -1,6 +1,6 @@ let left_sidebar ~tutorials ~current_tutorial =
- <%s! Learn_layout.render_sidebar ~tutorials ~current_tutorial ~language:English ~section:Learn_layout.Guides %> + <%s! Learn_layout.render_sidebar ~tutorials ~current_tutorial ~language:None ~section:Learn_layout.Guides %>
let render diff --git a/src/ocamlorg_frontend/pages/tutorial.eml b/src/ocamlorg_frontend/pages/tutorial.eml index c68a474bea..565dd88f85 100644 --- a/src/ocamlorg_frontend/pages/tutorial.eml +++ b/src/ocamlorg_frontend/pages/tutorial.eml @@ -61,6 +61,7 @@ commit_hash ~recommended_next_tutorials ~prerequisite_tutorials ~canonical +~language = let href, description = match tutorial.external_tutorial with | None -> @@ -81,7 +82,7 @@ Learn_layout.three_column_layout ~title:(Printf.sprintf "%s · OCaml Documentation" tutorial.short_title) ~description:tutorial.description ~canonical -~left_sidebar_html:(Some(left_sidebar ~current_tutorial:(Some tutorial.slug) ~language:tutorial.language ~tutorials ~section:(of_tutorial_section tutorial.section))) +~left_sidebar_html:(Some(left_sidebar ~current_tutorial:(Some tutorial.slug) ~language ~tutorials ~section:(of_tutorial_section tutorial.section))) ~right_sidebar_html:(Some(right_sidebar tutorial)) ~current:(of_tutorial_section tutorial.section) @@
diff --git a/src/ocamlorg_web/lib/handler.ml b/src/ocamlorg_web/lib/handler.ml index 7af0b19138..b7fcc41da8 100644 --- a/src/ocamlorg_web/lib/handler.ml +++ b/src/ocamlorg_web/lib/handler.ml @@ -801,6 +801,7 @@ let tutorial commit_hash req = (Ocamlorg_frontend.tutorial commit_hash ~tutorials ~canonical:(Url.tutorial tutorial.slug None) ~related_exercises ~recommended_next_tutorials ~prerequisite_tutorials + ~language tutorial) let exercises req = From 2c1cf6189a84abc4dde1fbc3b34e4e4cfe361f9d Mon Sep 17 00:00:00 2001 From: Masayuki Takeda Date: Sun, 4 Jan 2026 14:22:36 +0800 Subject: [PATCH 08/10] add language frontmatter --- data/tutorials/language/1ms_02_first_class_modules.md | 1 + dune-project | 1 + ocamlorg.opam | 1 + 3 files changed, 3 insertions(+) diff --git a/data/tutorials/language/1ms_02_first_class_modules.md b/data/tutorials/language/1ms_02_first_class_modules.md index 3b95bbb7ad..4ce041b071 100644 --- a/data/tutorials/language/1ms_02_first_class_modules.md +++ b/data/tutorials/language/1ms_02_first_class_modules.md @@ -4,6 +4,7 @@ title: First-Class Modules description: > First-Class Modules permit the use of modules as ordinary values. category: "Module System" +language: English --- ## Introduction diff --git a/dune-project b/dune-project index 8038b124ec..93f47bdb5a 100644 --- a/dune-project +++ b/dune-project @@ -36,6 +36,7 @@ (ocaml (>= 5.2.0)) ppx_deriving + ppx_compare cohttp cohttp-lwt-unix bos diff --git a/ocamlorg.opam b/ocamlorg.opam index 03e1f4bb0c..11d7a6183e 100644 --- a/ocamlorg.opam +++ b/ocamlorg.opam @@ -21,6 +21,7 @@ depends: [ "dune" {>= "3.15"} "ocaml" {>= "5.2.0"} "ppx_deriving" + "ppx_compare" "cohttp" "cohttp-lwt-unix" "bos" From a583c407adf849ea18a384d0ea912b45832af9ce Mon Sep 17 00:00:00 2001 From: Masayuki Takeda Date: Sun, 8 Feb 2026 11:23:30 +0800 Subject: [PATCH 09/10] add language selector --- src/ocamlorg_data/data.ml | 10 +++++ src/ocamlorg_data/data.mli | 2 + src/ocamlorg_frontend/components/icons.eml | 5 +++ src/ocamlorg_frontend/pages/tutorial.eml | 49 +++++++++++++++++++++- src/ocamlorg_web/lib/handler.ml | 3 +- 5 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/ocamlorg_data/data.ml b/src/ocamlorg_data/data.ml index 740ad39005..cb1eeedf3a 100644 --- a/src/ocamlorg_data/data.ml +++ b/src/ocamlorg_data/data.ml @@ -228,6 +228,16 @@ module Tutorial = struct | "ja" -> Some Japanese | _ -> None + let language_to_string = function + | English -> "English" + | Japanese -> "日本語" + + let get_available_languages slug = + all + |> List.filter (fun x -> String.equal slug x.slug) + |> List.map (fun x -> x.language) + |> List.sort_uniq compare_language + let get_by_slug_and_language slug language = match List.find_opt (fun x -> String.equal slug x.slug && diff --git a/src/ocamlorg_data/data.mli b/src/ocamlorg_data/data.mli index b257563ca2..cfeec5ac20 100644 --- a/src/ocamlorg_data/data.mli +++ b/src/ocamlorg_data/data.mli @@ -202,6 +202,8 @@ module Tutorial : sig include module type of struct include Tutorial end val language_of_query_param : string -> language option + val language_to_string : language -> string + val get_available_languages : string -> language list val all : t list val all_search_documents : search_document list diff --git a/src/ocamlorg_frontend/components/icons.eml b/src/ocamlorg_frontend/components/icons.eml index f1fd3db026..06a3db5f6a 100644 --- a/src/ocamlorg_frontend/components/icons.eml +++ b/src/ocamlorg_frontend/components/icons.eml @@ -194,6 +194,11 @@ let globe_europe_africa class_ = +let language class_ = + + let hamburger_menu class_ =
+ +
+
+ <% available_languages |> List.iter (fun lang -> %> + <% if Data.Tutorial.equal_language lang tutorial.language then ( %> + + <%s Data.Tutorial.language_to_string lang %> + + <% ) else ( %> + + <%s Data.Tutorial.language_to_string lang %> + + <% ); %> + <% ); %> +
+
+
+ else + "" + let right_sidebar (tutorial : Data.Tutorial.t) +~(available_languages : Data.Tutorial.language list) = + <%s! language_switcher tutorial ~available_languages %> <%s! Toc.render (List.map tutorial_toc_to_toc tutorial.toc) %> let left_sidebar ~tutorials ~current_tutorial ~language ~section = @@ -61,6 +107,7 @@ commit_hash ~recommended_next_tutorials ~prerequisite_tutorials ~canonical +~available_languages ~language = let href, description = match tutorial.external_tutorial with @@ -83,7 +130,7 @@ Learn_layout.three_column_layout ~description:tutorial.description ~canonical ~left_sidebar_html:(Some(left_sidebar ~current_tutorial:(Some tutorial.slug) ~language ~tutorials ~section:(of_tutorial_section tutorial.section))) -~right_sidebar_html:(Some(right_sidebar tutorial)) +~right_sidebar_html:(Some(right_sidebar tutorial ~available_languages)) ~current:(of_tutorial_section tutorial.section) @@
<%s! render_external_tutorial_banner tutorial.external_tutorial %> diff --git a/src/ocamlorg_web/lib/handler.ml b/src/ocamlorg_web/lib/handler.ml index 45dd89a656..9b05b4c777 100644 --- a/src/ocamlorg_web/lib/handler.ml +++ b/src/ocamlorg_web/lib/handler.ml @@ -770,6 +770,7 @@ let tutorial commit_hash req = (Dream.query req "lang") Data.Tutorial.language_of_query_param in + let available_languages = Data.Tutorial.get_available_languages slug in let? tutorial = Data.Tutorial.get_by_slug_and_language slug language in let all_tutorials = Data.Tutorial.all in @@ -802,7 +803,7 @@ let tutorial commit_hash req = (Ocamlorg_frontend.tutorial commit_hash ~tutorials ~canonical:(Url.tutorial tutorial.slug None) ~related_exercises ~recommended_next_tutorials ~prerequisite_tutorials - ~language + ~available_languages ~language tutorial) let exercises req = From 789783b585a728fb16bdb985db9154e9d84a7c4f Mon Sep 17 00:00:00 2001 From: Masayuki Takeda Date: Sun, 8 Feb 2026 12:07:30 +0800 Subject: [PATCH 10/10] add draft translations of remaining getting-started pages --- .../getting-started/1_00_install_OCaml.ja.md | 16 +- .../1_01_a_tour_of_ocaml.ja.md | 839 ++++++++++++++++++ .../1_02_your_first_ocaml_program.ja.md | 413 +++++++++ .../getting-started/2_00_editor_setup.ja.md | 254 ++++++ .../getting-started/2_01_toplevel.ja.md | 82 ++ .../getting-started/2_02_opam_switch.ja.md | 86 ++ .../3_01_ocaml_on_windows.ja.md | 145 +++ .../getting-started/3_02_arm_fix.ja.md | 68 ++ .../3_03_ocaml_playground.ja.md | 114 +++ 9 files changed, 2009 insertions(+), 8 deletions(-) create mode 100644 data/tutorials/getting-started/1_01_a_tour_of_ocaml.ja.md create mode 100644 data/tutorials/getting-started/1_02_your_first_ocaml_program.ja.md create mode 100644 data/tutorials/getting-started/2_00_editor_setup.ja.md create mode 100644 data/tutorials/getting-started/2_01_toplevel.ja.md create mode 100644 data/tutorials/getting-started/2_02_opam_switch.ja.md create mode 100644 data/tutorials/getting-started/3_01_ocaml_on_windows.ja.md create mode 100644 data/tutorials/getting-started/3_02_arm_fix.ja.md create mode 100644 data/tutorials/getting-started/3_03_ocaml_playground.ja.md diff --git a/data/tutorials/getting-started/1_00_install_OCaml.ja.md b/data/tutorials/getting-started/1_00_install_OCaml.ja.md index 19c2754471..453ebca08f 100644 --- a/data/tutorials/getting-started/1_00_install_OCaml.ja.md +++ b/data/tutorials/getting-started/1_00_install_OCaml.ja.md @@ -2,7 +2,7 @@ id: installing-ocaml title: OCamlのインストール description: | - このページでは、OCamlとOCaml Platformツールのインストール方法を説明します。 | + このページでは、OCamlとOCamlプラットフォームツールのインストール方法を説明します。 | この手順は、Windows、およびLinuxやmacOSのようなUnix系システムで動作します。 category: "First Steps" language: Japanese @@ -36,7 +36,7 @@ brew install opam port install opam ``` -**注**: macOSでopamをインストールするのはかなり簡単ですが、Homebrewのインストールの仕組みが変更されたため、後で問題が発生する可能性があります。新しいMacで使われているM1プロセッサーなどのARM64環境では、実行ファイルが見つからない場合があります。これに対処するのはかなり複雑な手順になる可能性があるため、このインストールガイドの妨げにならないように、[短いARM64修正ドキュメント](/docs/arm64-fix)(英語)を作成しました。 +**注**: macOSでopamをインストールするのはかなり簡単ですが、Homebrewのインストールの仕組みが変更されたため、後で問題が発生する可能性があります。新しいMacで使われているM1プロセッサーなどのARM64環境では、実行可能ファイルが見つからない場合があります。これに対処するのはかなり複雑な手順になる可能性があるため、このインストールガイドの妨げにならないように、[Apple M1でのHomebrewエラーの修正](/docs/arm64-fix?lang=ja)ドキュメントを作成しました。 **Linuxの場合** @@ -84,7 +84,7 @@ Windowsでは、wingetパッケージはopamの開発者によってメンテナ Invoke-Expression "& { $(Invoke-RestMethod https://opam.ocaml.org/install.ps1) }" ``` -**上級のWindowsユーザー向け**: CygwinやWSL2に詳しい場合は、[OCaml on Windows](/docs/ocaml-on-windows)(英語)ページに記載されている他のインストール方法があります。 +**上級のWindowsユーザー向け**: CygwinやWSL2に詳しい場合は、[Windows上のOCaml](/docs/ocaml-on-windows?lang=ja)ページに記載されている他のインストール方法があります。 ## opamの初期化 @@ -113,19 +113,19 @@ PowerShellの場合: (& opam env) -split '\r?\n' | ForEach-Object { Invoke-Expression $_ } ``` -opamの初期化には数分かかることがあります。インストールと設定が完了するのを待つ間、[A Tour of OCaml](/docs/tour-of-ocaml)(英語)を読み始めてください。 +opamの初期化には数分かかることがあります。インストールと設定が完了するのを待つ間、[OCamlツアー](/docs/tour-of-ocaml?lang=ja)を読み始めてください。 -**注**: opamは_スイッチ_と呼ばれるものを管理できます。これは複数のOCamlプロジェクトを切り替える際に重要です。しかし、この「はじめの一歩」シリーズのチュートリアルでは、スイッチは必要ありません。興味があれば、[opamスイッチの紹介](/docs/opam-switch-introduction)(英語)を読むことができます。 +**注**: opamは_スイッチ_と呼ばれるものを管理できます。これは複数のOCamlプロジェクトを切り替える際に重要です。しかし、この「はじめの一歩」シリーズのチュートリアルでは、スイッチは必要ありません。興味があれば、[opamスイッチ入門](/docs/opam-switch-introduction?lang=ja)を読むことができます。 **インストールで問題がありましたか?** 必ず[最新のリリースノート](https://opam.ocaml.org/blog/opam-2-2-0/)(英語)を読んでください。問題は または で報告できます。 -## Platformツールのインストール +## プラットフォームツールのインストール -OCamlコンパイラとopamパッケージマネージャーのインストールに成功したので、次はOCamlでの完全な開発体験を得るために必要な[OCaml Platformツール](https://ocaml.org/docs/platform)(英語)をいくつかインストールしましょう。 +OCamlコンパイラとopamパッケージマネージャーのインストールに成功したので、次はOCamlでの完全な開発体験を得るために必要な[OCamlプラットフォームツール](https://ocaml.org/docs/platform)(英語)をいくつかインストールしましょう。 - [UTop](https://github.com/ocaml-community/utop): モダンな対話的トップレベル (REPL: Read-Eval-Print Loop) - [Dune](https://dune.build): 高速で多機能なビルドシステム -- [`ocaml-lsp-server`](https://github.com/ocaml/ocaml-lsp): Language Server Protocolを実装し、VS Code、Vim、EmacsなどでOCamlのエディタサポートを可能にします。 +- [`ocaml-lsp-server`](https://github.com/ocaml/ocaml-lsp): Language Server Protocolを実装し、VS Code、Vim、EmacsなどでOCamlのエディタのサポートを可能にします。 - [`odoc`](https://github.com/ocaml/odoc): OCamlコードからドキュメントを生成します。 - [OCamlFormat](https://opam.ocaml.org/packages/ocamlformat/): OCamlコードを自動的にフォーマットします。 diff --git a/data/tutorials/getting-started/1_01_a_tour_of_ocaml.ja.md b/data/tutorials/getting-started/1_01_a_tour_of_ocaml.ja.md new file mode 100644 index 0000000000..86212e0406 --- /dev/null +++ b/data/tutorials/getting-started/1_01_a_tour_of_ocaml.ja.md @@ -0,0 +1,839 @@ +--- +id: tour-of-ocaml +title: OCamlツアー +description: > + OCaml観光バスに乗りましょう。この初心者向けチュートリアルでは、OCamlの素晴らしい機能を巡ります。最もよく使われる言語機能を見ていきます。 +category: "First Steps" +language: Japanese +recommended_next_tutorials: + - "values-and-functions" + - "basic-data-types" + - "loops-recursion" + - "lists" +--- + +このチュートリアルでは、OCamlの基本的な機能(値、式、リスト、関数、パターンマッチングなど)を紹介します。 + +OCamlや関数型プログラミングの知識は必要ありませんが、基本的なソフトウェア開発の知識があることを前提としています。 +[OCamlのインストール](/docs/installing-ocaml?lang=ja)ページの説明に従って、OCamlをインストールし、環境を設定しておいてください。 + +提供する例を実行し、それらを使って実験することをお勧めします。OCamlでのコーディングの感覚をつかむためです。 +これには、UTop(ユニバーサルトップレベル)を使用できます。 + +UTopでは、OCamlのフレーズ(式や値の定義など)を読み込み、評価し、結果を画面に表示することで、ユーザーがOCamlと対話できます。`utop`コマンドを使用してUTopを起動します。`Ctrl+D`を押して終了します。詳細については、[OCamlトップレベル入門](/docs/toplevel-introduction?lang=ja)を参照してください。 + +このツアーの例にはコメントが含まれているものがあります。OCamlのコメントは`(*`で始まり`*)`で終わり、ネストすることができます。OCamlはコメントを無視するため、空白が許可されている場所であればどこでも使用できます。以下のコードをUTopに入力する際、コメントは省略しても構いません。以下にいくつかの例を示します: + +```ocaml +(* Here is a comment *) +(* Outside of the nested comment is still a comment. (* Here is a nested comment *) Outside of the nested comment again. *) +# 50 + (* A comment in between parts of an expression *) 50;; +- : int = 100 +``` + + + +## 式と定義 + +簡単な式から始めましょう: + +```ocaml +# 50 * 50;; +- : int = 2500 +``` + +OCamlでは、すべてに値があり、すべての値には型があります。上の例は、「`50 * 50`は`int`(整数)型の式で、`2500`に評価される」ことを示しています。名前のない式であるため、名前の代わりに文字`-`が表示されます。 + +末尾のダブルセミコロン`;;`は、与えられたフレーズを評価して結果を表示するようにトップレベルに指示します。 + +以下は、他のプリミティブ値と型の例です: + +```ocaml +# 6.28;; +- : float = 6.28 + +# "This is really disco!";; +- : string = "This is really disco!" + +# 'a';; (* Note the single quotes *) +- : char = 'a' + +# true;; +- : bool = true +``` + +OCamlには_型推論_があります。プログラマーからの指示をほとんど必要とせずに、式の型を自動的に決定します。_リスト_には[専用のチュートリアル](/docs/lists)があります。当面は、以下の2つの式はどちらもリストです。前者は整数を含み、後者は文字列を含みます。 + +```ocaml +# let u = [1; 2; 3; 4];; +val u : int list = [1; 2; 3; 4] + +# ["this"; "is"; "mambo"];; +- : string list = ["this"; "is"; "mambo"] +``` + +リストの型`int list`と`string list`は、要素の型から推論されています。リストは空`[]`(「ニル」と読みます)にすることができます。最初のリストには、以下で詳しく説明する`let … = …`構文を使用して名前が付けられていることに注意してください。リストに対する最も基本的な操作は、既存のリストの先頭に新しい要素を追加することです。これは「cons」演算子、ダブルコロン演算子`::`を使って行います。 + +```ocaml +# 9 :: u;; +- : int list = [9; 1; 2; 3; 4] +``` + +OCamlでは、`if … then … else …`は文ではなく式です。 + +```ocaml +# 2 * if "hello" = "world" then 3 else 5;; +- : int = 10 +``` + +`if`で始まり`5`で終わるソースは、2で乗算される単一の整数式として解析されます。OCamlには2つの異なるテスト構文は必要ありません。[三項条件演算子](https://en.wikipedia.org/wiki/Ternary_conditional_operator)と`if … then … else …`は同じものです。また、ここでは括弧が不要であることにも注意してください。OCamlではよくあることです。 + +`let`キーワードを使用して、値に名前を付けることができます。これは値を名前に_バインド_するといいます。例えば: + +```ocaml +# let x = 50;; +val x : int = 50 + +# x * x;; +- : int = 2500 +``` + +`let x = 50;;`を入力すると、OCamlは`val x : int = 50`と応答します。これは、`x`が値`50`にバインドされた識別子であることを意味します。したがって、`x * x;;`は`50 * 50;;`と同じ結果に評価されます。 + +OCamlのバインディングは_不変_です。名前に割り当てられた値は変更されません。`x`はしばしば変数と呼ばれますが、実際にはそうではありません。実際には定数です。簡略化した表現ですが、OCamlではすべての変数が不変です。更新可能な値に名前を付けることは可能です。OCamlでは、これを_参照_と呼び、[ミュータブルな状態の操作](/docs/tour-of-ocaml#working-with-mutable-state)セクションで説明します。 + +OCamlにはオーバーロードがありません。そのため、レキシカルスコープ内では、名前は単一の値を持ち、その定義にのみ依存します。 + +名前にハイフンを使用しないでください。代わりにアンダースコアを使用してください。例えば、`x_plus_y`は使えますが、`x-plus-y`は使えません。 + +バインディングには、エディタやツールがバインディングに関連するものとして扱う特別なコメント(「docstring」と呼ばれることもあります)を付けることができます。これはコメントの開始に2番目の`*`を追加することで示されます。例えば: + +```ocaml +(** Feet in a mile *) +let feet = 5280;; +val feet : int = 5280 +``` + +これについては、[`odoc` for Authors: Special Comments](https://ocaml.github.io/odoc/odoc/odoc_for_authors.html#special_comments)(英語)でさらに詳しく説明されています。 + +`let … = … in …`構文を使用して、式内でローカルに名前を定義できます: + +```ocaml +# let y = 50 in y * y;; +- : int = 2500 + +# y;; +Error: Unbound value y +``` + +この例では、名前`y`を定義し、値`50`にバインドしています。次に、式`y * y`で使用され、値`2500`が得られます。`y`は`in`キーワードに続く式内でのみ定義されていることに注意してください。 + +`let … = … in …`は式であるため、別の式内で使用して、それぞれ独自の名前を持つ複数の値を定義できます: + +```ocaml +# let a = 1 in + let b = 2 in + a + b;; +- : int = 3 +``` + +これは2つの名前を定義しています:値`1`の`a`と値`2`の`b`。次に、式`a + b`で使用され、値`3`が得られます。 + +OCamlでは、等号には2つの意味があります。定義と等価性テストに使用されます。 + +```ocaml +# let dummy = "hi" = "hello";; +val dummy : bool = false +``` + +これは「`dummy`を文字列`"hi"`と`"hello"`の構造的等価性テストの結果として定義する」と解釈されます。OCamlにはダブルイコール演算子`==`もあり、これは物理的等価性を意味しますが、このチュートリアルでは使用しません。演算子`<>`は`=`の否定であり、`!=`は`==`の否定です。 + +## 関数 + +OCamlでは、すべてが値なので、関数も値です。関数は`let`キーワードを使用して定義されます: + +```ocaml +# let square x = x * x;; +val square : int -> int = + +# square 50;; +- : int = 2500 +``` + +この例では、単一のパラメータ`x`を持つ`square`という名前の関数を定義しています。その_関数本体_は式`x * x`です。OCamlには「return」キーワードはありません。 + +`square`を`50`に適用すると、`x * x`が`50 * 50`に評価され、`2500`になります。 + +REPLは`square`の型が`int -> int`であることを示しています。これは、引数(入力)として`int`を取り、結果(出力)として`int`を返す関数であることを意味します。関数の値は表示できないため、代わりに``が表示されます。 + +```ocaml +# String.ends_with;; +- : suffix:string -> string -> bool = + +# String.ends_with ~suffix:"less" "stateless";; +- : bool = true +``` + +`String.ends_with`のような一部の関数にはラベル付きパラメータがあります。ラベルは、関数が同じ型の複数のパラメータを持つ場合に役立ちます。引数に名前を付けることで、その目的を推測できます。上の例では、`~suffix:"less"`は`"less"`がラベル付き引数`suffix`として渡されることを示しています。ラベル付き引数については、[ラベル付き引数](/docs/labels)(英語)チュートリアルで詳しく説明されています。 + +### 無名関数 + +_無名_関数には名前がなく、`fun`キーワードで定義されます: + +```ocaml +# fun x -> x * x;; +- : int -> int = +``` + +無名関数を書いて、すぐに値に適用できます: + +```ocaml +# (fun x -> x * x) 50;; +- : int = 2500 +``` + +### 複数のパラメータを持つ関数と部分適用 + +関数はスペースで区切られた複数のパラメータを持つことができます。 + +```ocaml +# let cat a b = a ^ " " ^ b;; +val cat : string -> string -> string = +``` + +関数`cat`は2つの`string`パラメータ`a`と`b`を持ち、`string`型の値を返します。 + +```ocaml +# cat "ha" "ha";; +- : string = "ha ha" +``` + +関数は期待するすべての引数を指定して呼び出す必要はありません。`b`を渡さずに`a`のみを`cat`に渡すことが可能です。 + +```ocaml +# let cat_hi = cat "hi";; +val cat_hi : string -> string = +``` + +これは、`cat`の定義における`b`に対応する、単一の文字列を期待する関数を返します。これを_部分適用_と呼びます。上の例では、`cat`が`"hi"`に部分適用されました。 + +`cat`の部分適用から得られた関数`cat_hi`は、次のように動作します: + +```ocaml +# cat_hi "friend";; +- : string = "hi friend" +``` + +### 型パラメータと高階関数 + +関数はパラメータとして関数を受け取ることができます。これを_高階_関数と呼びます。高階関数のよく知られた例は`List.map`です。以下はその使い方です: + +```ocaml +# List.map;; +- : ('a -> 'b) -> 'a list -> 'b list = + +# List.map (fun x -> x * x);; +- : int list -> int list = + +# List.map (fun x -> x * x) [0; 1; 2; 3; 4; 5];; +- : int list = [0; 1; 4; 9; 16; 25] +``` + +この関数の名前が`List.`で始まるのは、リストに作用する定義済みライブラリの一部だからです。この件については後ほど詳しく説明します。関数`List.map`には2つのパラメータがあります:2番目はリストで、1番目はリストの要素に適用できる関数です(要素が何であっても)。`List.map`は、引数として提供された関数を入力リストの各要素に適用して形成されたリストを返します。 + +関数`List.map`はあらゆる種類のリストに適用できます。ここでは整数のリストが与えられていますが、浮動小数点数、文字列、その他何でもリストにすることができます。これを_多相性_と呼びます。`List.map`関数は多相的であり、2つの暗黙の_型変数_`'a`と`'b`(「アルファ」と「ベータ」と読みます)を持ちます。どちらも何にでもなれますが、`List.map`に渡される関数に関して: + +1. 入力リストの要素は、その入力と同じ型を持ちます。 +2. 出力リストの要素は、その出力と同じ型を持ちます。 + +### 副作用と`unit`型 + +オペレーティングシステムレベルの入出力操作は関数を使用して行われます。以下はそれぞれの例です: + +```ocaml +# read_line;; +- : unit -> string = + +# read_line ();; +caramba +- : string = "caramba" + +# print_endline;; +- : string -> unit = + +# print_endline "¿Cuándo se come aquí?";; +¿Cuándo se come aquí? +- : unit = () +``` + +関数`read_line`は標準入力から文字を読み取り、行末(EOL)に達したときに文字列として返します。関数`print_endline`は文字列を標準出力に出力し、その後にEOLを追加します。 + +関数`read_line`は処理に必要なデータを必要とせず、関数`print_endline`は返す意味のあるデータがありません。このデータの欠如を示すのが`unit`型の役割で、これらの関数のシグネチャに表示されます。`unit`型には`()`と書かれ「ユニット」と読む、単一の値があります。データが渡されたり返されたりしないが、処理の開始や処理の終了を示すために何らかのトークンを渡す必要がある場合のプレースホルダーとして使用されます。 + +入出力は、関数の実行時に発生するが関数型には現れないものの例です。これを_副作用_と呼び、I/Oに限りません。`unit`型は副作用の存在を示すためによく使用されますが、常にそうとは限りません。 + +### 再帰関数 + +再帰関数は、自身の本体内で自分自身を呼び出します。このような関数は、単なる`let`ではなく`let rec … = …`を使用して宣言する必要があります。再帰はOCamlで反復計算を実行する唯一の手段ではありません。`for`や`while`などのループが利用可能ですが、これらはミュータブルなデータと組み合わせて命令型OCamlを書く場合に使用するものです。それ以外の場合は、再帰関数を使用することが推奨されます。 + +以下は、2つの境界値の間の連続した整数のリストを作成する関数の例です。 + +```ocaml +# let rec range lo hi = + if lo > hi then + [] + else + lo :: range (lo + 1) hi;; +val range : int -> int -> int list = + +# range 2 5;; +- : int list = [2; 3; 4; 5] +``` + +型`int -> int -> int list`が示すように、関数`range`は2つの整数を引数として取り、整数のリストを結果として返します。最初の`int`パラメータ`lo`は範囲の下限、2番目の`int`パラメータ`hi`は上限です。`lo > hi`の場合、空の範囲が返されます。これは`if … then … else`式の最初の分岐です。そうでない場合、`lo`の値は`range`自身を呼び出して作成されたリストの先頭に追加されます。これが再帰です。先頭への追加はOCamlのcons演算子`::`を使用して行われます。既存のリストの先頭に要素を追加して新しいリストを構築します。各呼び出しで進行します。`lo`がリストの先頭に追加されたばかりなので、`range`は`lo + 1`で呼び出されます。これは次のように視覚化できます(これはOCaml構文ではありません): + +``` + range 2 5 +=> 2 :: range 3 5 +=> 2 :: 3 :: range 4 5 +=> 2 :: 3 :: 4 :: range 5 5 +=> 2 :: 3 :: 4 :: 5 :: range 6 5 +=> 2 :: 3 :: 4 :: 5 :: [] +=> [2; 3; 4; 5] +``` + +各`=>`記号は、最後のものを除いて再帰ステップの計算に対応します。OCamlは内部的にリストを最後から2番目の式のように処理しますが、最後の式のように表示します。これは単なる整形出力です。最後の2つのステップ間では計算は行われません。 + +## データと型 + +### 型変換と型推論 + +OCamlには`float`型の浮動小数点値があります。浮動小数点数を加算するには、`+`の代わりに`+.`を使用する必要があります: + +```ocaml +# 2.0 +. 2.0;; +- : float = 4. +``` + +OCamlでは、`+.`は浮動小数点数同士の加算であり、`+`は整数同士の加算です。 + +多くのプログラミング言語では、値はある型から別の型に自動的に変換されます。これには_暗黙の型変換_や_昇格_が含まれます。例えば、そのような言語で`1 + 2.5`と書くと、最初の引数(整数)が浮動小数点数に昇格され、結果も浮動小数点数になります。 + +OCamlは値をある型から別の型に暗黙的に変換することはありません。浮動小数点数と整数の加算を行うことはできません。以下の両方の例はエラーを発生させます: + +```ocaml +# 1 + 2.5;; +Error: This expression has type float but an expression was expected of type + int + +# 1 +. 2.5;; +Error: This expression has type int but an expression was expected of type + float + Hint: Did you mean `1.'? +``` + +最初の例では、`+`は整数に使用するためのものなので、`2.5`の浮動小数点数には使用できません。2番目の例では、`+.`は浮動小数点数に使用するためのものなので、`1`の整数には使用できません。 + +OCamlでは、`float_of_int`関数を使用して整数を浮動小数点数に明示的に変換する必要があります: + +```ocaml +# float_of_int 1 +. 2.5;; +- : float = 3.5 +``` + +OCamlが明示的な変換を必要とする理由はいくつかあります。最も重要なのは、型を自動的に推論できるようにすることです。OCamlの_型推論_アルゴリズムは各式の型を計算し、他の言語と比較して非常に少ないアノテーションしか必要としません。議論の余地はありますが、より明示的にすることで失う時間よりも多くの時間を節約できます。 + +### リスト + +リストはOCamlで最も一般的なデータ型かもしれません。同じ型の値の順序付きコレクションです。以下にいくつかの例を示します。 + +```ocaml +# [];; +- : 'a list = [] + +# [1; 2; 3];; +- : int list = [1; 2; 3] + +# [false; false; true];; +- : bool list = [false; false; true] + +# [[1; 2]; [3]; [4; 5; 6]];; +- : int list list = [[1; 2]; [3]; [4; 5; 6]] +``` + +上の例は次のように読みます: + +1. 空のリスト(nil) +1. 数値1、2、3を含むリスト +1. ブール値`false`、`false`、`true`を含むリスト。重複は許可されます。 +1. リストのリスト + +リストは、空`[]`であるか、要素`x`が別のリスト`u`の先頭に追加されたもの`x :: u`(ダブルコロン演算子は「cons」と読みます)のいずれかとして定義されます。 + +```ocaml +# 1 :: [2; 3; 4];; +- : int list = [1; 2; 3; 4] +``` + +OCamlでは、_パターンマッチング_は関数を除くあらゆる種類のデータを検査する手段を提供します。このセクションではリストに対して紹介し、次のセクションで他のデータ型に一般化します。以下は、整数のリストの合計を計算する再帰関数をパターンマッチングを使って定義する方法です: + +```ocaml +# let rec sum u = + match u with + | [] -> 0 + | x :: v -> x + sum v;; +val sum : int list -> int = + +# sum [1; 4; 3; 2; 5];; +- : int = 15 +``` +2番目のマッチング式の`x :: v`パターンは、リストを先頭`x`と残り`v`に分解するために使用されます。ここで_先頭_はリストの最初の要素、_残り_はリストの残りの部分です。 + +#### リストに対する多相関数 + +以下は、リストの長さを計算する再帰関数の書き方です: + +```ocaml +# let rec length u = + match u with + | [] -> 0 + | _ :: v -> 1 + length v;; (* _ doesn't define a name; it can't be used in the body *) +val length : 'a list -> int = + +# length [1; 2; 3; 4];; +- : int = 4 + +# length ["cow"; "sheep"; "cat"];; +- : int = 3 + +# length [[]];; +- : int = 1 +``` + +この関数は整数のリストだけでなく、あらゆる種類のリストに対して動作します。これは多相関数です。その型は入力が`'a list`型であることを示しており、`'a`は任意の型を表す型変数です。空のリストパターン`[]`は任意の要素型を持つことができます。`_ :: v`パターンも同様で、リストの先頭の値は`_`パターンが示すように検査されないため、無関係です。両方のパターンは同じ型でなければならないため、型推論アルゴリズムは`'a list -> int`型を推論します。 + +#### 高階関数の定義 + +関数を別の関数の引数として渡すことが可能です。他の関数をパラメータとして持つ関数を_高階_関数と呼びます。これは先ほど関数`List.map`を使って説明しました。以下は、リストに対するパターンマッチングを使って`map`を書く方法です。 + +```ocaml +# let square x = x * x;; +val square : int -> int + +# let rec map f u = + match u with + | [] -> [] + | x :: u -> f x :: map f u;; +val map : ('a -> 'b) -> 'a list -> 'b list = + +# map square [1; 2; 3; 4;];; +- : int list = [1; 4; 9; 16] +``` + +### パターンマッチング(続き) + +パターンマッチングはリストに限定されません。関数を除くあらゆる種類のデータをパターンマッチングで検査できます。パターンは検査される値と比較される式です。`if … then … else …`を使っても実行できますが、パターンマッチングの方が便利です。以下は、[モジュールと標準ライブラリ](#モジュールと標準ライブラリ)セクションで詳しく説明する`option`データ型を使った例です。 + +```ocaml +# #show option;; +type 'a option = None | Some of 'a + +# let f opt = match opt with + | None -> None + | Some None -> None + | Some (Some x) -> Some x;; +val f : 'a option option-> 'a option = +``` + +検査される値は`option`型の`opt`です。パターンと上から下に比較されます。`opt`が`None`オプションの場合、最初のパターンとマッチします。`opt`が`Some None`オプションの場合、2番目のパターンとマッチします。`opt`が値を持つ二重ラップされたオプションの場合、3番目のパターンとマッチします。パターンは`let`と同じように名前を導入できます。3番目のパターンでは、`x`は二重ラップされたオプション内のデータを指します。 + +パターンマッチングについては、[基本データ型](/docs/basic-data-types)(英語)チュートリアルやデータ型ごとのチュートリアルで詳しく説明されています。 + +この別の例では、`if … then … else …`とパターンマッチングを使って同じ比較を行っています。 + +```ocaml +# let g x = + if x = "foo" then 1 + else if x = "bar" then 2 + else if x = "baz" then 3 + else if x = "qux" then 4 + else 0;; +val g : string -> int = + +# let g' x = match x with + | "foo" -> 1 + | "bar" -> 2 + | "baz" -> 3 + | "qux" -> 4 + | _ -> 0;; +val g' : string -> int = +``` + +アンダースコア記号はキャッチオールパターンで、何にでもマッチします。 + +OCamlはパターンマッチングがすべてのケースをカバーしていない場合に警告を出すことに注意してください: + +```ocaml +# fun i -> match i with 0 -> 1;; +Line 1, characters 9-28: +Warning 8 [partial-match]: this pattern-matching is not exhaustive. +Here is an example of a case that is not matched: +1 +- : int -> int = +``` + +### ペアとタプル + +タプルは任意の型の要素の固定長コレクションです。ペアは2つの要素を持つタプルです。以下は3タプルとペアです: + +```ocaml +# (1, "one", 'K');; +- : int * string * char = (1, "one", 'K') + +# ([], false);; +- : 'a list * bool = ([], false) +``` + +タプルの要素へのアクセスはパターンマッチングを使って行います。例えば、定義済みの関数`snd`はペアの2番目の要素を返します: + +```ocaml +# let snd p = + match p with + | (_, y) -> y;; +val snd : 'a * 'b -> 'b = + +# snd (42, "apple");; +- : string = "apple" +``` + +注:関数`snd`はOCaml標準ライブラリで定義済みです。 + +タプルの型はコンポーネントの型の間に`*`を使って書きます。 + +### バリアント型 + +パターンマッチングが`switch`文を一般化するように、バリアント型は列挙型と共用体型を一般化します。 + +以下は、列挙データ型として機能するバリアント型の定義です: + +```ocaml +# type primary_colour = Red | Green | Blue;; +type primary_colour = Red | Green | Blue + +# [Red; Blue; Red];; +- : primary_colour list = [Red; Blue; Red] +``` + +以下は、共用体型として機能するバリアント型の定義です: + +```ocaml +# type http_response = + | Data of string + | Error_code of int;; +type http_response = Data of string | Error_code of int + +# Data " + + + + Dummy + + + Dummy Page + +";; + +- : http_response = +Data + "\n\n \n \n Dummy\n \n \n Dummy Page\n \n" + +# Error_code 404;; +- : http_response = Error_code 404 +``` + +以下はその中間的なものです: + +```ocaml +# type page_range = + | All + | Current + | Range of int * int;; +type page_range = All | Current | Range of int * int +``` + +前述の定義で、大文字で始まる識別子は_コンストラクタ_と呼ばれます。これらはバリアント値の作成を可能にします。これはオブジェクト指向プログラミングとは無関係です。 + +このセクションの最初の文で示唆されているように、バリアントはパターンマッチングと組み合わせて使います。以下にいくつかの例を示します: + +```ocaml +# let colour_to_rgb colour = + match colour with + | Red -> (0xff, 0, 0) + | Green -> (0, 0xff, 0) + | Blue -> (0, 0, 0xff);; +val colour_to_rgb : primary_colour -> int * int * int = + +# let http_status_code response = + match response with + | Data _ -> 200 + | Error_code code -> code;; +val http_status_code : http_response -> int = + +# let is_printable page_count cur range = + match range with + | All -> true + | Current -> 0 <= cur && cur < page_count + | Range (lo, hi) -> 0 <= lo && lo <= hi && hi < page_count;; +val is_printable : int -> int -> page_range -> bool = +``` + +関数と同様に、バリアントも自身の定義内で自分自身を参照する場合、再帰的になれます。定義済みの型`list`はそのようなバリアントの例です: + +```ocaml +# #show list;; +type 'a list = [] | (::) of 'a * 'a list +``` + +先ほど示したように、`sum`、`length`、`map`関数はリストバリアント型に対するパターンマッチングの例を提供しています。 + +### レコード + +タプルと同様に、レコードも複数の型の要素をまとめます。ただし、各要素には名前が付けられます。バリアント型と同様に、レコード型は使用する前に定義する必要があります。以下は、レコード型、値、コンポーネントへのアクセス、同じレコードに対するパターンマッチングの例です。 + +```ocaml +# type person = { + first_name : string; + surname : string; + age : int + };; +type person = { first_name : string; surname : string; age : int; } + +# let gerard = { + first_name = "Gérard"; + surname = "Huet"; + age = 76 + };; +val gerard : person = {first_name = "Gérard"; surname = "Huet"; age = 76} +``` + +`gerard`を定義する際、型を宣言する必要はありません。型チェッカーは、名前と型が一致する3つのフィールドを持つレコードを検索します。レコード間に型の関係はないことに注意してください。フィールドを追加して別のレコードを拡張するレコード型を宣言することはできません。レコード型の検索は完全一致が見つかった場合に成功し、それ以外の場合は失敗します。 + +```ocaml +# let s = gerard.surname;; +val s : string = "Huet" + +# let is_teenager person = + match person with + | { age = x; _ } -> 13 <= x && x <= 19;; +val is_teenager : person -> bool = + +# is_teenager gerard;; +- : bool = false +``` + +ここで、パターン`{ age = x; _ }`は`int`型の`age`フィールドを持つ最も最近宣言されたレコード型で型付けされます。`int`型は式`13 <= x && x <= 19`から推論されます。関数`is_teenager`は、見つかったレコード型(ここでは`person`)でのみ動作します。 + +## エラーの処理 + +### 例外 + +計算が中断されると、例外がスローされます。例えば: + +```ocaml +# 10 / 0;; +Exception: Division_by_zero. +``` + +例外は`raise`関数を使って発生させます。 + +```ocaml +# let id_42 n = if n <> 42 then raise (Failure "Sorry") else n;; +val id_42 : int -> int = + +# id_42 42;; +- : int = 42 + +# id_42 0;; +Exception: Failure "Sorry". +``` + +例外は関数の型には現れないことに注意してください。 + +例外は`try … with …`構文を使ってキャッチします: + +```ocaml +# try id_42 0 with Failure _ -> 0;; +- : int = 0 +``` + +標準ライブラリにはいくつかの定義済み例外があります。例外を定義することも可能です。 + +### `result`型の使用 + +OCamlでエラーを処理するもう一つの方法は、`result`型の値を返すことです。 +これは正しい結果またはエラーのいずれかを表すことができます。以下はその定義です: + +```ocaml +# #show result;; +type ('a, 'b) result = Ok of 'a | Error of 'b +``` + +したがって、次のように書くことができます: + +```ocaml +# let id_42_res n = if n <> 42 then Error "Sorry" else Ok n;; +val id_42_res : int -> (int, string) result = + +# id_42_res 42;; +- : (int, string) result = Ok 42 + +# id_42_res 0;; +- : (int, string) result = Error "Sorry" + +# match id_42_res 0 with + | Ok n -> n + | Error _ -> 0;; +- : int = 0 +``` + +## ミュータブルな状態の操作 + +OCamlは命令型プログラミングをサポートしています。通常、`let … = …`構文は変数を定義するのではなく、定数を定義します。ただし、OCamlにはミュータブルな変数が存在します。それらは_参照_と呼ばれます。以下は整数への参照を作成する方法です: + +```ocaml +# let r = ref 0;; +val r : int ref = {contents = 0} +``` + +初期化されていない参照やnull参照を作成することは構文的に不可能です。参照`r`は整数ゼロで初期化されています。参照の内容にアクセスするには、`!`逆参照演算子を使用します。 + +```ocaml +# !r;; +- : int = 0 +``` + +`!r`と`r`は異なる型を持つことに注意してください:それぞれ`int`と`int ref`です。整数と浮動小数点数の乗算ができないのと同様に、整数を更新したり参照を乗算したりすることはできません。 + +`r`の内容を更新しましょう。ここで`:=`は代入演算子です。 + +```ocaml +# r := 42;; +- : unit = () +``` + +参照の内容を変更することは副作用であるため、`()`が返されます。 + +```ocaml +# !r;; +- : int = 42 +``` + +`;`演算子を使って式を順番に実行します。`a; b`と書くと、`a`を実行し、完了したら`b`を実行して、`b`の値のみを返します。 + +```ocaml +# let text = ref "hello ";; +val text : string ref = {contents = "hello "} + +# print_string !text; text := "world!"; print_endline !text;; +hello world! +- : unit = () +``` + +2行目で発生する副作用は以下の通りです: + +1. 参照`text`の内容を標準出力に表示 +1. 参照`text`の内容を更新 +1. 参照`text`の内容を標準出力に表示 + +この動作は命令型言語と同じです。ただし、`;`は関数として定義されていませんが、`unit -> unit -> unit`型の関数であるかのように動作します。 + +## モジュールと標準ライブラリ + +OCamlでソースコードを整理するには_モジュール_を使用します。モジュールは定義のグループです。_標準ライブラリ_はすべてのOCamlプログラムで利用可能なモジュールのセットです。以下は、標準ライブラリの`Option`モジュールに含まれる定義を一覧表示する方法です: + +```ocaml +# #show Option;; +module Option : + sig + type 'a t = 'a option = None | Some of 'a + val none : 'a t + val some : 'a -> 'a t + val value : 'a t -> default:'a -> 'a + val get : 'a t -> 'a + val bind : 'a t -> ('a -> 'b t) -> 'b t + val join : 'a t t -> 'a t + val map : ('a -> 'b) -> 'a t -> 'b t + val fold : none:'a -> some:('b -> 'a) -> 'b t -> 'a + val iter : ('a -> unit) -> 'a t -> unit + val is_none : 'a t -> bool + val is_some : 'a t -> bool + val equal : ('a -> 'a -> bool) -> 'a t -> 'a t -> bool + val compare : ('a -> 'a -> int) -> 'a t -> 'a t -> int + val to_result : none:'e -> 'a t -> ('a, 'e) result + val to_list : 'a t -> 'a list + val to_seq : 'a t -> 'a Seq.t + end +``` + +モジュールが提供する定義は、モジュール名をプレフィックスとして名前に追加することで参照されます。 + +```ocaml +# Option.map;; +- : ('a -> 'b) -> 'a option -> 'b option = + +# Option.map (fun x -> x * x);; +- : int option -> int option = + +# Option.map (fun x -> x * x) None;; +- : int option = None + +# Option.map (fun x -> x * x) (Some 8);; +- : int option = Some 64 +``` + +ここでは、関数`Option.map`の使い方をいくつかのステップで説明しています。 + +1. 型を表示します。2つのパラメータがあります:`'a -> 'b`型の関数と`'a option`。 +1. 部分適用を使って、`fun x -> x * x`のみを渡します。結果の関数の型を確認します。 +1. `None`で適用します。 +1. `Some 8`で適用します。 + +提供されたオプション値に実際の値が含まれている場合(つまり`Some`何かの場合)、提供された関数を適用し、その結果をオプションにラップして返します。提供されたオプション値に何も含まれていない場合(つまり`None`の場合)、結果にも何も含まれません(つまり`None`も返されます)。 + +このセクションで先ほど使用した`List.map`関数も、`List`モジュールの一部です。 + +```ocaml +# List.map;; +- : ('a -> 'b) -> 'a list -> 'b list = + +# List.map (fun x -> x * x);; +- : int list -> int list = +``` + +これはOCamlモジュールシステムの最初の特徴を示しています。名前の衝突を防ぐことで関心の分離を可能にします。異なる型を持つ2つの関数が、異なるモジュールによって提供されている場合、同じ名前を持つことができます。 + +モジュールは効率的な分割コンパイルも可能にします。これは次のチュートリアルで説明されます。 + +## まとめ + + + +このチュートリアルでは、OCamlを対話的に使用しました。次のチュートリアル「[はじめてのOCamlプログラム](/docs/your-first-program?lang=ja)」では、OCamlファイルの書き方、コンパイル方法、プロジェクトの始め方を説明します。 diff --git a/data/tutorials/getting-started/1_02_your_first_ocaml_program.ja.md b/data/tutorials/getting-started/1_02_your_first_ocaml_program.ja.md new file mode 100644 index 0000000000..f196f91a3f --- /dev/null +++ b/data/tutorials/getting-started/1_02_your_first_ocaml_program.ja.md @@ -0,0 +1,413 @@ +--- +id: your-first-program +title: はじめてのOCamlプログラム +description: > + はじめてのOCamlプログラムを書く方法を学びましょう。 +category: "First Steps" +language: Japanese +recommended_next_tutorials: + - "values-and-functions" + - "basic-data-types" + - "loops-recursion" + - "lists" +--- + +このチュートリアルを完了するには、[OCamlをインストール](/install)している必要があります。オプションとして、[エディタの設定](/docs/set-up-editor?lang=ja)をお勧めします。 + +OCamlのソースコードを含むファイルを作成し、実行可能なバイナリを生成するためにコンパイルします。ただし、これはOCamlのコンパイル、プロジェクトのモジュール化、依存関係管理に関する詳細なチュートリアルではありません。それらのトピックの概要を示すだけです。目標は、詳細に迷わないように全体像を描くことです。つまり、深さ優先学習ではなく幅優先学習を行います。 + +前のチュートリアルでは、ほとんどのコマンドをUTopで入力しました。このチュートリアルでは、コマンドの大部分をターミナルで入力します。ドル記号`$`で始まるコード例はターミナルで入力することを意図しており、ハッシュ記号`#`で始まる行はUTopで入力することを意図しています。 + +このチュートリアルを完了すると、OCamlのビルドシステムであるDuneを使用してOCamlプロジェクトを作成、コンパイル、実行できるようになります。ファイルの操作、モジュール内のプライベートな定義の作成、opamパッケージのインストールと使用方法を学びます。 + + + +**注意**: このチュートリアルのファイルは[Gitリポジトリ](https://github.com/ocaml-web/ocamlorg-docs-your-first-program)として公開されています。 + +## opamスイッチ内での作業 + +OCamlをインストールした際に、グローバルopamスイッチが自動的に作成されました。このチュートリアルは、このグローバルopamスイッチ内で作業しながら完了できます。 + +複数のOCamlプロジェクトを同時に作業する場合は、追加のopamスイッチを作成する必要があります。その方法については、[opamスイッチ入門](/docs/opam-switch-introduction?lang=ja)を参照してください。 + +## OCamlプログラムのコンパイル + +デフォルトで、OCamlには2つのコンパイラが付属しています:ソースをネイティブバイナリに変換するコンパイラと、ソースをバイトコード形式に変換するコンパイラです。OCamlには、そのバイトコード形式のインタプリタも付属しています。このチュートリアルでは、ネイティブコンパイラを使用してOCamlプログラムを作成する方法を示します。 + + + +まず、Duneを使用して従来の「Hello, World!」プロジェクトをセットアップします。バージョン3.12以降がインストールされていることを確認してください。以下のコマンドで`hello`という名前のプロジェクトを作成します: + +```shell +$ opam exec -- dune init proj hello +Success: initialized project component named hello +``` + +**注1**: 現在のターミナルセッションの開始時に`eval $(opam env)`を実行した場合、または`opam init`実行時の質問に「はい」と答えた場合、`dune`コマンドの先頭にある`opam exec --`を省略できます。 + +**注2**: このチュートリアル全体を通して、Duneによって生成される出力は、インストールされているDuneのバージョンによってわずかに異なる場合があります。このチュートリアルではDune 3.12の出力を表示しています。最新バージョンのDuneを入手したい場合は、ターミナルで`opam update; opam upgrade dune`を実行してください。 + +プロジェクトは`hello`という名前のディレクトリに保存され、以下の内容を含みます: + +```shell +hello +├── bin +│ ├── dune +│ └── main.ml +├── _build +│ └── log +├── dune-project +├── hello.opam +├── lib +│ └── dune +└── test + ├── dune + └── hello.ml +``` + +Unixではコンパイルされたバイナリが含まれますが、`lib`と`bin`ディレクトリにはそれぞれライブラリとプログラムのソースコードファイルが含まれます。これは多くのOCamlプロジェクト(Duneで作成されたものを含む)で使用される慣例です。すべてのビルド成果物とソースのコピーは`_build`ディレクトリに保存されます。`_build`ディレクトリ内のものを編集しないでください。手動の編集は後続のビルド時に上書きされます。 + +OCamlソースファイルには`.ml`拡張子があり、「Meta Language」の略です。Meta Language(ML)はOCamlの先祖です。これが「OCaml」の「ml」の由来でもあります。以下は`bin/main.ml`ファイルの内容です: + +```ocaml +let () = print_endline "Hello, World!" +``` + +プロジェクト全体のメタデータは`dune-project`ファイルにあります。プロジェクト名、依存関係、グローバルセットアップに関する情報が含まれています。 + +ビルドが必要なソースファイルを含む各ディレクトリには、ビルド方法を記述した`dune`ファイルが必要です。 + +これでプロジェクトをビルドします: + +```shell +opam exec -- dune build +``` + +これで作成された実行可能ファイルを起動します: + +```shell +$ opam exec -- dune exec hello +Hello, World! +``` + +`bin/main.ml`ファイルを直接編集するとどうなるか見てみましょう。エディタでファイルを開き、`World`をあなたの名前に置き換えてください。前と同じように`dune build`でプロジェクトを再コンパイルし、`dune exec hello`で再度起動してください。 + +これで完了です!はじめてのOCamlプログラムを書きました。 + +このチュートリアルの残りでは、OCamlのツールを説明するために、このプロジェクトにさらに変更を加えます。 + +## ウォッチモード + +本題に入る前に、Duneのウォッチモードを使用して、継続的にコンパイルし、オプションでプログラムを再起動することが一般的です。これにより、言語サーバーがプロジェクトに関する最新のデータを持つことが保証され、エディタのサポートが最高の状態になります。ウォッチモードを使用するには、`-w`フラグを追加するだけです: + +```shell +opam exec -- dune build -w +opam exec -- dune exec hello -w +``` + +## なぜmain関数がないのか? + +`bin/main.ml`の名前はプロジェクトへのアプリケーションのエントリポイントを含むことを示唆していますが、専用の`main`関数は含まれておらず、実行可能ファイルを生成するためにプロジェクトにその名前のファイルが必要という要件もありません。コンパイルされたOCamlファイルは、そのファイルがトップレベルに1行ずつ入力されたかのように動作します。言い換えれば、実行可能なOCamlファイルのエントリポイントは最初の行です。 + +ソースファイルでは、トップレベルのようにダブルセミコロンは必要ありません。文は上から下へ順番に処理され、それぞれが持つ副作用がトリガーされます。定義は環境に追加されます。名前のない式から得られる値は無視されます。これらすべての副作用は同じ順序で発生します。これがOCamlのmainです。 + +ただし、すべての副作用をトリガーする値を1つ選び出し、意図されたメインエントリポイントとしてマークするのが一般的な慣行です。OCamlでは、`let () =`がその役割を果たし、名前を作成せずに右辺の式(すべての副作用を含む)を評価します。 + +## モジュールと標準ライブラリ(続き) + +[OCamlツアー](/docs/tour-of-ocaml?lang=ja)でモジュールについて述べたことをまとめましょう: + +- モジュールは名前付き値のコレクションです。 +- 異なるモジュールの同名の定義は衝突しません。 +- 標準ライブラリは複数のモジュールのコレクションです。 + +モジュールはプロジェクトの整理に役立ちます。関心事を分離したモジュールに分けることができます。これは次のセクションで概説します。自分でモジュールを作成する前に、標準ライブラリのモジュールから定義を使用する方法を示します。`bin/main.ml`ファイルの内容を次のように変更してください: + +```ocaml +let () = Printf.printf "%s\n" "Hello, World!" +``` + +これは`print_endline`関数を標準ライブラリの`Printf`モジュールの`printf`関数に置き換えます。この変更版をビルドして実行すると、以前と同じ出力が得られるはずです。`dune exec hello`で試してみてください。 + +## すべてのファイルがモジュールを定義する + +各OCamlファイルは、コンパイルされるとモジュールを定義します。これがOCamlの分割コンパイルの仕組みです。十分に独立した各関心事はモジュールに分離する必要があります。外部モジュールへの参照は依存関係を作成します。モジュール間の循環依存は許可されていません。 + +モジュールを作成するために、以下の内容を含む`lib/en.ml`という新しいファイルを作成しましょう: + +```ocaml +let v = "Hello, world!" +``` + +以下は`bin/main.ml`ファイルの新しいバージョンです: + +```ocaml +let () = Printf.printf "%s\n" Hello.En.v +``` + +結果のプロジェクトを実行してみましょう: + +```shell +$ opam exec -- dune exec hello +Hello, world! +``` + +ファイル`lib/en.ml`は`En`という名前のモジュールを作成し、`v`という名前の文字列値を定義します。Duneは`En`を`Hello`と呼ばれる別のモジュールにラップします。この名前は`lib/dune`ファイルの`name hello`スタンザによって定義されています。文字列の定義は`bin/main.ml`ファイルから`Hello.En.v`としてアクセスされます。 + +Duneは、プロジェクトが公開するモジュールに対話的にアクセスするためにUTopを起動できます。方法は以下の通りです: + +```shell +opam exec -- dune utop +``` + +次に、`utop`トップレベル内で`Hello.En`モジュールを調べることができます: + +```ocaml +# #show Hello.En;; +module Hello : sig val v : string end +``` + +次のセクションに進む前に、`Ctrl-D`で`utop`を終了するか、`#quit;;`と入力してください。 + +**注意**: `lib`ディレクトリに`hello.ml`という名前のファイルを追加すると、Duneはそれを`Hello`モジュール全体とみなし、`En`にアクセスできなくなります。モジュール`En`を可視にしたい場合は、`hello.ml`ファイルに以下を追加する必要があります: + +```ocaml +module En = En +``` + + +## モジュールインターフェースの定義 + +UTopの`#show`コマンドは[API](https://en.wikipedia.org/wiki/API#Libraries_and_frameworks)(ソフトウェアライブラリの意味)を表示します:モジュールが提供する定義のリストです。OCamlでは、これを_モジュールインターフェース_と呼びます。`.ml`ファイルはモジュールを定義します。同様に、`.mli`ファイルはモジュールインターフェースを定義します。モジュールインターフェースファイルは、対応するモジュールファイルと同じベース名を持つ必要があります。例えば、`en.mli`はモジュール`en.ml`のモジュールインターフェースです。以下の内容で`lib/en.mli`ファイルを作成してください: + +```ocaml +val v : string +``` + +モジュールシグネチャの宣言リスト(`#show`出力の`sig`と`end`の間にあるもの)のみがインターフェースファイル`lib/en.mli`に書かれていることに注意してください。これについては、[モジュール](/docs/modules)(英語)の専用チュートリアルでさらに詳しく説明されています。 + +モジュールインターフェースは_プライベート_な定義を作成するためにも使用されます。モジュールの定義が対応するモジュールインターフェースに記載されていない場合、その定義はプライベートです。モジュールインターフェースファイルが存在しない場合、すべてがパブリックです。 + +お好みのエディタで`lib/en.ml`ファイルを修正してください。既存の内容を以下に置き換えてください: + +```ocaml +let hello = "Hello" +let v = hello ^ ", world!" +``` + +`bin/main.ml`ファイルも次のように編集してください: + +```ocaml +let () = Printf.printf "%s\n" Hello.En.hello +``` + +これをコンパイルしようとすると失敗します。 + +```shell +$ opam exec -- dune build +File "hello/bin/main.ml", line 1, characters 30-43: +1 | let () = Printf.printf "%s\n" Hello.En.hello + ^^^^^^^^^^^^^^ +Error: Unbound value Hello.En.hello +``` + +これは`lib/en.mli`を変更していないためです。`hello`が記載されていないので、プライベートのままです。 + +## ライブラリ内での複数モジュールの定義 + +単一のライブラリ内に複数のモジュールを定義できます。これを示すために、以下の内容で`lib/es.ml`という新しいファイルを作成してください: + +```ocaml +let v = "¡Hola, mundo!" +``` + +`bin/main.ml`で新しいモジュールを使用します: + +```ocaml +let () = Printf.printf "%s\n" Hello.Es.v +let () = Printf.printf "%s\n" Hello.En.v +``` + +最後に、`dune build`と`dune exec hello`を実行して、`hello`ライブラリで作成したモジュールを使った新しい出力を確認してください。 + +```shell +$ opam exec -- dune exec hello +¡Hola, mundo! +Hello, world! +``` + +モジュールのより詳しい紹介は[モジュール](/docs/modules)(英語)にあります。 + +## パッケージからモジュールをインストールして使用する + +OCamlには活発なオープンソースコントリビュータのコミュニティがあります。ほとんどのプロジェクトはopamパッケージマネージャーを使用して利用でき、[OCamlのインストール](/docs/up-and-ready)チュートリアルでインストールしました。以下のセクションでは、opamのオープンソースリポジトリからパッケージをインストールして使用する方法を示します。 + +これを説明するために、`hello`プロジェクトを更新して、[S式](https://en.wikipedia.org/wiki/S-expression)を含む文字列を解析し、[Sexplib](https://github.com/janestreet/sexplib)を使用して文字列に変換して表示します。まず、`opam update`を実行してopamのパッケージリストを更新してください。次に、以下のコマンドで`Sexplib`パッケージをインストールします: + +```shell +opam install sexplib +``` + +次に、`bin/main.ml`で有効なS式を含む文字列を定義します。`Sexplib.Sexp.of_string`関数でS式に解析し、`Sexplib.Sexp.to_string`で文字列に変換して表示します。 + +```ocaml +(* Read in Sexp from string *) +let exp1 = Sexplib.Sexp.of_string "(This (is an) (s expression))" + +(* Do something with the Sexp ... *) + +(* Convert back to a string to print *) +let () = Printf.printf "%s\n" (Sexplib.Sexp.to_string exp1) +``` + +入力した有効なS式を表す文字列は、S式型に解析されます。S式型は`Atom`(文字列)またはS式の`List`(再帰的な型)として定義されています。詳細は[Sexplib ドキュメント](https://github.com/janestreet/sexplib)(英語)を参照してください。 + +この例をビルドして実行する前に、プロジェクトのコンパイルに`Sexplib`が必要であることをDuneに伝える必要があります。`bin/dune`ファイルの`library`スタンザに`Sexplib`を追加してください。完全な`bin/dune`ファイルは以下のようになります。 + +```lisp +(executable + (public_name hello) + (name main) + (libraries hello sexplib)) +``` + +**豆知識**: Dune設定ファイルはS式です。 + +最後に、前と同じように実行します: + +```shell +$ opam exec -- dune exec hello +(This(is an)(s expression)) +``` + +## プリプロセッサを使用したコード生成 + + +**注意**: この例はDkML 2.1.0を使用してWindowsでテストに成功しています。`dkml version`を実行してバージョンを確認してください。 + +`hello`の出力をUTopでの文字列リストのように表示したいとしましょう:`["hello"; "using"; "an"; "opam"; "library"]`。そのためには、`string list`を`string`に変換し、括弧、スペース、カンマを追加する関数が必要です。自分で定義する代わりに、パッケージを使って自動生成しましょう。[`ppx_deriving`](https://github.com/ocaml-ppx/ppx_deriving)を使用します。インストール方法は以下の通りです: + +```shell +opam install ppx_deriving +``` + +Duneにその使い方を伝える必要があります。これは`lib/dune`ファイルで行います。これは先ほど編集した`bin/dune`ファイルとは異なることに注意してください!`lib/dune`ファイルを開き、以下のように編集してください: + +```lisp +(library + (name hello) + (preprocess (pps ppx_deriving.show))) +``` + +`(preprocess (pps ppx_deriving.show))`の行は、コンパイル前にソースを`ppx_deriving`パッケージが提供する`show`プリプロセッサを使用して変換する必要があることを意味します。`(libraries ppx_deriving)`を書く必要はなく、Duneは`preprocess`スタンザからそれを推論します。 + +`lib/en.ml`と`lib/en.mli`ファイルも編集する必要があります: + +**`lib/en.mli`** + +```ocaml +val string_of_string_list : string list -> string +val v : string list +``` + +**`lib/en.ml`** + +```ocaml +let string_of_string_list = [%show: string list] + +let v = String.split_on_char ' ' "Hello using an opam library" +``` + +下から読んでいきましょう: + +- `v`は`string list`型です。`String.split_on_char`を使用して、スペース文字で分割することで`string`を`string list`に変換しています。 +- `string_of_string_list`は`string list -> string`型です。文字列のリストを、期待される書式を適用して文字列に変換します。 + +最後に、`bin/main.ml`も編集する必要があります + +```ocaml +let () = print_endline Hello.En.(string_of_string_list v) +``` + +結果は以下の通りです: + +```shell +$ opam exec -- dune exec hello +["Hello"; "using"; "an"; "opam"; "library"] +``` + +## ワンストップショップとしてのDuneを覗く + +このセクションでは、`dune init proj`によって作成されたがこれまで言及されていなかったファイルとディレクトリの目的を説明します。 + +OCamlの歴史の中で、いくつかのビルドシステムが使用されてきました。このチュートリアルを執筆している時点(2023年夏)では、Duneが主流であり、そのためこのチュートリアルで使用しています。Duneはファイルからモジュール間の依存関係を自動的に抽出し、互換性のある順序でコンパイルします。ビルドするものがある各ディレクトリに1つの`dune`ファイルのみが必要です。`dune init proj`で作成される3つのディレクトリには以下の目的があります: + +- `bin`: 実行可能プログラム +- `lib`: ライブラリ +- `test`: テスト + +Dune専用のチュートリアルが用意される予定です。そのチュートリアルでは、Duneの多くの機能が紹介されますが、そのうちのいくつかをここに挙げます: + +- テストの実行 +- ドキュメントの生成 +- パッケージングメタデータの生成(ここでは`hello.opam`) +- 汎用ルールを使用した任意のファイルの作成 + +`_build`ディレクトリはDuneが生成するすべてのファイルを保存する場所です。いつでも削除できますが、後続のビルドで再作成されます。 + +## 最小セットアップ + +この最後のセクションでは、Duneが動作するために本当に必要なものを強調して、必要最小限のプロジェクトを作成しましょう。まず、新しいプロジェクトディレクトリを作成します: + +```shell +cd .. +mkdir minimo +cd minimo +``` + +最低限、Duneには`dune-project`と1つの`dune`ファイルの2つのファイルのみが必要です。できるだけ少ないテキストで書く方法は以下の通りです: + +`dune-project` + +```lisp +(lang dune 3.6) +``` + +`dune` + +```lisp +(executable (name minimo)) +``` + +`minimo.ml` + +```ocaml +let () = print_endline "My name is Minimo" +``` + +これだけです!これでDuneが`minimo.ml`ファイルをビルドして実行するのに十分です。 + +```shell +$ opam exec -- dune exec ./minimo.exe +My name is Minimo +``` + +**注意**: `minimo.exe`はファイル名ではありません。これは、バイトコードコンパイラの代わりにOCamlのネイティブコンパイラを使用して`minimo.ml`ファイルをコンパイルするようにDuneに指示する方法です。豆知識として、空のファイルも有効なOCaml構文であることに注意してください。これを使って`minimo`をさらに縮小できます。もちろん何も表示されませんが、有効なプロジェクトになります! + +## まとめ + +このチュートリアルは「はじめの一歩」シリーズの最後です。ここから先は、自分の学習パスに従って他のチュートリアルを選んで進めるのに十分な知識があります。 + + diff --git a/data/tutorials/getting-started/2_00_editor_setup.ja.md b/data/tutorials/getting-started/2_00_editor_setup.ja.md new file mode 100644 index 0000000000..a1f84111cd --- /dev/null +++ b/data/tutorials/getting-started/2_00_editor_setup.ja.md @@ -0,0 +1,254 @@ +--- +id: set-up-editor +title: エディタの設定 +description: | + このページでは、OCaml用にエディタを設定する方法を説明します。 +category: "Tooling" +language: Japanese +--- +トップレベルは言語を対話的に試すのに最適ですが、すぐにエディタでOCamlファイルを書く必要が出てきます。エディタのOCamlサポートを強化するために必要なツール、MerlinとOCamlサポートを提供する`ocaml-lsp-server`(LSPサーバーを通じてエディタに「定義へジャンプ」や「型の表示」などの機能を提供するサーバー)は既にインストール済みです。 +OCamlには多くのエディタ用プラグインがありますが、最も活発にメンテナンスされているのはVisual Studio Code、Emacs、Vim用のものです。 + +## Visual Studio Code + +> TL;DR +> [opamスイッチ](/docs/opam-switch-introduction?lang=ja)に`ocaml-lsp-server`と`ocamlformat`パッケージをインストールしてください。 + +VSCodeの場合、Visual Studio Marketplaceから[OCaml Platform Visual Studio Code拡張機能](https://marketplace.visualstudio.com/items?itemName=ocamllabs.ocaml-platform)をインストールしてください。この拡張機能はOCaml LSPとOCamlFormatに依存しています。スイッチにインストールするには、以下を実行します: + +```shell +opam install ocaml-lsp-server ocamlformat +``` + +OCamlソースファイルを最初に読み込むと、使用するツールチェインの選択を求められる場合があります。リストから使用しているOCamlのバージョン(例:`5.1.0`)を選択してください。 + +### 利用できるエディタ機能 + +エディタが正しく設定されていれば、以下の重要な機能を活用し始めることができます: + +#### 1) ホバーによる型情報の表示 + +![VSCode ホバー](/media/tutorials/vscode-hover.gif) + +これは、OCamlの変数や関数の型情報を確認できる優れた機能です。コードの上にカーソルを置くだけで、ツールチップに型情報が表示されます。 + +#### 2) `Ctrl + クリック`による定義へのジャンプ + +![VSCode Ctrlクリック](/media/tutorials/vscode-ctrl-click.gif) + +Ctrlキーを押しながらホバーすると、コードがクリック可能なリンクとして表示され、クリックすると実装があるファイルに移動します。コードが内部でどのように動作するかを理解したい場合に便利です。この例では、`Queue`モジュールの`peek`メソッドにホバーして`Ctrl + クリック`すると、`peek`メソッド自体の定義とその実装に移動します。 + +#### 3) `Ctrl + Shift + P`によるOCamlコマンド + +![VSCode OCamlコマンド](/media/tutorials/vscode-ocaml-commands.gif) + +Ctrl + Shift + Pキーの組み合わせを押すと、上部にモーダルダイアログが開きます。`ocaml`と入力すると、さまざまな目的に使用できる各種OCamlコマンドのリストが表示されます。 + +### Windowsユーザー + +DkMLディストリビューションを使用した場合、以下の手順が必要です: + 1. `File` > `Preferences` > `Settings`ビューを開きます(または`Ctrl ,`を押します) + 2. `User` > `Extensions` > `OCaml Platform`を選択します + 3. `OCaml: Use OCaml Env`のチェックを外します。以上です! + +## Emacs + +EmacsでOCamlを使うには、少なくとも2つのモードが必要です: + +- メジャーモード:構文ハイライトやインデントレベルの構造化などをサポートします +- マイナーモード:言語サーバー(`ocaml-lsp-server`や`merlin`など)と連携します。このチュートリアルでは、新しい`ocaml-eglot`モードと`ocaml-lsp-server`をサーバーとして使用することに焦点を当てます。 + +### メジャーモードの選択 + +OCaml専用のメジャーモードにはいくつかありますが、主な3つは以下の通りです: + +- [Tuareg](https://github.com/ocaml/tuareg):古くからある(現在も更新されている)非常に完成度の高いモードで、通常推奨されるものです +- [Caml](https://github.com/ocaml/caml-mode):`tuareg`よりさらに古い(しかし現在も更新されている)、`tuareg`よりも軽量なモードです +- [Neocaml](https://github.com/bbatsov/neocaml):[tree-sitter](https://tree-sitter.github.io/tree-sitter/)のような最新のツールに基づいた新しいモードです。執筆時点ではまだ実験的です。 + +このチュートリアルでは、メジャーモードとして`tuareg`を使用しますが、自由に実験してお好みのものを選んでください!`tuareg`を使用するには、Emacs設定に以下の行を追加してください: + +```elisp +(use-package tuareg + :ensure t + :mode (("\\.ocamlinit\\'" . tuareg-mode))) +``` + + +#### Melpaと`use-package` + +お使いのEmacsのバージョンが`use-package`マクロをサポートしていない場合(またはMELPAパッケージを考慮するように設定されていない場合)は、更新して以下の指示に従い[`use-package`](https://github.com/jwiegley/use-package)と[MELPA](https://melpa.org/#/getting-started)をインストールしてください。 + +### OCaml用LSP設定 + +バージョン`29.1`以降、EmacsにはLSPサーバーと連携するための組み込みモード[Eglot](https://www.gnu.org/software/emacs/manual/html_mono/eglot.html)があります。それ以前のバージョンのEmacsを使用している場合は、以下の方法でインストールする必要があります: + +```elisp +(use-package eglot + :ensure t) +``` + +次に、メジャーモード(この場合は`tuareg`)と`eglot`の橋渡しが必要です。これは[`ocaml-eglot`](https://github.com/tarides/ocaml-eglot)パッケージを使って行います: + +```elisp +(use-package ocaml-eglot + :ensure t + :after tuareg + :hook + (tuareg-mode . ocaml-eglot) + (ocaml-eglot . eglot-ensure)) +``` + +これだけです!あとは`ocaml-lsp-server`と`ocamlformat`を[スイッチ](/docs/opam-switch-introduction?lang=ja)にインストールするだけです: + +```shell +opam install ocaml-lsp-server ocamlformat +``` + +これでEmacsでOCamlコードを_生産的に_編集する準備が整いました! + +#### より詳細な設定 + +ocaml-eglotは詳細に設定でき、プロジェクトの[README](https://github.com/tarides/ocaml-eglot/blob/main/README.md)(英語)にはワークフローに完全に適合するための複数の設定パスが示されています。モードが提供するさまざまな機能の網羅的な紹介もそこにあります。 + + +#### 型情報の取得 + +OCamlファイルを開くと`ocaml-lsp`サーバーが起動するはずです。例えば、`ocaml-eglot-type-enclosing`コマンド(または`C-c C-t`バインディング)を選択した式に使用することで、動作していることを確認できます: + +![Emacs 型情報](/media/tutorials/emacs-type-info.gif) + +ocaml-eglotの[README](https://github.com/tarides/ocaml-eglot/blob/main/README.md)(英語)には、このモードで利用可能なすべての機能の包括的な概要が掲載されています! + + +## Vim + +Vimの場合、LSPサーバーは使用せず、Merlinと直接通信します。 + +```shell +opam install merlin +``` + +上記でMerlinをインストールした後、エディタとMerlinをリンクする方法の手順が表示されます。表示されない場合は、以下のコマンドを実行してください: + +```shell +opam user-setup install +``` + +### Merlinとの通信 + +#### 型情報の取得 + +![Vim 型情報](/media/tutorials/vim-type-info.gif) + +- VimエディタでEscを押してコマンドモードに入ります。 +- 変数の上にカーソルを置きます。 +- `:MerlinTypeOf`と入力してEnterを押します。 +- コマンドバーに型情報が表示されます。 +その他のVim用Merlinコマンドについては、[Merlin公式ドキュメントのVim版](https://ocaml.github.io/merlin/editor/vim/)(英語)で使い方を確認できます。 + +## Neovim + +NeovimにはLSPクライアントが組み込まれています。 + +ここで注意すべき点は、`ocaml-lsp-server`はバージョンに敏感であり、人気のある言語サービスのパッケージマネージャーであるMasonの時として古いソースとうまく連携しないことが多いということです。LSPサーバーをスイッチに直接インストールし、Neovimの設定をそれを使用するように指定することをお勧めします。 + +LSPサーバーとフォーマッターをインストールするには、以下を実行してください。 +```shell +opam install ocaml-lsp-server ocamlformat +``` + +LSPサーバーのインストールと管理には2つの主な方法があります。 +- より新しい推奨される方法は、v0.11.0以降のバージョンで`vim.lsp`を介して新しいNeovim LSP APIを使用することです。 +- より従来の方法は`nvim-lspconfig`を使用することです。詳細については、`kickstart.nvim`に優れたセットアップ例があります。 + +### vim.lspの使用: + +トップレベルの`init.lua`に以下を追加してください。 +```lua +vim.lsp.config['ocamllsp'] = { + cmd = { 'ocamllsp' }, + filetypes = { + 'ocaml', + 'ocaml.interface', + 'ocaml.menhir', + 'ocaml.ocamllex', + 'dune', + 'reason' + }, + root_markers = { + { 'dune-project', 'dune-workspace' }, + { "*.opam", "esy.json", "package.json" }, + '.git' + }, + settings = {}, +} + +vim.lsp.enable 'ocamllsp' +``` + +設定オプションの詳細については`:h lsp-config`を参照してください。 + +#### runtimepathを使用したvim.lsp + +`init.lua`を最小限に保ちたい場合は、`runtimepath`を介してLSP設定を別のファイルに移動することもできます。設定テーブルを`lsp/.lua`または`after/lsp/.lua`に配置すると、Neovimが自動的に検索します。 + +詳細については`:h runtimepath`を参照してください。 + +設定のルートで以下を実行してください。 +```text +mkdir lsp +touch lsp/ocamllsp.lua +``` + +Neovimの設定は以下のような構造になるはずです。 +```text +. +├── init.lua +├── lsp +│ └── ocamllsp.lua +└── ... +``` + +LSP設定を`lsp/ocamllsp.lua`に追加してください。 +```lua +return { + cmd = { 'ocamllsp' }, + filetypes = { + 'ocaml', + 'ocaml.interface', + 'ocaml.menhir', + 'ocaml.ocamllex', + 'dune', + 'reason' + }, + root_markers = { + { 'dune-project', 'dune-workspace' }, + { "*.opam", "esy.json", "package.json" }, + '.git' + }, + settings = {}, +} +``` + +次にトップレベルの`init.lua`で有効にします。 +```lua +vim.lsp.enable 'ocamllsp' +``` + +### nvim-lspconfigの使用 + +`nvim-lspconfig`のセットアップに以下を追加してください。 +```lua +{ + 'neovim/nvim-lspconfig', + config = function() + -- rest of config... + + -- add this line specifically for OCaml + require('lspconfig').ocamllsp.setup {} + end, +}, +``` + +`nvim-lspconfig`が妥当なデフォルト値を提供するため、`setup`にさらに設定を渡す必要はありません。詳細は[こちら](https://github.com/neovim/nvim-lspconfig/blob/master/lsp/ocamllsp.lua)(英語)を参照してください。 diff --git a/data/tutorials/getting-started/2_01_toplevel.ja.md b/data/tutorials/getting-started/2_01_toplevel.ja.md new file mode 100644 index 0000000000..cab8ea407f --- /dev/null +++ b/data/tutorials/getting-started/2_01_toplevel.ja.md @@ -0,0 +1,82 @@ +--- +id: toplevel-introduction +title: OCamlトップレベル入門 +description: | + このページでは、OCamlトップレベルの簡単な紹介をします。 +category: "Tooling" +language: Japanese +--- + +OCamlトップレベルは、ユーザーとOCamlの対話です。ユーザーがOCamlコードを書くと、UTopがそれを評価します。このため、Read-Eval-Print-Loop(REPL)とも呼ばれます。`ocaml`や`utop`など、いくつかのOCamlトップレベルが存在します。[OCaml Platform](/docs/platform)ツールチェインの一部であるUTopの使用をお勧めします。 + +UTopを実行するには、`utop`コマンドを使用します。以下のように表示されます: + +```shell +$ utop +────────┬─────────────────────────────────────────────────────────────┬───────── + │ Welcome to utop version 2.12.1 (using OCaml version 5.0.0)! │ + └─────────────────────────────────────────────────────────────┘ + +Type #utop_help for help about using utop. + +─( 17:00:09 )─< command 0 >──────────────────────────────────────{ counter: 0 }─ +utop # +``` + +`Ctrl-D`(ファイルの終わり)を押すか、`#quit;;`と入力して`utop`を終了します。 + +UTopはハッシュプロンプト`#`を表示します。これはCLIの`$`に似ています。この`#`は入力を待っていることを意味するので、プロンプトの後にコードを書き始めることができます。評価するには、式の終わりを示すためにダブルセミコロン`;;`を追加して`Enter`を押してください。 + +ダブルセミコロンで終わる行は、プロンプトとダブルセミコロンの間に入力されたすべてのものの構文解析、型チェック、評価をトリガーします。ダブルセミコロンの解釈はOCamlインタプリタではなく、UTopによって行われます。ダブルセミコロンで終了したエントリの評価が完了すると、REPLは別の入力を待ちます。 + +`#`で始まるコードサンプルは、UTopにコピー&ペーストすることを意図しています。 + +例えば、以下のコードスニペットを考えてみましょう: + +```ocaml +# 2 + 2;; +- : int = 4 +``` + +上のコードスニペットでは、`2 + 2;;`がユーザーの入力で、`- : int = 4`がOCamlの出力です。 + +`Enter`を押す前にコードを修正する必要がある場合は、キーボードの左右の矢印キーを使用してテキスト内を移動できます。上下の矢印キーを使用すると、以前に評価された式をナビゲートできます。ダブルセミコロン`;;`なしで`Enter`を入力すると新しい行が作成されるため、この方法で複数行の式を書くことができます。 + +`#quit`や`#help`のようにハッシュ文字`#`で始まるコマンドは、OCamlでは評価されません。UTopによるコマンドとして解釈されます。 + +これでUTopでハッキングする準備ができました!トップレベルで問題が発生した場合は、遠慮なく[Discussで質問](https://discuss.ocaml.org/)してください。 + +> 注意:ダブルセミコロン`;;`はトップレベル以外のOCaml構文でも有効なトークンです。OCamlソースコード内では、[no-op](https://en.wikipedia.org/wiki/NOP_(code))(何もしない操作)であり、いかなる動作もトリガーしないため、コンパイラに無視されます。ファイルをコンパイルまたはスクリプトとして解釈する場合、OCamlで書くときにダブルセミコロンは省略でき、省略すべきです。残しておいてもエラーにはなりませんが、不要です。コンパイラはそれらを許容しますが、これはUTopからファイルへのコピー&ペーストを、削除する必要なく行えるようにするためです。 + +## UTopでのパッケージの使用 + +### UTopでのライブラリの読み込み + +現在のopamスイッチにインストールされたパッケージのライブラリをUTopで使用したい場合は、トップレベルに + +```ocaml +# #require "";; +``` + +と入力して、ライブラリのすべての定義を利用可能にします。例えば、以下を試してみてください + +```ocaml +# Str.quote {|"hello"|};; +Error: Unbound module Str + +# #require "str";; + +# Str.quote {|"hello"|};; +- : string = "\"hello\"" +``` + +**ヒント**: UTopは利用可能なライブラリを認識しており、コード補完が機能します。`utop`の外では、`ocamlfind list`を使用してライブラリの完全なリストを表示できます。opamパッケージは複数のライブラリをバンドルでき、ライブラリは複数のモジュールをバンドルできることに注意してください。 + +### UTopでのプリプロセッサ拡張(PPX)の使用 + +プリプロセッサ拡張は、コードのアノテーションを通じてコード生成を可能にします +(例については、[「はじめてのOCamlプログラム」の「プリプロセッサを使用したコード生成」セクション](/docs/your-first-program#using-the-preprocessor-to-generate-code)を参照してください)。 + +UTopでPPXを有効にするには、対応するライブラリを読み込むだけです。 + +[`ppx_deriving`](https://ocaml.org/p/ppx_deriving/latest)パッケージがopamスイッチにインストールされている場合は、`#require "ppx_deriving.show"`を実行します。 diff --git a/data/tutorials/getting-started/2_02_opam_switch.ja.md b/data/tutorials/getting-started/2_02_opam_switch.ja.md new file mode 100644 index 0000000000..f9f41f8bae --- /dev/null +++ b/data/tutorials/getting-started/2_02_opam_switch.ja.md @@ -0,0 +1,86 @@ +--- +id: opam-switch-introduction +title: opamスイッチ入門 +description: | + このページでは、opamスイッチの簡単な紹介、その用途、作成方法を説明します。 +category: "Tooling" +language: Japanese +--- + +OCamlのパッケージマネージャーであるopamは、隔離されたOCaml環境である_スイッチ_という概念を導入しています。スイッチはOCaml初心者の間で混乱を引き起こすことが多いため、このドキュメントではopamスイッチとその依存関係管理やプロジェクト固有の設定に対する使用法をよりよく理解することを目的としています。 + +opamは「スイッチ」と呼ばれる複数の同時インストールプレフィックスを管理するように設計されています。Pythonの`virtualenv`と同様に、opamスイッチは隔離された環境を作成するツールです。スイッチは互いに独立しており、独自のインストール済みパッケージ、リポジトリ、設定オプションのセットを持っています。スイッチには独自のOCamlコンパイラ、ライブラリ、バイナリもあります。これにより、複数のコンパイラバージョンを同時に利用可能にできます。 + +## スイッチの一覧表示 + +以下のコマンドは、システムに設定されているopamスイッチを表示します。[OCamlのインストール](/docs/installing-ocaml?lang=ja)で説明されているようにOCamlのインストールを完了すると、`default`というスイッチが1つ作成されています。その時点でスイッチを一覧表示すると、そのスイッチのみが表示されます。 + +```shell +$ opam switch list +# switch compiler description +-> default ocaml.4.13.1 default +``` + +## 新しいスイッチの作成 + +新しいopamスイッチを作成するには、`opam switch`コマンドの後に目的のスイッチ名とオプションのOCamlコンパイラバージョンを指定します。例えば、特定のOCamlコンパイラバージョンで「my_project」という名前のスイッチを作成するには: + +``` +opam switch create my_project +``` + +``を使用したいOCamlコンパイラのバージョン(例:`5.2.0`)に置き換えてください(利用可能なOCamlコンパイラバージョンのリストは`opam switch list-available`を参照)。 + +コンパイラを指定せず、`my_project`がディレクトリの場合、opamはデフォルトバージョンを選択します。`my_project`がディレクトリでない場合、opamはそれをプレーンな名前とみなし、同じ名前のコンパイラバージョンのインストールを試みます。 + +次に、新しいスイッチを**アクティブ化**します。これにより、現在選択されているスイッチとして設定され、OCaml関連の操作はすべてこのスイッチを使用します。以下を実行してアクティブ化できます: + +``` +opam switch my_project +``` + +`my_project`を新しいスイッチの名前に置き換えてください。 + +以下を実行してアクティブ化を**確認**します: + +``` +opam switch +``` + +出力が新しいスイッチの名前であれば、アクティブ化に成功しています!これで、OCamlプロジェクトに使用し、他のスイッチやシステム全体のOCaml環境に影響を与えることなく、このスイッチ固有のOCamlパッケージ、ライブラリ、依存関係をインストールできます。 + +## スイッチの種類 + +### グローバルスイッチ + +グローバルスイッチは、システム全体のOCamlインストールによく使用され、特定のプロジェクトやディレクトリに関連付けられていません。スイッチはシステムレベルで作成・設定され、通常、OCamlとそのエコシステムをグローバルスケールで管理するために使用されます。 + +opamスイッチを作成する場合、他に設定しない限り、デフォルトでグローバルです。`--global`フラグを付けてopam switchコマンドを使用することで、明示的にグローバルスイッチを選択することもできます。 + +opamの**システムスイッチ**は、オペレーティングシステム上のOCamlインストールに関連付けられたグローバルスイッチです。システムスイッチはシステム全体からアクセスできます。 + +### ローカルスイッチ + +一方、ローカルopamスイッチは特定のプロジェクトディレクトリに関連付けられています。プロジェクトのディレクトリまたはサブディレクトリ内に作成されるため、その特定のプロジェクトのコンテキスト内でのみOCamlとその依存関係を管理できます。 + +つまり、ローカルスイッチはプロジェクト固有のOCaml環境の隔離を提供し、システム全体や他のプロジェクト固有のOCamlインストールに干渉することなく、特定のプロジェクトに必要な特定のコンパイラバージョンとパッケージを定義・管理できます。 + +これは、プロジェクトがOCamlとそのパッケージの特定のバージョンを使用することを保証したい場合に特に便利です。 + +ローカルスイッチは現在の作業ディレクトリに基づいて自動的に選択されます。関連するローカルスイッチがあるディレクトリに移動すると、opamはそのディレクトリ内のOCaml関連の操作にそのスイッチを使用します。 + +## スイッチの選択 + +opamのほとんどのパッケージ関連コマンドは、選択されたスイッチのコンテキスト内で動作します。以下のいくつかの方法でスイッチを選択できます: + +**グローバル選択**: `opam switch `コマンドを使用します。上書きされない限り、opamはこのスイッチをすべての後続コマンドに使用します。 + +**ローカル選択**: スイッチを含むディレクトリで作業している場合、自動的に選択されます。ローカルスイッチはopamルートの外部にあります。 + +**環境変数**: 単一のシェルセッション内でスイッチを選択するには、`OPAMSWITCH=`環境変数を設定します。シェル環境を適切に設定するには`eval $(opam env --switch )`を使用してください。 + +**コマンドラインフラグ**: 単一のコマンドにスイッチを指定するには、`--switch `コマンドラインフラグを使用します。 + +--- + +> opamスイッチの詳細と使い方については、[opamマニュアルのFile Hierarchiesページ](https://opam.ocaml.org/doc/Manual.html)(英語)と[スイッチ専用ページ](https://opam.ocaml.org/doc/man/opam-switch.html)(英語)を参照してください。 diff --git a/data/tutorials/getting-started/3_01_ocaml_on_windows.ja.md b/data/tutorials/getting-started/3_01_ocaml_on_windows.ja.md new file mode 100644 index 0000000000..2bc0aa0b48 --- /dev/null +++ b/data/tutorials/getting-started/3_01_ocaml_on_windows.ja.md @@ -0,0 +1,145 @@ +--- +id: ocaml-on-windows +title: Windows上のOCaml +description: > + Windows上のOCamlの現状と、Windowsサポートを改善するためのロードマップについてお読みください。 +category: "Resources" +language: Japanese +--- + +新規ユーザーには[opam](https://opam.ocaml.org/)のインストールをお勧めします。OCamlパッケージマネージャーであるopamはバージョン2.2以降でWindowsを完全にサポートしており、最新のOCaml環境を提供します。 + +[Docker](#dockerイメージ)や[WSL2](#wsl2)を使用することもできます。 + +推奨事項は以下の利用可能状況表に基づいています: + +* Tier 1は最新のコンパイラで完全にサポートされています。 +* Tier 2はサポートされていますが、可能な場合にメンテナンスされます。 +* Tier 3はユーザーサポートです。 + +```text +╭──────────────────────────────────────────────────────────────────────────╮ +│ Tier │ OCaml Version and Environment │ Support │ +│ ------ │ ------------------------------- │ ----------------------------- │ +│ Tier 1 │ OCaml 5.x with Opam 2.2+ │ Full support. │ +│ Tier 3 │ 5.x with WSL2 │ User supported. │ +│ Tier 3 │ 5.x with Docker │ User supported. │ +╰──────────────────────────────────────────────────────────────────────────╯ +``` + +## WindowsへのOpamのインストール + +OpamはWindows向けにwingetで配布されています。インストールするには、ターミナルで以下のコマンドを実行してください: + +```shell-session +> winget install Git.Git OCaml.opam +``` + +`winget`からGitをインストールすることをお勧めしますが、必要に応じてこの手順を省略してお好みの方法でGitをインストールしても構いません。 +Opamは互換性のあるGitを探し、見つからない場合はインストールするためのオプションを提示します。 + +インストール後、opamバイナリにアクセスするために新しいシェルを起動してください。 + +```shell-session +$ opam --version +2.2.1 +``` + +opamがインストールされたら、`opam init`コマンドを実行してopam環境をセットアップします。 + +リポジトリ情報の取得段階に時間がかかることに気づくでしょう。 +これは(現時点では)正常なことなので、実行中はお気に入りの温かい飲み物を +ご用意することをお勧めします。 + +opamはUnixライクな環境を必要とします。デフォルトで、 +opamはCygwinに依存し、MSYS2とも互換性があります。 + +*初期化時*に、opamはマシン上で利用可能なUnix環境をスキャンし、 +お好みのオプションを選択するよう促します。opamが管理する +独自の内部Cygwinインストールを作成させることをお勧めします。これにより、 +そのような環境と連携する他のツールからの干渉の可能性を減らすことができます。 +サンドボックス環境と考えてください。 + +Opamの初期化時のデフォルトの動作は、新しい`switch`と +バージョン`> 4.05`のOCamlコンパイラをインストールすることです。デフォルトで、opamはスイッチ作成時にCコンパイラとして`mingw`を選択しますが、以下のコマンドで`msvc`のような代替をインストールすることも可能です: + +```sh +opam install system-msvc +``` + +`opam init`が完了したら、以下のコマンドを実行して環境を更新してください: + +CMDの場合: + +```dosbatch +> for /f "tokens=*" %i in ('opam env --switch=default') do @%i +``` + +PowerShellの場合: + +```powershell +> (& opam env --switch=default) -split '\\r?\\n' | ForEach-Object { Invoke-Expression $_ } +``` + +[Nushell](https://www.nushell.sh/)の場合: + +```nushell +> opam env --shell=powershell | parse "$env:{key} = '{val}'" | transpose -rd | load-env +``` + +Opamは必要な場合にシェル更新コマンドを表示します。 + +以下でインストールを確認できます + +```text +> ocaml --version +The OCaml toplevel, version 5.2.0 + +> ocaml +OCaml version 5.2.0 +Enter #help;; for help. + +# print_endline "Hello OCamleers!!";; +Hello OCamleers!! +- : unit = () +# +``` + +これで開発の準備ができたOCaml環境が整いました。問題が発生した場合やさらなる支援が必要な場合は、遠慮なく[OCamlコミュニティ](https://ocaml.org/community)にご相談ください。 + +## その他のインストール環境 + +### WSL2 + +WindowsマシンでOCamlプログラムを*実行*するだけでよい場合、最も簡単な方法はWindows Subsystem for Linux 2(WSL2)を使用することです。WSL2はLinuxプログラムをWindows上で直接実行できる機能です。WSL2はWSL1よりも大幅に使いやすく高速です。Microsoftは[WSL2のセットアップ](https://docs.microsoft.com/en-us/windows/wsl/install-win10)(英語)に関する包括的なインストール手順を提供しています。 + +WSL2をインストールして1つのLinuxディストリビューション([Ubuntu LTS](https://apps.microsoft.com/store/detail/ubuntu/9PDXGNCFSCZV?hl=en-us&gl=US)をお勧めします)を選択したら、[OCamlのインストール:LinuxとmacOSでのインストール](/docs/installing-ocaml?lang=ja)の手順に従うことができます。 + +### Dockerイメージ + +[`ocaml/opam`](https://hub.docker.com/r/ocaml/opam) Docker Hubリポジトリには、定期的に更新されるWindowsイメージが含まれています。これには`msvc`と`mingw`を使用したイメージが含まれます。Dockerに慣れている場合は、マシン上で動作するWindows環境を手に入れるより簡単な方法かもしれません。 + +## Windows上のOCamlのエディタのサポート + +### Visual Studio Code(VSCode) + +**opamインストールを使用する場合**、VSCodeを実行するパスにopamスイッチプレフィックスを追加する必要があります。 + +**WSL2を使用する場合**は、VSCodeからWSL2インスタンスにリモート接続します。Microsoftには、WSL2とVisual Studio Codeの接続について説明した[有用なブログ記事](https://code.visualstudio.com/blogs/2019/09/03/wsl2)(英語)があります。 + +### VimとEmacs + +**VimとEmacs**の場合、opamを使用して[Merlin](https://github.com/ocaml/merlin)システムをインストールしてください: + +```console +opam install merlin +``` + +インストール手順では、Merlinとエディタのリンク方法が表示されます。 + +**Vimを使用する場合**、デフォルトのCygwin Vimは +Merlinでは動作しません。Vimを別途インストールする必要があります。Merlinインストール時に表示される通常の手順に加えて、VimでPATHを設定する必要がある場合があります: + +```vim +let $PATH .= ";".substitute(system('opam config var bin'),'\n$','','''') +``` diff --git a/data/tutorials/getting-started/3_02_arm_fix.ja.md b/data/tutorials/getting-started/3_02_arm_fix.ja.md new file mode 100644 index 0000000000..334932b9b6 --- /dev/null +++ b/data/tutorials/getting-started/3_02_arm_fix.ja.md @@ -0,0 +1,68 @@ +--- +id: arm64-fix +title: Apple M1でのHomebrewエラーの修正 +description: | + このページでは、新しいMacのARM64プロセッサ向けの回避策を説明します。 +category: "Resources" +language: Japanese +--- + +[Homebrewのインストール方法が変更された](https://github.com/Homebrew/brew/issues/9177)ため、macOS ARM64 M1で実行可能ファイルが見つからないことがあります。これにより、チュートリアルを進める際にエラーが発生する可能性があります。HomebrewがデフォルトでARM64をインストールするようにしたいので、いくつかの変更が必要です。 + +始める前に、Homebrewがどこにインストールされているか確認しましょう。CLIで以下を実行してください: + +```shell +where brew +``` + +応答が`/usr/local/bin/brew`の場合、変更が必要です。`/opt/homebrew/bin/brew`である必要があります。 + +### CLTのインストール + +まず、以下を実行してCommand Line Tools(CLT)がインストールされていることを確認してください + +```shell +$ ls /Library/Developer/CommandLineTools +Library SDKs usr +``` + +インストールされていない場合は、今すぐインストールしましょう。XCode全体をインストールする必要はありません。[Appleの開発者ページから直接ダウンロード](https://developer.apple.com/download/all/)してCLTのみをインストールできます。安定性のために「Command Line Tools for XCode 14.3.1」のようなベータ版でないものを探してください。 + +### Rosettaの無効化 + +次に、Rosettaがインストールされている場合は無効にする必要があります。この[Appleサポート記事](https://support.apple.com/en-us/HT211861)(英語)で確認方法が説明されています。インストールされている場合は、以下の手順に従ってください。 + +1. 以下を実行してHomebrewをアンインストールします: + +```shell +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/uninstall.sh)" +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/uninstall.sh)" +``` + +2. Homebrewを再インストールします: + +```Shell +brew install /Users/tarides/Library/Caches/Homebrew/downloads/9e6d2a225119ad88cde6474d39696e66e4f87dc4a4d101243b91986843df691e--libev--4.33.arm64_monterey.bottle.tar.gz +``` + +3. Homebrewが正しい場所にあるか確認します。以下のように表示されるはずです: + +```shell +$ which brew +/opt/homebrew/bin/brew +``` + +4. ターミナルを閉じる + +正しく動作するために、現在のターミナルウィンドウを閉じて新しいものを開くことが重要です。その後、以下のコマンドを実行してください。以下のように表示されたら、brewの準備は完了です! + +```shell +$ brew doctor +Your system is ready to brew. +``` + +### インストールチュートリアルに戻る + +問題が解決したので、[OCamlのインストールチュートリアル](/docs/installing-ocaml?lang=ja)に戻ってopamのインストールと初期化を行うことができます。 + +これでOCamlの学習を続ける準備が整いました! diff --git a/data/tutorials/getting-started/3_03_ocaml_playground.ja.md b/data/tutorials/getting-started/3_03_ocaml_playground.ja.md new file mode 100644 index 0000000000..3075816809 --- /dev/null +++ b/data/tutorials/getting-started/3_03_ocaml_playground.ja.md @@ -0,0 +1,114 @@ +--- +id: ocaml-playground +title: OCaml Playgroundの使い方 +short_title: OCaml Playground +description: | + このページでは、OCaml Playgroundの使い方を説明します +category: "Resources" +language: Japanese +--- + +OCamlのブラウザ内プレイグラウンドへようこそ! + +[OCaml Playground](https://ocaml.org/play)は、特に初心者がインストールを心配することなくOCamlを始められるように作られています。開いたらすぐに使用できます。 + +シンプルなインターフェースで、左側に_エディタパネル_、右側に_出力パネル_の2つのパネルがあります。 + +エディタパネルはコードを書く場所で、出力パネルは結果が表示される場所です。とても簡単です。 + +ある意味、プレイグラウンドは[トップレベル](https://ocaml.org/docs/toplevel-introduction)よりもずっとシンプルです。プロンプト上で1行ずつコードを入力する必要はなく、代わりにエディタパネルにコードを直接入力またはコピーします。また、式を`;;`で終わらせる必要もありません。エディタパネルのコードを実行するには、エディタパネルの下部にある「Run」ボタンをクリックしてください。 + +OCamlコードを書くファイルのようなものだと考えてください。出力パネルの「Clear output」ボタンをクリックすることで、いつでも出力をクリアできます。心配しないでください、エディタパネルのコードには影響しません。 + +エディタパネルの下部にある「Share」ボタン(「Run」ボタンの左側)をクリックすることで、書いたコードを他の人と共有することもできます。「Share」をクリックした後、URLをコピーして他の人と共有してください。 + +困ったときは、上部の検索バーを使って標準ライブラリを調べることもできます。 + +## コードを入力してみましょう + +プレイグラウンドに初めて入ると、以下のような画面が表示されます。 + +![OCaml Playground](/media/tutorials/get-started/playground.png) + +慌てないでください!エディタパネルには説明とサンプルコードが、出力パネルにはOCamlのバージョンとコンパイル情報が表示されているだけです。 + +Ctrl+AとBackspaceを押すだけでエディタパネルをクリアし、OCamlコードを書き始めることができます。同様に、出力パネルの「Clear output」ボタンを押すとパネルをクリアできます。 + +簡単なものから始めましょう。エディタパネルに以下を入力して「Run」をクリックしてください。 + +``` +2+3 +``` + +以下のような出力が表示されるはずです。 + +`- : int = 5` + +次に、出力をクリアし、エディタパネルの内容も削除してください。文字列を試してみましょう。エディタパネルに以下を入力して「Run」をクリックしてください。 + +``` +"OCaml is amazing" +``` + +以下のような出力が表示されるはずです。 + +`- : string = "OCaml is amazing"` + +素晴らしいですね!順調です。では、短いプログラムを書いてみましょう。プレイグラウンドに入ったときに見たコードサンプルを使います。 + +``` +let num_domains = 2 +let n = 20 + +let rec fib n = + if n < 2 then 1 + else fib (n-1) + fib (n-2) + +let rec fib_par n d = + if d <= 1 then fib n + else + let a = fib_par (n-1) (d-1) in + let b = Domain.spawn (fun _ -> fib_par (n-2) (d-1)) in + a + Domain.join b + +let () = + let res = fib_par n num_domains in + Printf.printf "fib(%d) = %d\n" n res +``` + +出力は以下のようになります。 + +``` +fib(20) = 10946 + +val num_domains : int = 2 +val n : int = 20 +val fib : int -> int = +val fib_par : int -> int -> int = +``` + +## オートコンプリート + +プレイグラウンドはコード補完もサポートしています。コンテキストに基づいてユーザーの入力を提案・補完することで支援します。 + +![OCaml Playgroundのオートコンプリート](/media/tutorials/get-started/playground-autocomplete.png) + +## 注意点 + +上のコードサンプルからわかるように、定義の末尾に`;;`を使用する必要はありません。 + +ここで少し注意すべき点は、プレイグラウンドはOCamlトップレベルとは異なる動作をするということです。 +「Run」ボタンをクリックするたびに、すべての式と定義が順番に評価されます。 +`2+3`と書いて次の行に文字列`"this is a string"`と書くと([こちらを参照](/play#code=MiszCiJ0aGlzIGlzIGEgc3RyaW5nIg%3D%3D))、以下のようなエラーが表示されます: + +``` +Line 1, characters 2-3: +Error: This expression has type int + This is not a function; it cannot be applied. +``` + +一方、これらの式を`;;`で区切った場合([このように](/play#code=MiszOzsKInRoaXMgaXMgYSBzdHJpbmci))、またはそれらを名前にバインドした場合([このように](/play#code=bGV0IHggPSAyKzMKbGV0IHkgPSAidGhpcyBpcyBhIHN0cmluZyI%3D))は、1つずつ正常に評価されます。 + +## まとめ + +おめでとうございます!最後まで読んでいただきました。これで、[OCaml Playground](/play)の使い方についてより良い理解が得られたと思います。OCamlコードの練習に活用して楽しんでください。Happy Hacking!