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
  • X
  • BlueSky
  • GitHub
  • Forum
Language Manual
Overview
  • Introduction
  • Installation
  • Editor Plugins
  • Migrate to ReScript Syntax
  • Try
Language Features
  • Overview
  • Let Binding
  • 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
  • Async & Promise
  • Module
  • Import & Export
  • Attribute (Decorator)
  • Unboxed
    • Usage
  • Reserved Keyword
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
Build System
  • Overview
  • Configuration
  • Configuration Schema
  • External Stdlib
  • Pinned Dependencies
  • Interop with JS Build Systems
  • Performance
Guides
  • Converting from JS
Extra
  • Newcomer Examples
  • Project Structure
  • FAQ
Docs / Language Manual / Unboxed
Edit

Unboxed

Consider a ReScript variant with a single payload, and a record with a single field:

ReScriptJS Output
type name = Name(string)
let studentName = Name("Joe")

type greeting = {message: string}
let hi = {message: "hello!"}

If you check the JavaScript output, you'll see the studentName and hi JS object, as expected (see the variant JS output and record JS output sections for details).

For performance and certain JavaScript interop situations, ReScript offers a way to unwrap (aka unbox) the JS object wrappers from the output for records with a single field and variants with a single constructor and single payload. Annotate their type declaration with the attribute @unboxed:

ReScriptJS Output
@unboxed
type name = Name(string)
let studentName = Name("Joe")

@unboxed
type greeting = {message: string}
let hi = {message: "hello!"}

Check the new output! Clean.

Usage

Why would you ever want a variant or a record with a single payload? Why not just... pass the payload? Here's one use-case for variant.

Suppose you have a game with a local/global coordinate system:

ReScriptJS Output
type coordinates = {x: float, y: float}

let renderDot = (coordinates) => {
  Js.log3("Pretend to draw at:", coordinates.x, coordinates.y)
}

let toWorldCoordinates = (localCoordinates) => {
  {
    x: localCoordinates.x +. 10.,
    y: localCoordinates.x +. 20.,
  }
}

let playerLocalCoordinates = {x: 20.5, y: 30.5}

renderDot(playerLocalCoordinates)

Oops, that's wrong! renderDot should have taken global coordinates, not local ones... Let's prevent passing the wrong kind of coordinates:

ReScriptJS Output
type coordinates = {x: float, y: float}
@unboxed type localCoordinates = Local(coordinates)
@unboxed type worldCoordinates = World(coordinates)

let renderDot = (World(coordinates)) => {
  Js.log3("Pretend to draw at:", coordinates.x, coordinates.y)
}

let toWorldCoordinates = (Local(coordinates)) => {
  World({
    x: coordinates.x +. 10.,
    y: coordinates.x +. 20.,
  })
}

let playerLocalCoordinates = Local({x: 20.5, y: 30.5})

// This now errors!
// renderDot(playerLocalCoordinates)
// We're forced to do this instead:
renderDot(playerLocalCoordinates->toWorldCoordinates)

Now renderDot only takes worldCoordinates. Through a nice combination of using distinct variant types + argument destructuring, we've achieved better safety without compromising on performance: the unboxed attribute compiled to clean, variant-wrapper-less JS code! Check the output.

As for a record with a single field, the use-cases are a bit more edgy. We won't mention them here.

Attribute (Decorator)Reserved Keyword

© 2024 The ReScript Project

Software and assets distribution powered by KeyCDN.

About
  • Community
  • ReScript Association
Find us on