This is the latest docs version
Quick Links
  • -Overview
  • -Language Features
  • -JS Interop
  • -Build System
Documentation
Language Manual
Reference for all language features
ReScript & React
First class bindings for ReactJS
GenType
Seamless TypeScript integration
Reanalyze
Dead Code & Termination analysis
Exploration
Packages
Explore third party libraries and bindings
Syntax Lookup
Discover all syntax constructs
APIPlaygroundBlogCommunity
  • Playground
  • Blog
  • Twitter
  • GitHub
  • Forum
Language Manual
Overview
  • Introduction
  • Installation
  • Migrate to v11
  • Editor Plugins
  • Try
Language Features
  • Overview
  • Let Binding
    • Block Scope
    • Bindings Are Immutable
    • Binding Shadowing
    • Private let bindings
  • Type
  • Primitive Types
  • Tuple
  • Record
  • Object
  • Variant
  • Polymorphic Variant
  • Null, Undefined and Option
  • Array & List
  • Function
  • If-Else & Loops
  • Pipe
  • Pattern Matching / Destructuring
  • Mutation
  • JSX
  • Exception
  • Lazy Value
  • Promises
  • Async / Await
  • Tagged templates
  • Module
  • Import & Export
  • Attribute (Decorator)
  • Reserved Keywords
  • Equality and Comparison
Advanced Features
  • Extensible Variant
  • Scoped Polymorphic Types
JavaScript Interop
  • Interop Cheatsheet
  • Embed Raw JavaScript
  • Shared Data Types
  • External (Bind to Any JS Library)
  • Bind to JS Object
  • Bind to JS Function
  • Import from / Export to JS
  • Bind to Global JS Values
  • JSON
  • Inlining Constants
  • Use Illegal Identifier Names
  • Generate Converters & Helpers
  • Browser Support & Polyfills
  • Libraries & Publishing
  • TypeScript
Build System
  • Overview
  • Configuration
  • Configuration Schema
  • External Stdlib
  • Pinned Dependencies
  • Interop with JS Build Systems
  • Performance
  • Warning Numbers
Guides
  • Converting from JS
Extra
  • Newcomer Examples
  • Project Structure
  • FAQ
Docs / Language Manual / Let Binding
Edit

Let Binding

A "let binding", in other languages, might be called a "variable declaration". let binds values to names. They can be seen and referenced by code that comes after them.

ReScriptJS Output
let greeting = "hello!"
let score = 10
let newScore = 10 + score

Block Scope

Bindings can be scoped through {}.

ReScriptJS Output
let message = {
  let part1 = "hello"
  let part2 = "world"
  part1 ++ " " ++ part2
}
// `part1` and `part2` not accessible here!

The value of the last line of a scope is implicitly returned.

Design Decisions

ReScript's if, while and functions all use the same block scoping mechanism. The code below works not because of some special "if scope"; but simply because it's the same scope syntax and feature you just saw:

ReScriptJS Output
if displayGreeting {
  let message = "Enjoying the docs so far?"
  Console.log(message)
}
// `message` not accessible here!

Bindings Are Immutable

Let bindings are "immutable", aka "cannot change". This helps our type system deduce and optimize much more than other languages (and in turn, help you more).

Binding Shadowing

The above restriction might sound unpractical at first. How would you change a value then? Usually, 2 ways:

The first is to realize that many times, what you want isn't to mutate a variable's value. For example, this JavaScript pattern:

JS
var result = 0; result = calculate(result); result = calculateSomeMore(result);

...is really just to comment on intermediate steps. You didn't need to mutate result at all! You could have just written this JS:

JS
var result1 = 0; var result2 = calculate(result1); var result3 = calculateSomeMore(result2);

In ReScript, this obviously works too:

ReScriptJS Output
let result1 = 0
let result2 = calculate(result1)
let result3 = calculateSomeMore(result2)

Additionally, reusing the same let binding name overshadows the previous bindings with the same name. So you can write this too:

ReScriptJS Output
let result = 0
let result = calculate(result)
let result = calculateSomeMore(result)

(Though for the sake of clarity, we don't recommend this).

As a matter of fact, even this is valid code:

ReScriptJS Output
let result = "hello"
Console.log(result) // prints "hello"
let result = 1
Console.log(result) // prints 1

The binding you refer to is whatever's the closest upward. No mutation here! If you need real mutation, e.g. passing a value around, have it modified by many pieces of code, we provide a slightly heavier mutation feature.

Private let bindings

Private let bindings are introduced in the release 7.2.

In the module system, everything is public by default, the only way to hide some values is by providing a separate signature to list public fields and their types:

RES
module A: { let b: int } = { let a = 3 let b = 4 }

%%private gives you an option to mark private fields directly

RES
module A = { %%private(let a = 3) let b = 4 }

%%private also applies to file level modules, so in some cases, users do not need to provide a separate interface file just to hide some particular values.

Note interface files are still recommended as a general best practice since they give you better separate compilation units and also they're better for documentation.

Still, %%private is useful in the following scenarios:

  • Code generators. Some code generators want to hide some values but it is sometimes very hard or time consuming for code generators to synthesize the types for public fields.

  • Quick prototyping. During prototyping, we still want to hide some values, but the interface file is not stable yet, %%private provide you such convenience.

OverviewType

© 2024 The ReScript Project

Software and assets distribution powered by KeyCDN.

About
  • Community
  • ReScript Association
Find us on